Handle off-by-1 errors in cdparanoia progress parsing

Fixes the exception I got on ripping The Strokes - Someday
This commit is contained in:
Thomas Vander Stichele
2013-02-24 11:37:13 +01:00
parent f890ba7f57
commit 3f4291bb18
8 changed files with 181 additions and 13 deletions

View File

@@ -41,3 +41,6 @@ CDROMS
PLEXTOR CD-R PX-W8432T Read offset of device is: 355.
Test discs
----------
The Strokes - Someday (promo): has 1 frame silence marked as SILENCE

View File

@@ -30,7 +30,7 @@ from morituri.extern.log import log
FRAMES_PER_SECOND = 75
SAMPLES_PER_FRAME = 588
SAMPLES_PER_FRAME = 588 # a sample is 2 16-bit values, left and right channel
WORDS_PER_FRAME = SAMPLES_PER_FRAME * 2
BYTES_PER_FRAME = SAMPLES_PER_FRAME * 4

View File

@@ -64,19 +64,21 @@ class ChecksumException(Exception):
pass
# example:
# ##: 0 [read] @ 24696
_PROGRESS_RE = re.compile(r"""
^\#\#: (?P<code>.+)\s # function code
\[(?P<function>.*)\]\s@\s # function name
(?P<offset>\d+) # offset
^\#\#: (?P<code>.+)\s # function code
\[(?P<function>.*)\]\s@\s # [function name] @
(?P<offset>\d+) # offset in words (2-byte one channel value)
""", re.VERBOSE)
_ERROR_RE = re.compile("^scsi_read error:")
# from reading cdparanoia source code, it looks like offset is reported in
# number of single-channel samples, ie. 2 bytes per unit, and absolute
# number of single-channel samples, ie. 2 bytes (word) per unit, and absolute
class ProgressParser(object):
class ProgressParser(log.Loggable):
read = 0 # last [read] frame
wrote = 0 # last [wrote] frame
errors = 0 # count of number of scsi errors
@@ -128,13 +130,15 @@ class ProgressParser(object):
# set nframes if not yet set
if self._nframes is None and self.read != 0:
self._nframes = frameOffset - self.read
self.debug('set nframes to %r', self._nframes)
# set firstFrames if not yet set
if self._firstFrames is None:
self._firstFrames = frameOffset - self.start
self.debug('set firstFrames to %r', self._firstFrames)
markStart = None
markEnd = None
markEnd = None # the next unread frame (half-inclusive)
# verify it either read nframes more or went back for verify
if frameOffset > self.read:
@@ -165,10 +169,11 @@ class ProgressParser(object):
# cdparanoia reads quite a bit beyond the current track before it
# goes back to verify; don't count those
if markEnd > self.stop:
markEnd = self.stop
if markStart > self.stop:
markStart = self.stop
# markStart, markEnd of 0, 21 with stop 0 should give 1 read
if markEnd > self.stop + 1:
markEnd = self.stop + 1
if markStart > self.stop + 1:
markStart = self.stop + 1
self.reads += markEnd - markStart
@@ -185,8 +190,9 @@ class ProgressParser(object):
Each frame gets read twice.
More than two reads for a frame reduce track quality.
"""
frames = self.stop - self.start + 1
frames = self.stop - self.start + 1 # + 1 since stop is inclusive
reads = self.reads
self.debug('getTrackQuality: frames %d, reads %d' % (frames, reads))
# don't go over a 100%; we know cdparanoia reads each frame at least
# twice

View File

@@ -44,8 +44,10 @@ EXTRA_DIST = \
track-single.cue \
cdparanoia.progress \
cdparanoia.progress.error \
cdparanoia.progress.strokes \
cdrdao.readtoc.progress \
silentalarm.result.pickle \
strokes-someday.toc \
totbl.fast.toc \
track.flac \
cache/result/fe105a11.pickle \

View File

@@ -0,0 +1,111 @@
Sending all callbacks to stderr for wrapper script
cdparanoia III release 10.2 (September 11, 2008)
Ripping from sector 0 (track 0 [0:00.00])
to sector 0 (track 0 [0:00.00])
outputting to cdda.wav
##: 0 [read] @ 24696
##: 0 [read] @ 56448
##: 0 [read] @ 88200
##: 0 [read] @ 119952
##: 0 [read] @ 151704
##: 0 [read] @ 183456
##: 0 [read] @ 215208
##: 0 [read] @ 246960
##: 0 [read] @ 278712
##: 0 [read] @ 310464
##: 0 [read] @ 342216
##: 0 [read] @ 373968
##: 0 [read] @ 405720
##: 0 [read] @ 437472
##: 0 [read] @ 469224
##: 0 [read] @ 500976
##: 0 [read] @ 532728
##: 0 [read] @ 564480
##: 0 [read] @ 596232
##: 0 [read] @ 627984
##: 0 [read] @ 659736
##: 0 [read] @ 691488
##: 0 [read] @ 723240
##: 0 [read] @ 754992
##: 0 [read] @ 786744
##: 0 [read] @ 818496
##: 0 [read] @ 850248
##: 0 [read] @ 882000
##: 0 [read] @ 913752
##: 0 [read] @ 945504
##: 0 [read] @ 977256
##: 0 [read] @ 1009008
##: 0 [read] @ 1040760
##: 0 [read] @ 1072512
##: 0 [read] @ 1104264
##: 0 [read] @ 1136016
##: 0 [read] @ 1167768
##: 0 [read] @ 1199520
##: 0 [read] @ 1231272
##: 0 [read] @ 1263024
##: 0 [read] @ 1294776
##: 0 [read] @ 1326528
##: 0 [read] @ 1358280
##: 0 [read] @ 1390032
##: 0 [read] @ 1410024
##: 0 [read] @ 23520
##: 0 [read] @ 55272
##: 0 [read] @ 87024
##: 0 [read] @ 118776
##: 0 [read] @ 150528
##: 0 [read] @ 182280
##: 0 [read] @ 214032
##: 0 [read] @ 245784
##: 0 [read] @ 277536
##: 0 [read] @ 309288
##: 0 [read] @ 341040
##: 0 [read] @ 372792
##: 0 [read] @ 404544
##: 0 [read] @ 436296
##: 0 [read] @ 468048
##: 0 [read] @ 499800
##: 0 [read] @ 531552
##: 0 [read] @ 563304
##: 0 [read] @ 595056
##: 0 [read] @ 626808
##: 0 [read] @ 658560
##: 0 [read] @ 690312
##: 0 [read] @ 722064
##: 0 [read] @ 753816
##: 0 [read] @ 785568
##: 0 [read] @ 817320
##: 0 [read] @ 849072
##: 0 [read] @ 880824
##: 0 [read] @ 912576
##: 0 [read] @ 944328
##: 0 [read] @ 976080
##: 0 [read] @ 1007832
##: 0 [read] @ 1039584
##: 0 [read] @ 1071336
##: 0 [read] @ 1103088
##: 0 [read] @ 1134840
##: 0 [read] @ 1166592
##: 0 [read] @ 1198344
##: 0 [read] @ 1230096
##: 0 [read] @ 1261848
##: 0 [read] @ 1293600
##: 0 [read] @ 1325352
##: 0 [read] @ 1357104
##: 0 [read] @ 1388856
##: 0 [read] @ 1410024
##: 1 [verify] @ 0
##: 3 [correction] @ 1005459
##: 3 [correction] @ 1005480
##: 1 [verify] @ 1005480
##: 1 [verify] @ 1005480
##: -2 [wrote] @ 1175
##: -2 [wrote] @ 1176
##: -1 [finished] @ 1175
Done.

View File

@@ -0,0 +1,12 @@
CD_DA
// Track 1
TRACK AUDIO
COPY
NO PRE_EMPHASIS
TWO_CHANNEL_AUDIO
SILENCE 00:00:01
FILE "data.wav" 0 03:06:59
START 00:00:01

View File

@@ -318,3 +318,21 @@ class TOTBLTestCase(common.TestCase):
def testCDDBId(self):
self.toc.table.absolutize()
self.assertEquals(self.toc.table.getCDDBDiscId(), '810b7b0b')
# The Strokes - Someday has a 1 frame SILENCE marked as such in toc
class StrokesTestCase(common.TestCase):
def setUp(self):
self.path = os.path.join(os.path.dirname(__file__),
u'strokes-someday.toc')
self.toc = toc.TocFile(self.path)
self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 1)
def testIndexes(self):
t = self.toc.table.tracks[0]
self.assertEquals(t.getIndex(0).relative, 0)
self.assertEquals(t.getIndex(1).relative, 1)

View File

@@ -25,7 +25,23 @@ class ParseTestCase(common.TestCase):
self._parser.parse(line)
q = '%.01f %%' % (self._parser.getTrackQuality() * 100.0, )
self.assertEquals(q, '99.7 %')
self.assertEquals(q, '99.6 %')
class Parse1FrameTestCase(common.TestCase):
def setUp(self):
path = os.path.join(os.path.dirname(__file__),
'cdparanoia.progress.strokes')
self._parser = cdparanoia.ProgressParser(start=0, stop=0)
self._handle = open(path)
def testParse(self):
for line in self._handle.readlines():
self._parser.parse(line)
q = '%.01f %%' % (self._parser.getTrackQuality() * 100.0, )
self.assertEquals(q, '100.0 %')
class ErrorTestCase(common.TestCase):