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 namespaceSee also FreeDB Access