The www.freedb.org internet data base provides information on artists, album- and songtitles on audio CDs.
This package reads in the TOC of an audio CD and calculates the discID required for queries at freedb.org.
A problem is the the access to the TOC information of an audio CDs. The easiest way at least on linux is to use the program cdparanoia, as
cdparanoia -Q
prints the TOC information.
Only on addition of 2s lead-in per track is required and a possible addition of 2 sec lead out in the total-length. The handling of the lead out is varying between different implementations of the protocol, but it is required from the freedb.org Test-CD.
The algorithm is a direct translation of the C Code provided in the FAQ, the freedb test CD discid is calculated correctly with an extra lead out of 2s.
The offset are provided by cdparanoia as a string mm:ss.ff (minutes:seconds.frames, an audio CD has 75 frames/s) which will be stored as 3 element list {mm ss tt}.
The variable cdtoc gives the offsets indexed by track number (starting from 0), additionally
cdtoc(num_traks) gives the number of tracks on the CD and cdtoc(total_trks) the total length of the CD.
package provide discid 0.1 # # (C) Joachim Heidemeier 2011 # published under the same licence terms as Tcl # namespace eval cddb { namespace export cddbID readTOC tocList2TrackOffset variable cdtoc # # transforms the string mm:ss.ff in 3 element list # 2 sec lead-in time are automatically added # add is optional value for lead out # proc getMCF {val {add 0}} { set l [split $val {: .}] set s [lindex $l 1] set m [lindex $l 0] set t [lindex $l 2] scan $m %d m scan $s %d s scan $t %d t incr s $add incr s 2 if {$s > 60} { lset l 0 [incr m] lset l 1 [expr {$s - 60}] } else { lset l 0 $m lset l 1 $s } lset l 2 $t return $l } # # transform TOC-List in total number of tracks # 75 frames / s # proc tocList2TrackOffset {tocList} { set to [expr {([lindex $tocList 0] * 60 * 75) + ([lindex $tocList 1] * 75) + [lindex $tocList 2]}] return $to } # # # get TOC from CD # uses " cdparanoia -Q" # drive is the CD rom device # platform dependend, alternatives could be # direct wrapping of libcdio # proc read_TOC {{drive /dev/sr0}} { variable cdtoc # # cdparanoia -Q writes to stderror # catch {exec cdparanoia -Q -d $drive} result # # skip the header from cdparanoia, header ends with a line of equal signs (==...) # set mode skip foreach line [split $result \n] { if {[regexp {[=]+} $line]} { set mode process } else { if {$mode eq {process}} { if {[regexp -nocase -all -line -- \ { ([0-9]+)\..*([0-9]{2}:[0-9]{2}\.[0-9]{2}).*([0-9];{2}:[0-9]{2}\.[0-9]{2}).*}\ $line -> total_trks length begin]} { # get tracks, indexed from 0 set cdtoc(num_trks) $total_trks set cdtoc([incr total_trks -1]) [getMCF $begin] } elseif {[regexp -nocase -all -line -- {TOTAL.*([0-9]{2}:[0-9]{2}\.[0-9]{2})} $line -> begin]} { # # get total_tracks time # additional lead out 2s # set cdtoc(total_trks) [getMCF $begin 2] set cdtoc(total_seconds) [expr {[lindex $cdtoc(total_trks) 0] * 60 + [lindex $cdtoc(total_trks) 1]}] set mode skip } } else { # in skip mode, possible errors are reported if {[string match {*Unable*} $line]} { return -code error $result } } } } } # Now starts the discid algorithm starts # This proc calculates a sum over of the digits of all tracks # It is a direct translation of the C-Code provided in the # freedb FAQ proc cddb_sum {n} { set ret 0 while {$n > 0} { set ret [expr {$ret + ($n % 10)}] set n [expr {$n / 10}] } return $ret } # # calculate the actual discid # proc cddbID {} { variable cdtoc set t 0 set i 0 set n 0 set total_trks $cdtoc(num_trks) while {$i < $total_trks} { set jmin [lindex $cdtoc($i) 0] set jsec [lindex $cdtoc($i) 1] set jval [expr {($jmin * 60) + $jsec}] set n [expr {$n + [cddb_sum $jval]}] incr i } set jmin [lindex $cdtoc(total_trks) 0] set jsec [lindex $cdtoc(total_trks) 1] set kmin [lindex $cdtoc(0) 0] set ksec [lindex $cdtoc(0) 1] set t [expr {(($jmin*60) + $jsec) - (($kmin*60)+$ksec)}] set r_int [expr {($n % 0xff) << 24 | $t << 8 | $total_trks}] set ret [format %08x $r_int] set cdtoc(cddbID) $ret return } } ;# end namespace
See also FreeDB Access