* morituri/program/cdrdao.py:

read all sessions by reading session 9.
	* morituri/image/toc.py:
	  Parse ZERO statements properly.
	  Also set absolute offsets when we know them from the .toc file.
	  Properly set audio flag on tracks.
	  FIXME: probably doesn't work if the .toc does not have lengths.
	* morituri/image/table.py:
	  Add some debug to cddb disc id calculation.
	  Fix absolutize function, it was going one index too far.
	  raise ValueError when overriding .absolute with a wrong value.
	* examples/readdisc.py:
	  Show CDDB disc id at the start.
	  Assert when toc and table have different disc id's (to be fixed)
	* morituri/test/test_image_cue.py:
	  Update for having the table already with absolute values.
	* morituri/test/test_image_toc.py:
	  Add Ladyhawke CDDB test, it has a data track.
This commit is contained in:
Thomas Vander Stichele
2009-05-16 19:09:27 +00:00
parent 2f43fe3a33
commit 483c7b8a0c
7 changed files with 147 additions and 27 deletions

View File

@@ -1,3 +1,24 @@
2009-05-16 Thomas Vander Stichele <thomas at apestaart dot org>
* morituri/program/cdrdao.py:
read all sessions by reading session 9.
* morituri/image/toc.py:
Parse ZERO statements properly.
Also set absolute offsets when we know them from the .toc file.
Properly set audio flag on tracks.
FIXME: probably doesn't work if the .toc does not have lengths.
* morituri/image/table.py:
Add some debug to cddb disc id calculation.
Fix absolutize function, it was going one index too far.
raise ValueError when overriding .absolute with a wrong value.
* examples/readdisc.py:
Show CDDB disc id at the start.
Assert when toc and table have different disc id's (to be fixed)
* morituri/test/test_image_cue.py:
Update for having the table already with absolute values.
* morituri/test/test_image_toc.py:
Add Ladyhawke CDDB test, it has a data track.
2009-05-16 Thomas Vander Stichele <thomas at apestaart dot org>
* examples/readdisc.py:

View File

@@ -229,6 +229,7 @@ def main(argv):
assert ittoc.hasTOC()
# already show us some info based on this
print "CDDB disc id", ittoc.getCDDBDiscId()
metadata = musicbrainz(ittoc.getMusicBrainzDiscId())
# now, read the complete index table, which is slower
@@ -241,6 +242,11 @@ def main(argv):
assert itable.hasTOC()
assert itable.getCDDBDiscId() == ittoc.getCDDBDiscId(), \
"full table's id %s differs from toc id %s" % (
itable.getCDDBDiscId(), ittoc.getCDDBDiscId())
assert itable.getMusicBrainzDiscId() == ittoc.getMusicBrainzDiscId()
lastTrackStart = 0
# check for hidden track one audio

View File

@@ -208,19 +208,28 @@ class Table(object, log.Loggable):
#if self.getTrackStart(1) > 0:
# delta = 0
debug = [str(len(self.tracks))]
for track in self.tracks:
offset = self.getTrackStart(track.number) + delta
debug.append(str(offset))
seconds = offset / common.FRAMES_PER_SECOND
n += self._cddbSum(seconds)
last = self.tracks[-1]
# the 'real' leadout, not offset by 150 frames
# print 'THOMAS: disc leadout', self.leadout
leadout = self.getTrackEnd(last.number) + 1
self.debug('leadout LBA: %d', leadout)
startSeconds = self.getTrackStart(1) / common.FRAMES_PER_SECOND
leadoutSeconds = leadout / common.FRAMES_PER_SECOND
t = leadoutSeconds - startSeconds
debug.append(str(leadoutSeconds + 2)) # 2 is the 150 frame cddb offset
value = (n % 0xff) << 24 | t << 8 | len(self.tracks)
# compare this debug line to cd-discid output
self.debug('cddb disc id debug: %s',
" ".join(["%08x" % value, ] + debug))
return "%08x" % value
@@ -457,20 +466,27 @@ class Table(object, log.Loggable):
# the first cut is the deepest
counter = index.counter
#for t in self.tracks: print t, t.indexes
self.debug('absolutizing')
while True:
track = self.tracks[t - 1]
index = track.getIndex(i)
assert track.number == t
assert index.number == i
if index.counter is None:
self.debug('Track %d, index %d has no counter', t, i)
break
if index.counter != counter:
self.debug('Track %d, index %d has a different counter', t, i)
break
track = self.tracks[t - 1]
index = track.getIndex(i)
assert track.number == t
assert index.number == i
self.debug('Setting absolute offset %d on track %d, index %d',
index.relative, t, i)
if index.absolute is not None:
if index.absolute != index.relative:
msg = 'Track %d, index %d had absolute %d,' \
' overriding with %d' % (
t, i, index.absolute, index.relative)
raise ValueError(msg)
index.absolute = index.relative
try:
t, i = self.getNextTrackIndex(t, i)

View File

