|
|
@@ -5,6 +5,8 @@ import UserDict |
|
|
|
|
|
|
|
from ctypes import * |
|
|
|
|
|
|
|
__all__ = [ 'FLACDec' ] |
|
|
|
|
|
|
|
# Find out if we need to endian swap the buffer |
|
|
|
t = array.array('H', '\x00\x01') |
|
|
|
if t[0] == 1: |
|
|
@@ -407,28 +409,74 @@ def clientdatawrapper(fun): |
|
|
|
|
|
|
|
return newfun |
|
|
|
|
|
|
|
def getindex(ar, idx): |
|
|
|
for i in ar: |
|
|
|
if idx == i['number']: |
|
|
|
return i |
|
|
|
|
|
|
|
raise ValueError('index %d not present: %s' % (idx, `ar`)) |
|
|
|
|
|
|
|
def _checksum(n): |
|
|
|
ret = 0 |
|
|
|
|
|
|
|
while n > 0: |
|
|
|
n, m = divmod(n, 10) |
|
|
|
ret += m |
|
|
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
def _cuetotrackinfo(cuesheet): |
|
|
|
'''XXX - do not use. This will not work because the cuesheet does |
|
|
|
not include data tracks!''' |
|
|
|
if not cuesheet['is_cd']: |
|
|
|
raise ValueError('cuesheet isn\'t for a cd') |
|
|
|
|
|
|
|
tracks = [] |
|
|
|
ta = cuesheet['tracks_array'] |
|
|
|
tot = 0 |
|
|
|
cksum = 0 |
|
|
|
for i in xrange(1, len(ta)): |
|
|
|
offstart = ta[i - 1]['offset'] |
|
|
|
offend = ta[i]['offset'] |
|
|
|
secs = offend / 44100 - offstart / 44100 |
|
|
|
#print ta[i - 1]['number'], secs, offstart / (2352 / 4), (offend - offstart) / (2352 / 4) |
|
|
|
tot += secs |
|
|
|
cksum += _checksum(offstart / 44100 + 2) |
|
|
|
|
|
|
|
#print tot |
|
|
|
tracks.append(divmod(tot, 60)) |
|
|
|
|
|
|
|
#print `tracks` |
|
|
|
return (long(cksum) % 0xff) << 24 | tot << 8 | (len(ta) - 1), 0 |
|
|
|
|
|
|
|
class FLACDec(object): |
|
|
|
'''Class to support flac decoding.''' |
|
|
|
|
|
|
|
channels = property(lambda x: x._channels) |
|
|
|
samplerate = property(lambda x: x._samplerate) |
|
|
|
bitspersample = property(lambda x: x._bitspersample) |
|
|
|
totalsamples = property(lambda x: x._totalsamples) |
|
|
|
bytespersample = property(lambda x: x._bytespersample) |
|
|
|
vorbis = property(lambda x: x._vorbis) |
|
|
|
cuesheet = property(lambda x: x._cuesheet) |
|
|
|
|
|
|
|
def __len__(self): |
|
|
|
return self._total_samps |
|
|
|
|
|
|
|
def __init__(self, file): |
|
|
|
'''Pass in the file name. We currently do not support file objects.''' |
|
|
|
|
|
|
|
self.flacdec = None |
|
|
|
self._lasterror = None |
|
|
|
|
|
|
|
# We need to keep references to the callback functions |
|
|
|
# around so they won't be garbage collected. |
|
|
|
self.write_wrap = FLAC__StreamDecoderWriteCallback( |
|
|
|
clientdatawrapper(self.cb_write)) |
|
|
|
clientdatawrapper(self._cb_write)) |
|
|
|
self.metadata_wrap = FLAC__StreamDecoderMetadataCallback( |
|
|
|
clientdatawrapper(self.cb_metadata)) |
|
|
|
clientdatawrapper(self._cb_metadata)) |
|
|
|
self.error_wrap = FLAC__StreamDecoderErrorCallback( |
|
|
|
clientdatawrapper(self.cb_error)) |
|
|
|
clientdatawrapper(self._cb_error)) |
|
|
|
|
|
|
|
self.flacdec = flaclib.FLAC__stream_decoder_new() |
|
|
|
if self.flacdec == 0: |
|
|
@@ -448,8 +496,8 @@ class FLACDec(object): |
|
|
|
raise ValueError( |
|
|
|
FLAC__StreamDecoderInitStatusString[status]) |
|
|
|
|
|
|
|
self.vorbis = None |
|
|
|
self.cuesheet = None |
|
|
|
self._vorbis = None |
|
|
|
self._cuesheet = None |
|
|
|
|
|
|
|
flaclib.FLAC__stream_decoder_process_until_end_of_metadata( |
|
|
|
self.flacdec) |
|
|
@@ -463,6 +511,8 @@ class FLACDec(object): |
|
|
|
self.vorbis = '', VorbisComments() |
|
|
|
|
|
|
|
def close(self): |
|
|
|
'''Finish decoding and close all the objects.''' |
|
|
|
|
|
|
|
if self.flacdec is None: |
|
|
|
return |
|
|
|
|
|
|
@@ -481,7 +531,7 @@ class FLACDec(object): |
|
|
|
pass |
|
|
|
#raise ValueError('invalid md5') |
|
|
|
|
|
|
|
def cb_write(self, frame_p, buffer_pp): |
|
|
|
def _cb_write(self, frame_p, buffer_pp): |
|
|
|
frame = frame_p[0] |
|
|
|
#print 'write:', `frame` |
|
|
|
#for i in xrange(frame.header.channels): |
|
|
@@ -512,7 +562,7 @@ class FLACDec(object): |
|
|
|
|
|
|
|
return 0 |
|
|
|
|
|
|
|
def cb_metadata(self, metadata_p): |
|
|
|
def _cb_metadata(self, metadata_p): |
|
|
|
md = metadata_p[0] |
|
|
|
#print 'metadata:', `md` |
|
|
|
if md.type == FLAC__METADATA_TYPE_STREAMINFO: |
|
|
@@ -525,17 +575,17 @@ class FLACDec(object): |
|
|
|
si.bits_per_sample / 8 |
|
|
|
#print `si` |
|
|
|
elif md.type == FLAC__METADATA_TYPE_VORBIS_COMMENT: |
|
|
|
self.vorbis = md.data.vorbis_comment.asobj() |
|
|
|
self._vorbis = md.data.vorbis_comment.asobj() |
|
|
|
#print 'vc:', `md.data.vorbis_comment` |
|
|
|
#print 'v:', `self.vorbis` |
|
|
|
elif md.type == FLAC__METADATA_TYPE_CUESHEET: |
|
|
|
self.cuesheet = md.data.cue_sheet.asobj() |
|
|
|
self._cuesheet = md.data.cue_sheet.asobj() |
|
|
|
#print 'cs:', `md.data.cue_sheet` |
|
|
|
#print 'c:', `self.cuesheet` |
|
|
|
else: |
|
|
|
print 'unknown metatype:', md.type |
|
|
|
|
|
|
|
def cb_error(self, errstatus): |
|
|
|
def _cb_error(self, errstatus): |
|
|
|
#print 'error:', `errstatus` |
|
|
|
self._lasterror = errstatus |
|
|
|
|
|
|
@@ -546,6 +596,8 @@ class FLACDec(object): |
|
|
|
state = property(getstate) |
|
|
|
|
|
|
|
def goto(self, pos): |
|
|
|
'''Go to sample possition.''' |
|
|
|
|
|
|
|
if self.flacdec is None: |
|
|
|
raise ValueError('closed') |
|
|
|
|
|
|
|