From 271bca108fc37d5cecf0f8ea2c38b074b4376b38 Mon Sep 17 00:00:00 2001 From: Thomas Vander Stichele Date: Sun, 22 Mar 2009 15:07:42 +0000 Subject: [PATCH] * examples/ARcue.py: * morituri/common/task.py: First version that seems to get some AccurateRip checksums right. --- ChangeLog | 6 ++++ examples/ARcue.py | 7 ++-- morituri/common/task.py | 73 +++++++++++++++++++++++++++++++++-------- 3 files changed, 70 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index fbedc30..b2109de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2009-03-22 Thomas Vander Stichele + + * examples/ARcue.py: + * morituri/common/task.py: + First version that seems to get some AccurateRip checksums right. + 2009-02-28 Thomas Vander Stichele * morituri/image/cue.py: diff --git a/examples/ARcue.py b/examples/ARcue.py index 69fdb26..9a92caa 100644 --- a/examples/ARcue.py +++ b/examples/ARcue.py @@ -35,7 +35,9 @@ def main(path): for track in cuefile.tracks: index = track._indexes[1] + length = cuefile.getTrackLength(track) file = index[1] + offset = index[0] # find an actual potential file crctask = None @@ -46,12 +48,13 @@ def main(path): for ext in ['wav', 'flac']: path = '%s.%s' % (noext, ext) if os.path.exists(path): - crctask = task.CRCTask(path) + crctask = task.CRCAudioRipTask(path, offset * 588, + length * 588) if not crctask: print 'error: path %s not found' % file.path - print 'Analyzing', file.path + print 'Analyzing', file.path, "from CD frame ", offset, length, "CD frames" runner = task.SyncRunner(crctask) runner.run() diff --git a/morituri/common/task.py b/morituri/common/task.py index ec4ea72..bd38272 100644 --- a/morituri/common/task.py +++ b/morituri/common/task.py @@ -22,6 +22,7 @@ import os import sys +import struct import zlib import gobject @@ -37,8 +38,8 @@ class Task(object): _listeners = None def debug(self, *args, **kwargs): - print args, kwargs - sys.stdout.flush() + #print args, kwargs + #sys.stdout.flush() pass def start(self): @@ -70,13 +71,22 @@ class CRCTask(Task): # this object needs a main loop to stop description = 'Calculating CRC checksum...' - def __init__(self, path, frameStart=0, frameEnd=-1): + def __init__(self, path, frameStart=0, frameLength=-1): + """ + A frame is considered a set of samples for each channel; + ie 16 bit stereo is 4 bytes per frame. + If frameLength < 0 it is treated as 'unknown' and calculated. + + @type frameStart: int + @param frameStart: the frame to start at + """ if not os.path.exists(path): raise IndexError, '%s does not exist' % path self._path = path self._frameStart = frameStart - self._frameEnd = frameEnd + self._frameLength = frameLength + self._frameEnd = None self._crc = 0 self._bytes = 0 self._first = None @@ -98,10 +108,13 @@ class CRCTask(Task): self.debug('query duration') sink = self._pipeline.get_by_name('sink') - if self._frameEnd == -1: + if self._frameLength < 0: length, _ = sink.query_duration(gst.FORMAT_DEFAULT) - self._frameEnd = length - 1 - self.debug('last frame is', self._frameEnd) + print 'total length', length + self._frameLength = length - self._frameStart + self.debug('frame length is', self._frameLength) + print 'frame length is', self._frameLength + self._frameEnd = self._frameStart + self._frameLength - 1 self.debug('event') @@ -111,7 +124,8 @@ class CRCTask(Task): gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, self._frameStart, gst.SEEK_TYPE_SET, self._frameEnd + 1) # half-inclusive interval - # FIXME: sending it with frameEnd set screws up the seek, we don't get everything + # FIXME: sending it with frameEnd set screws up the seek, we don't get + # everything for flac; fixed in recent -good result = sink.send_event(event) #self.debug('event sent') #self.debug(result) @@ -132,14 +146,21 @@ class CRCTask(Task): self._last = buffer assert len(buffer) % 4 == 0, "buffer is not a multiple of 4 bytes" - self._bytes += len(buffer) # update progress frame = self._first + self._bytes / 4 - progress = float((frame - self._frameStart)) / float((self._frameEnd - self._frameStart)) + framesDone = frame - self._frameStart + progress = float(framesDone) / float((self._frameLength)) self.setProgress(progress) - self._crc = zlib.crc32(buffer, self._crc) + self._crc = self.do_crc_buffer(buffer, self._crc) + self._bytes += len(buffer) + + def do_crc_buffer(self, buffer, crc): + """ + Subclasses should implement this. + """ + raise NotImplementedError def _eos_cb(self, sink): # get the last one; FIXME: why does this not get to us before ? @@ -153,14 +174,34 @@ class CRCTask(Task): self._crc = self._crc % 2 ** 32 last = self._last.offset + len(self._last) / 4 - 1 self.debug("last sample:", last) - self.debug("frame end:", self._frameEnd) + self.debug("frame length:", self._frameLength) self.debug("CRC: %08X" % self._crc) self.debug("bytes: %d" % self._bytes) if self._frameEnd != last: - print 'ERROR: did not get all frames, %d missing' % (self._frameEnd - last) + print 'ERROR: did not get all frames, %d missing' % ( + self._frameEnd - last) self.crc = self._crc Task.stop(self) +class CRC32Task(CRCTask): + """ + I do a simple CRC32 check. + """ + def do_crc_buffer(self, buffer, crc): + return zlib.crc32(buffer, crc) + +class CRCAudioRipTask(CRCTask): + def do_crc_buffer(self, buffer, crc): + values = struct.unpack("<%dI" % (len(buffer) / 4), buffer) + for i, value in enumerate(values): + crc += (self._bytes / 4 + i + 1) * value + crc &= 0xFFFFFFFF + offset = self._bytes / 4 + i + 1 + #if offset % 588 == 0: + # print 'THOMAS: frame %d, offset %d, value %d, CRC %d' % ( + # offset / 588, offset, value, crc) + return crc + class SyncRunner: def __init__(self, task): self._task = task @@ -175,7 +216,11 @@ class SyncRunner: pass def progress(self, value): - pass + sys.stdout.write('Progress: %3d %%\r' % (value * 100.0)) + sys.stdout.flush() + + if value >= 1.0: + print 'Progress: 100 %' def stop(self): self._loop.quit()