@@ -39,7 +39,7 @@ _CATALOG_RE = re.compile(r'^CATALOG "(?P<catalog>\d+)"$')
# records
_TRACK_RE = re.compile(r"""
^TRACK # TRACK
\s(?P<mode>.+)$ # mode (AUDIO, MODEx/2xxx, ...)
\s(?P<mode>.+)$ # mode (AUDIO, MODE2_FORM_MIX, MODEx/2xxx, ...)
""", re.VERBOSE)
_ISRC_RE = re.compile(r'^ISRC "(?P<isrc>\w+)"$')
@@ -50,6 +50,13 @@ _SILENCE_RE = re.compile(r"""
\s(?P<length>.*)$ # pre-gap length
""", re.VERBOSE)
# ZERO is used as pre-gap source when switching mode
_ZERO_RE = re.compile(r"""
^ZERO # ZERO
\s(?P<mode>.+) # mode (AUDIO, MODEx/2xxx, ...)
\s(?P<length>.*)$ # zero length
""", re.VERBOSE)
_FILE_RE = re.compile(r"""
^FILE # FILE
@@ -58,6 +65,14 @@ _FILE_RE = re.compile(r"""
\s(?P<length>.+)$ # stop offset
""", re.VERBOSE)
_DATAFILE_RE = re.compile(r"""
^DATAFILE # DATA FILE
\s+"(?P<name>.*)" # 'file name' in quotes
\s+(?P<length>\S+) # start offset
\s*.* # possible // comment
""", re.VERBOSE)
# FIXME: start can be 0
_START_RE = re.compile(r"""
^START # START
@@ -83,7 +98,8 @@ class TocFile(object, log.Loggable):
counter = 0
trackNumber = 0
indexNumber = 0
currentOffset = 0 # running absolute offset of where each track starts
absoluteOffset = 0 # running absolute offset of where each track starts
relativeOffset = 0 # running relative offset, relative to counter source
currentLength = 0 # accrued during TRACK record parsing, current track
totalLength = 0 # accrued during TRACK record parsing, total disc
pregapLength = 0 # length of the pre-gap, current track
@@ -128,13 +144,16 @@ class TocFile(object, log.Loggable):
# set index 1 of previous track if there was one, using
# pregapLength if applicable
if currentTrack:
# FIXME: why not set absolute offsets too ?
currentTrack.index(1, path=currentFile.path,
relative=currentOffset + pregapLength, counter=counter)
absolute=absoluteOffset + pregapLength,
relative=relativeOffset + pregapLength, counter=counter)
self.debug('track %d, added index %r',
currentTrack.number, currentTrack.getIndex(1))
trackNumber += 1
currentOffset += currentLength
absoluteOffset += currentLength
relativeOffset += currentLength
totalLength += currentLength
currentLength = 0
indexNumber = 1
@@ -142,7 +161,9 @@ class TocFile(object, log.Loggable):
pregapLength = 0
# FIXME: track mode
currentTrack = table.Track(trackNumber)
self.debug('found track %d, mode %s', trackNumber, trackMode)
audio = trackMode == 'AUDIO'
currentTrack = table.Track(trackNumber, audio=audio)
self.table.tracks.append(currentTrack)
continue
@@ -163,6 +184,16 @@ class TocFile(object, log.Loggable):
length = m.group('length')
currentLength += common.msfToFrames(length)
# look for ZERO lines
m = _ZERO_RE.search(line)
if m:
if currentFile is not None:
self.debug('ZERO after FILE, increasing counter')
counter += 1
currentFile = None
length = m.group('length')
currentLength += common.msfToFrames(length)
# look for FILE lines
m = _FILE_RE.search(line)
if m:
@@ -174,12 +205,32 @@ class TocFile(object, log.Loggable):
common.msfToFrames(length))
if not currentFile or filePath != currentFile.path:
counter += 1
relativeOffset = 0
self.debug('track %d, switched to new FILE, increased counter to %d',
trackNumber, counter)
currentFile = File(filePath, start, length)
#currentOffset += common.msfToFrames(start)
#absoluteOffset += common.msfToFrames(start)
currentLength += common.msfToFrames(length)
# look for DATAFILE lines
m = _DATAFILE_RE.search(line)
if m:
filePath = m.group('name')
length = m.group('length')
# print 'THOMAS', length
self.debug('FILE %s, length %r',
filePath, common.msfToFrames(length))
if not currentFile or filePath != currentFile.path:
counter += 1
relativeOffset = 0
self.debug('track %d, switched to new FILE, increased counter to %d',
trackNumber, counter)
# FIXME: assume that a MODE2_FORM_MIX track always starts at 0
currentFile = File(filePath, 0, length)
#absoluteOffset += common.msfToFrames(start)
currentLength += common.msfToFrames(length)
# look for START lines
m = _START_RE.search(line)
if m:
@@ -190,7 +241,8 @@ class TocFile(object, log.Loggable):
length = common.msfToFrames(m.group('length'))
currentTrack.index(0, path=currentFile.path,
relative=currentOffset, counter=counter)
absolute=absoluteOffset,
relative=relativeOffset, counter=counter)
self.debug('track %d, added index %r',
currentTrack.number, currentTrack.getIndex(0))
# store the pregapLength to add it when we index 1 for this
@@ -215,7 +267,8 @@ class TocFile(object, log.Loggable):
# handle index 1 of final track, if any
if currentTrack:
currentTrack.index(1, path=currentFile.path,
relative=currentOffset + pregapLength, counter=counter)
absolute=absoluteOffset + pregapLength,
relative=relativeOffset + pregapLength, counter=counter)
self.debug('track %d, added index %r',
currentTrack.number, currentTrack.getIndex(1))

