This script will generate the seemingly unique disc ID strings used by CDDB in order to look up the title and listing of tracks for a given audio CD. There are lots of tools out there that will do this, but I wanted one that would work under IRIX, of which there seem to be very few. In fact I don't know of any. Everything I have seen is either part of a larger package (such as a CD player) or was intended for another operating system. Also, too, since IRIX will already produce an info file for an audio CD when it is inserted, it seemed like there should be a way to convert these text files into the ID strings used by CDDB. And sure enough, after reading over the cdframe(4) man page included with IRIX, I was able to figure out what I needed in order to create this program. Also, I borrowed heavily from the discid source written by Jeremy D. Zawodny, who is also the author of the PERL Net::CDDB module, which included a simple script that will take these ID strings and retrieve the title and song listing from CDDB. This script does not need Net::CDDB to run, by the way.
Anyhow, a little bit of technical background on how this works, since I like to babble on about crap of that sort. Under IRIX (at least versions 6.3 and above, but I don't know about anything below that because I never had the chance to play with audio CDs on machines running that), when you insert an audio CD into the CD-ROM drive, it will create a text file that includes various data about that CD. Say, for example, your CD-ROM drive gets mounted under /CDROM when there is a disc inserted. If the disc happens to be an audio CD, a file called /CDROM/info.disc will be created, and will look something like this:
album.key: 0BiM3i1@6A1R
album.duration: 48:30:64
album.tracks: 11
track1.start: 00:02:00
track1.duration: 03:48:46
track2.start: 03:50:46
track2.duration: 01:36:72
track3.start: 05:27:43
track3.duration: 06:10:17
track4.start: 11:37:60
track4.duration: 01:27:47
track5.start: 13:05:32
track5.duration: 05:10:23
track6.start: 18:15:55
track6.duration: 00:53:52
track7.start: 19:09:32
track7.duration: 07:44:47
track8.start: 26:54:04
track8.duration: 03:17:64
track9.start: 30:11:68
track9.duration: 10:33:22
track10.start: 40:45:15
track10.duration: 01:26:50
track11.start: 42:11:65
track11.duration: 06:18:73
In case you're wondering what CD this is from (and I know you aren't), it's from one of my particular favorites, the latest (and probably last) album from Dome (i.e. Bruce Gilbert and Graham Lewis of Wire) called "Yclept."
Times are given in the format of minutes:seconds:frames, and a single second of CD audio equals 75 frames. So, with that in mind, it's very easy to whip up a script that will convert the times listed into just frames in order to obtain the track offsets needed to generate a disc ID string. As far as the other ID elements go, I had to figure those out by reading over the aforementioned discid source. There rest of it is just simple text file parsing, something at which PERL excels. So here it is, the script that does all this. Note that it will default to looking for a file called /CDROM/info.disc but if you want you can specify an alternate file on the command line. This is nice in case you are doing a lot of mp3 encoding and decide to put the individual songs in one directory along with the info.disc file that describes the CD they came from. You can generate the ID string from that file and retrieve a song listing. Of course, you could also just put the ID string for the disc in a file with those mp3s and not need this script at all. Though it'd help to generate those strings in the first place, right?
"my_discid.perl" (click here to download)
#!/usr/bin/perl
my $sum1 = 0; # That I declare
my variables at the top of the script
my $sum2 = 0; # demonstrates
that I spend more time writing C code.
my $tracknum = 1; # these days.
my %discinfo;
if ($ARGV[0])
{
# If an
alternate file is specified on the command line, use it.
open(DISCINFO,
$ARGV[0]) or die "error opening $ARGV[0]: $!";
}
else
{
# Otherwise,
use the default file location.
open(DISCINFO,
"/CDROM/info.disc") or die "error opening /CDROM/info.disc: $!";
}
while (<DISCINFO>) # Damn I love parsing text files
in Perl.
{
# Amazing
how three lines of Perl can turn these files into an easily
# accessed
set of variables, isn't it?
($key,
$value) = split(/: /, $_);
chomp($value);
$discinfo{$key}
= $value;
}
close(DISCINFO);
# This section generates the checksum (though I'm
not sure it can really be called
# a checksum) that is the first element of the ID
string. I don't know why they
# do it this way, I suppose they figure this is the
best way. But, whatever works.
while ($tracknum <= $discinfo{"album.tracks"})
{
$sum1
+= &calculate_sum(&to_seconds($discinfo{"track$tracknum.start"}));
++$tracknum;
}
$sum2 = &to_seconds($discinfo{"album.duration"})
- &to_seconds($discinfo{"track1.start"});
printf("%08x ", $sum1 % 255 << 24 | $sum2 <<
8 | $discinfo{"album.tracks"});
# Next comes the number of tracks on the CD.
print $discinfo{"album.tracks"}, " ";
# Reset this variable back to 1 so I can re-use it.
Hey, I believe in recycling.
$tracknum = 1;
while ($tracknum <= $discinfo{"album.tracks"})
{
# This
converts each track offset to frames and then prints it out.
print
&to_frames($discinfo{"track$tracknum.start"}), " ";
++$tracknum;
}
# Last element of the ID string, the album duration
in seconds. For some reason
# the info.disc files always list the albums as 2
seconds longer than the discid.c
# source did, so I just subtract 2 from this to make
them conform to what CDDB wants.
print (&to_seconds($discinfo{"album.duration"})
- 2);
print "\n";
exit(0);
# Subroutine to generate the checksums for the first
ID element.
sub calculate_sum
{
my $seconds
= shift(@_);
my $retval
= 0;
while ($seconds
> 0)
{
$retval += $seconds % 10;
$seconds /= 10;
}
return($retval);
}
# Subroutine to convert the minutes:seconds:frames
strings to just frames.
sub to_frames
{
my $dirty
= shift @_;
my $minutes
= 0;
my $seconds
= 0;
my $frames
= 0;
($minutes,
$seconds, $frames) = split(/:/, $dirty);
$frames
+= (($minutes * 60) + $seconds) * 75;
return($frames);
}
# Subroutine to convert the minutes:seconds:frames
strings to just seconds.
# Note that the extra frames are truncated by this;
it's supposed to be
# like that in order to return values like what CDDB
wants.
sub to_seconds
{
my $dirty
= shift @_;
my $minutes
= 0;
my $seconds
= 0;
($minutes,
$seconds) = split(/:/, $dirty);
$seconds
+= $minutes * 60;
return($seconds);
}