View File

@@ -202,7 +202,8 @@ class CDRDAOTask(task.Task):
bufsize=bufsize,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True)
self.debug('Started cdrdao with pid %d', self._popen.pid)
self.debug('Started cdrdao with pid %d and options %r',
self._popen.pid, self.options)
self.runner.schedule(1.0, self._read, runner)
@@ -269,7 +270,8 @@ class ReadTableTask(CDRDAOTask):
os.close(fd)
os.unlink(self._tocfilepath)
self.options = ['read-toc', self._tocfilepath]
self.options = ['read-toc', '--session', '9',
self._tocfilepath, ]
def readbytes(self, bytes):
self.parser.read(bytes)
@@ -318,7 +320,10 @@ class ReadTOCTask(CDRDAOTask):
os.close(fd)
os.unlink(self._toc)
self.options = ['read-toc', '--fast-toc', self._toc]
# Reading a non-existent session gives you output for all sessions
# 9 should be a safe number
self.options = ['read-toc', '--fast-toc', '--session', '9',
self._toc, ]
def readbytes(self, bytes):
self.parser.read(bytes)

View File

@@ -55,20 +55,19 @@ class WriteCueFileTestCase(unittest.TestCase):
os.close(fd)
it = table.Table()
t = table.Track(1)
t.index(1, path='track01.wav', relative=0, counter=1)
t.index(1, absolute=0, path='track01.wav', relative=0, counter=1)
it.tracks.append(t)
t = table.Track(2)
t.index(0, path='track01.wav', relative=1000, counter=1)
t.index(1, path='track02.wav', relative=0, counter=2)
t.index(0, absolute=1000, path='track01.wav', relative=1000, counter=1)
t.index(1, absolute=2000, path='track02.wav', relative=0, counter=2)
it.tracks.append(t)
it.absolutize()
it.leadout = 2000
it.leadout = 3000
self.assertEquals(it.cue(), """REM DISCID 04001A02
self.assertEquals(it.cue(), """REM DISCID 0C002802
REM COMMENT "Morituri"
FILE "track01.wav" WAVE
TRACK 01 AUDIO

View File

@@ -49,9 +49,9 @@ class CureTestCase(unittest.TestCase):
self.assertEquals(index.relative, value)
def testSetFile(self):
self._assertAbsolute(1, 1, None)
self._assertAbsolute(2, 0, None)
self._assertAbsolute(2, 1, None)
self._assertAbsolute(1, 1, 0)
self._assertAbsolute(2, 0, 28245)
self._assertAbsolute(2, 1, 28324)
self._assertPath(1, 1, "data.wav")
def dump():
@@ -118,8 +118,7 @@ class BlocTestCase(unittest.TestCase):
# This disc has a pre-gap, so is a good test for .CUE writing
def testConvertCue(self):
self.failIf(self.toc.table.hasTOC())
self.toc.table.absolutize()
#self.toc.table.absolutize()
self.failUnless(self.toc.table.hasTOC())
cue = self.toc.table.cue()
ref = open(os.path.join(os.path.dirname(__file__),
@@ -128,6 +127,10 @@ class BlocTestCase(unittest.TestCase):
def testCDDBId(self):
self.toc.table.absolutize()
# cd-discid output:
# ad0be00d 13 15370 35019 51532 69190 84292 96826 112527 132448
# 148595 168072 185539 203331 222103 3244
self.assertEquals(self.toc.table.getCDDBDiscId(), 'ad0be00d')
def testAccurateRip(self):
@@ -156,10 +159,27 @@ class BreedersTestCase(unittest.TestCase):
self.assertEquals(cdt['TITLE'], 'OVERGLAZED')
def testConvertCue(self):
self.failIf(self.toc.table.hasTOC())
self.toc.table.absolutize()
self.failUnless(self.toc.table.hasTOC())
cue = self.toc.table.cue()
ref = open(os.path.join(os.path.dirname(__file__),
'breeders.cue')).read()
self.assertEquals(cue, ref)
# Ladyhawke has a data track
class LadyhawkeTestCase(unittest.TestCase):
def setUp(self):
self.toc = toc.TocFile(os.path.join(os.path.dirname(__file__),
'ladyhawke.toc'))
self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 13)
#import code; code.interact(local=locals())
self.failIf(self.toc.table.tracks[-1].audio)
def testCDDBId(self):
self.toc.table.absolutize()
self.assertEquals(self.toc.table.getCDDBDiscId(), 'c60af50d')
# output from cd-discid:
# c60af50d 13 150 15687 31841 51016 66616 81352 99559 116070 133243
# 149997 161710 177832 207256 2807