A Python UPnP Media Server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

701 lines
20 KiB

  1. import array
  2. import os.path
  3. import string
  4. import UserDict
  5. from ctypes import *
  6. __all__ = [ 'FLACDec' ]
  7. # Find out if we need to endian swap the buffer
  8. def _islittleendian():
  9. '''Run a check to see if the box is little endian or not.'''
  10. t = array.array('H', '\x00\x01')
  11. if t[0] == 1:
  12. return False
  13. else:
  14. return True
  15. is_little_endian = _islittleendian()
  16. inter = {}
  17. try:
  18. interleavelib = CDLL(os.path.join(os.path.dirname(__file__), '_interleave.so'))
  19. for i in (16, ):
  20. f = getattr(interleavelib, 'interleave%d' % i)
  21. f.restype = None
  22. # bigendian, nchan, chanmap, chansamps, data, out
  23. outtype = locals()['c_int%d' % i]
  24. f.argtypes = [ c_int, POINTER(c_int), c_int,
  25. POINTER(POINTER(c_int32)), POINTER(outtype), ]
  26. inter[i] = outtype, f
  27. except OSError:
  28. import warnings
  29. warnings.warn('Using slow interleaver, compile the _interleave.so module to improve performance.')
  30. inter[16] = None, lambda nchan, chanmap, blksize, inbuf, ignore: \
  31. array.array('h', [ inbuf[chanmap[x % nchan]][x / nchan] for x in
  32. xrange(blksize * nchan)])
  33. flaclib = CDLL('libFLAC.so')
  34. # Defines
  35. FLAC__METADATA_TYPE_STREAMINFO = 0
  36. FLAC__METADATA_TYPE_PADDING = 1
  37. FLAC__METADATA_TYPE_APPLICATION = 2
  38. FLAC__METADATA_TYPE_SEEKTABLE = 3
  39. FLAC__METADATA_TYPE_VORBIS_COMMENT = 4
  40. FLAC__METADATA_TYPE_CUESHEET = 5
  41. FLAC__METADATA_TYPE_PICTURE = 6
  42. FLAC__METADATA_TYPE_UNDEFINED = 7
  43. FLAC__MAX_CHANNELS = 8
  44. FLAC__MAX_LPC_ORDER = 32
  45. FLAC__MAX_FIXED_ORDER = 4
  46. # Data
  47. FLAC__StreamDecoderStateString = (c_char_p * 10).in_dll(flaclib,
  48. 'FLAC__StreamDecoderStateString')
  49. FLAC__StreamDecoderInitStatusString = (c_char_p * 6).in_dll(flaclib,
  50. 'FLAC__StreamDecoderInitStatusString')
  51. FLAC__StreamDecoderErrorStatusString = (c_char_p * 4).in_dll(flaclib,
  52. 'FLAC__StreamDecoderErrorStatusString')
  53. # Enums
  54. FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0
  55. FLAC__STREAM_DECODER_READ_METADATA = 1
  56. FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC = 2
  57. FLAC__STREAM_DECODER_READ_FRAME = 3
  58. FLAC__STREAM_DECODER_END_OF_STREAM = 4
  59. FLAC__STREAM_DECODER_OGG_ERROR = 5
  60. FLAC__STREAM_DECODER_SEEK_ERROR = 6
  61. FLAC__STREAM_DECODER_ABORTED = 7
  62. FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR = 8
  63. FLAC__STREAM_DECODER_UNINITIALIZED = 9
  64. FLAC__STREAM_DECODER_INIT_STATUS_OK = 0
  65. FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER = 0
  66. FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER = 1
  67. FLAC__SUBFRAME_TYPE_CONSTANT = 0
  68. FLAC__SUBFRAME_TYPE_VERBATIM = 1
  69. FLAC__SUBFRAME_TYPE_FIXED = 2
  70. FLAC__SUBFRAME_TYPE_LPC = 3
  71. OrigStructure = Structure
  72. class Structure(Structure):
  73. def getarray(self, base):
  74. base_array = getattr(self, base)
  75. return [ base_array[x] for x in xrange(getattr(self,
  76. 'num_' + base)) ]
  77. def __getattr__(self, k):
  78. if k[-6:] == '_array':
  79. return self.getarray(k[:-6])
  80. raise AttributeError(k)
  81. def asobj(self):
  82. r = {}
  83. #print 'asobj:', `self`
  84. if hasattr(self, '_material_'):
  85. flds = self._material_
  86. else:
  87. flds = self._fields_
  88. for i in flds:
  89. attr = i[0]
  90. obj = getattr(self, attr)
  91. if attr[-6:] == '_array':
  92. #print 'asarraycnt'
  93. v = [ x.asobj() for x in
  94. self.getarray(attr[:-6]) ]
  95. elif isinstance(obj, Structure):
  96. #print 'asstruct'
  97. v = obj.asobj()
  98. elif isinstance(obj, Array) and obj._type_ != c_char:
  99. #print 'asarray'
  100. v = obj[:]
  101. else:
  102. #print 'asobj'
  103. v = obj
  104. r[attr] = v
  105. return r
  106. def __repr__(self, fields=None):
  107. cls = self.__class__
  108. if fields is None:
  109. fields = self._fields_
  110. return '<%s.%s: %s>' % (cls.__module__, cls.__name__,
  111. ', '.join([ '%s: %s' % (x[0], `getattr(self, x[0])`)
  112. for x in fields ]))
  113. class FLAC__StreamMetadata_StreamInfo(Structure):
  114. _fields_ = [ ('min_blocksize', c_uint),
  115. ('max_blocksize', c_uint),
  116. ('min_framesize', c_uint),
  117. ('max_framesize', c_uint),
  118. ('sample_rate', c_uint),
  119. ('channels', c_uint),
  120. ('bits_per_sample', c_uint),
  121. ('total_samples', c_uint64),
  122. ('md5sum', c_ubyte * 16),
  123. ]
  124. class FLAC__StreamMetadata_VorbisComment_Entry(Structure):
  125. _fields_ = [ ('length', c_uint32),
  126. ('entry', POINTER(c_char)), # string bounded by length
  127. ]
  128. def asstring(self):
  129. return self.entry[:self.length].decode('utf-8')
  130. def __repr__(self):
  131. return '<FLAC__StreamMetadata_VorbisComment_Entry: %s>' % \
  132. `self.asstring()`
  133. def makestrrange(a, b):
  134. '''Make a string w/ characters from a through b (inclusive).'''
  135. return ''.join(chr(x) for x in xrange(a, b + 1))
  136. class VorbisComments(UserDict.DictMixin):
  137. def __init__(self, vc=()):
  138. d = self._dict = {}
  139. for i in vc:
  140. k, v = i.split('=', 1)
  141. try:
  142. self[k].append(v)
  143. except KeyError:
  144. d[self.makevalidkey(k)] = [ v ]
  145. transtable = string.maketrans(makestrrange(0x41, 0x5a),
  146. makestrrange(0x61, 0x7a))
  147. delchars = makestrrange(0, 0x1f) + '=' + makestrrange(0x7e, 0x7f)
  148. @staticmethod
  149. def makevalidkey(k):
  150. k = str(k)
  151. origlen = len(k)
  152. k = k.translate(VorbisComments.transtable,
  153. VorbisComments.delchars)
  154. if len(k) != origlen:
  155. raise ValueError('Invalid key')
  156. return k
  157. def __getitem__(self, k):
  158. return self._dict[self.makevalidkey(k)]
  159. def __setitem__(self, k, v):
  160. # XXX - allow? check v?
  161. return self._dict.__setitem__(self.makevalidkey(k), v)
  162. def __delitem__(self, k):
  163. return self._dict.__delitem__(self.makevalidkey(k))
  164. def keys(self):
  165. return self._dict.keys()
  166. class FLAC__StreamMetadata_VorbisComment(Structure):
  167. _fields_ = [ ('vendor_string',
  168. FLAC__StreamMetadata_VorbisComment_Entry),
  169. ('num_comments', c_uint32),
  170. ('comments', POINTER(FLAC__StreamMetadata_VorbisComment_Entry)),
  171. ]
  172. _material_ = (('vendor_string', ), ('comments_array', ))
  173. def asobj(self):
  174. return self.vendor_string.asstring(), \
  175. VorbisComments(x.asstring() for x in
  176. self.getarray('comments'))
  177. def __repr__(self):
  178. return Structure.__repr__(self, self._material_)
  179. class FLAC__StreamMetadata_CueSheet_Index(Structure):
  180. _fields_ = [ ('offset', c_uint64),
  181. ('number', c_ubyte),
  182. ]
  183. class FLAC__StreamMetadata_CueSheet_Track(Structure):
  184. _fields_ = [ ('offset', c_uint64),
  185. ('number', c_ubyte),
  186. ('isrc', c_char * 13), # string + NUL
  187. ('type', c_ubyte, 1),
  188. ('pre_emphasis', c_ubyte, 1),
  189. ('num_indices', c_ubyte),
  190. ('indices', POINTER(FLAC__StreamMetadata_CueSheet_Index)),
  191. ]
  192. _material_ = (('offset', ), ('number', ), ('isrc', ), ('type', ),
  193. ('pre_emphasis', ), ('indices_array', ))
  194. def __repr__(self):
  195. return Structure.__repr__(self, self._material_)
  196. class FLAC__StreamMetadata_CueSheet(Structure):
  197. _fields_ = [ ('media_catalog_number', c_char * 129), # string + nul
  198. ('lead_in', c_uint64),
  199. ('is_cd', c_int),
  200. ('num_tracks', c_uint),
  201. ('tracks', POINTER(FLAC__StreamMetadata_CueSheet_Track)),
  202. ]
  203. _material_ = (('media_catalog_number', ), ('lead_in', ), ('is_cd', ),
  204. ('tracks_array', ))
  205. def __repr__(self):
  206. return Structure.__repr__(self, self._material_)
  207. class FLAC__StreamMetadataData(Union):
  208. _fields_ = [ ('stream_info', FLAC__StreamMetadata_StreamInfo),
  209. ('vorbis_comment', FLAC__StreamMetadata_VorbisComment),
  210. ('cue_sheet', FLAC__StreamMetadata_CueSheet),
  211. ]
  212. class FLAC__StreamMetadata(Structure):
  213. _fields_ = [ ('type', c_int),
  214. ('is_last', c_int),
  215. ('length', c_uint),
  216. ('data', FLAC__StreamMetadataData),
  217. ]
  218. class FLAC__EntropyCodingMethod_PartitionedRiceContents(Structure):
  219. pass
  220. class FLAC__EntropyCodingMethod_PartitionedRice(Structure):
  221. _fields_ = [ ('order', c_uint),
  222. ('contents',
  223. POINTER(FLAC__EntropyCodingMethod_PartitionedRiceContents)),
  224. ]
  225. class FLAC__EntropyCodingMethod(Structure):
  226. _fields_ = [ ('type', c_int),
  227. ('partitioned_rice', FLAC__EntropyCodingMethod_PartitionedRice),
  228. ]
  229. class FLAC__Subframe_Constant(Structure):
  230. _fields_ = [ ('value', c_int32), ]
  231. class FLAC__Subframe_Verbatim(Structure):
  232. _fields_ = [ ('data', POINTER(c_int32)), ]
  233. class FLAC__Subframe_Fixed(Structure):
  234. _fields_ = [ ('entropy_coding_method', FLAC__EntropyCodingMethod),
  235. ('order', c_uint),
  236. ('warmup', c_int32 * FLAC__MAX_FIXED_ORDER),
  237. ('residual', POINTER(c_int32)),
  238. ]
  239. class FLAC__Subframe_LPC(Structure):
  240. _fields_ = [ ('entropy_coding_method', FLAC__EntropyCodingMethod),
  241. ('order', c_uint),
  242. ('qlp_coeff_precision', c_uint),
  243. ('quantization_level', c_int),
  244. ('qlp_coeff', c_int32 * FLAC__MAX_LPC_ORDER),
  245. ('warmup', c_int32 * FLAC__MAX_LPC_ORDER),
  246. ('residual', POINTER(c_int32)),
  247. ]
  248. class FLAC__SubframeUnion(Union):
  249. _fields_ = [ ('constant', FLAC__Subframe_Constant),
  250. ('fixed', FLAC__Subframe_Fixed),
  251. ('lpc', FLAC__Subframe_LPC),
  252. ('verbatim', FLAC__Subframe_Verbatim),
  253. ]
  254. class FLAC__Subframe(Structure):
  255. _fields_ = [ ('type', c_int),
  256. ('data', FLAC__SubframeUnion),
  257. ('wasted_bits', c_uint),
  258. ]
  259. class number_union(Union):
  260. _fields_ = [ ('frame_number', c_uint32),
  261. ('sample_number', c_uint64),
  262. ]
  263. class FLAC__FrameHeader(Structure):
  264. _fields_ = [ ('blocksize', c_uint),
  265. ('sample_rate', c_uint),
  266. ('channels', c_uint),
  267. ('channel_assignment', c_int),
  268. ('bits_per_sample', c_uint),
  269. ('number_type', c_int),
  270. ('number', number_union),
  271. ('crc', c_uint8),
  272. ]
  273. class FLAC__FrameFooter(Structure):
  274. _fields_ = [ ('crc', c_uint16), ]
  275. class FLAC__Frame(Structure):
  276. _fields_ = [ ('header', FLAC__FrameHeader),
  277. ('subframes', FLAC__Subframe * FLAC__MAX_CHANNELS),
  278. ('footer', FLAC__FrameFooter),
  279. ]
  280. class FLAC__StreamDecoder(Structure):
  281. pass
  282. # Types
  283. FLAC__StreamMetadata_p = POINTER(FLAC__StreamMetadata)
  284. FLAC__Frame_p = POINTER(FLAC__Frame)
  285. FLAC__StreamDecoder_p = POINTER(FLAC__StreamDecoder)
  286. # Function typedefs
  287. FLAC__StreamDecoderReadCallback = CFUNCTYPE(c_int, FLAC__StreamDecoder_p,
  288. POINTER(c_ubyte), POINTER(c_size_t), c_void_p)
  289. FLAC__StreamDecoderSeekCallback = CFUNCTYPE(c_int, FLAC__StreamDecoder_p,
  290. c_uint64, c_void_p)
  291. FLAC__StreamDecoderTellCallback = CFUNCTYPE(c_int, FLAC__StreamDecoder_p,
  292. POINTER(c_uint64), c_void_p)
  293. FLAC__StreamDecoderLengthCallback = CFUNCTYPE(c_int, FLAC__StreamDecoder_p,
  294. POINTER(c_uint64), c_void_p)
  295. FLAC__StreamDecoderEofCallback = CFUNCTYPE(c_int, FLAC__StreamDecoder_p,
  296. c_void_p)
  297. FLAC__StreamDecoderWriteCallback = CFUNCTYPE(c_int, FLAC__StreamDecoder_p,
  298. FLAC__Frame_p, POINTER(POINTER(c_int32)), c_void_p)
  299. FLAC__StreamDecoderMetadataCallback = CFUNCTYPE(None, FLAC__StreamDecoder_p,
  300. FLAC__StreamMetadata_p, c_void_p)
  301. FLAC__StreamDecoderErrorCallback = CFUNCTYPE(None, FLAC__StreamDecoder_p,
  302. c_int, c_void_p)
  303. funs = {
  304. 'FLAC__stream_decoder_new': (FLAC__StreamDecoder_p, []),
  305. 'FLAC__stream_decoder_delete': (None, [FLAC__StreamDecoder_p, ]),
  306. 'FLAC__stream_decoder_set_md5_checking': (c_int,
  307. [ FLAC__StreamDecoder_p, c_int, ]),
  308. 'FLAC__stream_decoder_set_metadata_respond': (c_int,
  309. [ FLAC__StreamDecoder_p, c_int, ]),
  310. 'FLAC__stream_decoder_set_metadata_respond_all': (c_int,
  311. [ FLAC__StreamDecoder_p, ]),
  312. 'FLAC__stream_decoder_get_state': (c_int,
  313. [ FLAC__StreamDecoder_p, ]),
  314. 'FLAC__stream_decoder_get_total_samples': (c_uint64,
  315. [ FLAC__StreamDecoder_p, ]),
  316. 'FLAC__stream_decoder_get_channels': (c_uint,
  317. [ FLAC__StreamDecoder_p, ]),
  318. 'FLAC__stream_decoder_get_channel_assignment': (c_int,
  319. [ FLAC__StreamDecoder_p, ]),
  320. 'FLAC__stream_decoder_get_bits_per_sample': (c_uint,
  321. [ FLAC__StreamDecoder_p, ]),
  322. 'FLAC__stream_decoder_get_sample_rate': (c_uint,
  323. [ FLAC__StreamDecoder_p, ]),
  324. 'FLAC__stream_decoder_get_blocksize': (c_uint,
  325. [ FLAC__StreamDecoder_p, ]),
  326. 'FLAC__stream_decoder_init_stream': (c_int, [ FLAC__StreamDecoder_p,
  327. FLAC__StreamDecoderReadCallback, FLAC__StreamDecoderSeekCallback,
  328. FLAC__StreamDecoderTellCallback, FLAC__StreamDecoderLengthCallback,
  329. FLAC__StreamDecoderEofCallback, FLAC__StreamDecoderWriteCallback,
  330. FLAC__StreamDecoderMetadataCallback,
  331. FLAC__StreamDecoderErrorCallback, ]),
  332. 'FLAC__stream_decoder_init_file': (c_int, [ FLAC__StreamDecoder_p,
  333. c_char_p, FLAC__StreamDecoderWriteCallback,
  334. FLAC__StreamDecoderMetadataCallback,
  335. FLAC__StreamDecoderErrorCallback, c_void_p, ]),
  336. 'FLAC__stream_decoder_finish': (c_int, [ FLAC__StreamDecoder_p, ]),
  337. 'FLAC__stream_decoder_flush': (c_int, [ FLAC__StreamDecoder_p, ]),
  338. 'FLAC__stream_decoder_reset': (c_int, [ FLAC__StreamDecoder_p, ]),
  339. 'FLAC__stream_decoder_process_single': (c_int,
  340. [ FLAC__StreamDecoder_p, ]),
  341. 'FLAC__stream_decoder_process_until_end_of_metadata': (c_int,
  342. [ FLAC__StreamDecoder_p, ]),
  343. 'FLAC__stream_decoder_process_until_end_of_stream': (c_int,
  344. [ FLAC__StreamDecoder_p, ]),
  345. 'FLAC__stream_decoder_seek_absolute': (c_int,
  346. [ FLAC__StreamDecoder_p, c_uint64, ]),
  347. }
  348. for i in funs:
  349. f = getattr(flaclib, i)
  350. f.restype, f.argtypes = funs[i]
  351. def clientdatawrapper(fun):
  352. def newfun(*args):
  353. #print 'nf:', `args`
  354. return fun(*args[1:-1])
  355. return newfun
  356. def getindex(ar, idx):
  357. for i in ar:
  358. if idx == i['number']:
  359. return i
  360. raise ValueError('index %d not present: %s' % (idx, `ar`))
  361. def _checksum(n):
  362. ret = 0
  363. while n > 0:
  364. n, m = divmod(n, 10)
  365. ret += m
  366. return ret
  367. def _cuetotrackinfo(cuesheet):
  368. '''XXX - do not use. This will not work because the cuesheet does
  369. not include data tracks!'''
  370. if not cuesheet['is_cd']:
  371. raise ValueError('cuesheet isn\'t for a cd')
  372. tracks = []
  373. ta = cuesheet['tracks_array']
  374. tot = 0
  375. cksum = 0
  376. for i in xrange(1, len(ta)):
  377. offstart = ta[i - 1]['offset']
  378. offend = ta[i]['offset']
  379. secs = offend / 44100 - offstart / 44100
  380. #print ta[i - 1]['number'], secs, offstart / (2352 / 4), (offend - offstart) / (2352 / 4)
  381. tot += secs
  382. cksum += _checksum(offstart / 44100 + 2)
  383. #print tot
  384. tracks.append(divmod(tot, 60))
  385. #print `tracks`
  386. return (long(cksum) % 0xff) << 24 | tot << 8 | (len(ta) - 1), 0
  387. class FLACDec(object):
  388. '''Class to support flac decoding.'''
  389. channels = property(lambda x: x._channels)
  390. samplerate = property(lambda x: x._samplerate)
  391. bitspersample = property(lambda x: x._bitspersample)
  392. totalsamples = property(lambda x: x._totalsamples)
  393. bytespersample = property(lambda x: x._bytespersample)
  394. vorbis = property(lambda x: x._vorbis)
  395. cuesheet = property(lambda x: x._cuesheet)
  396. def __len__(self):
  397. return self._total_samps
  398. def __init__(self, file):
  399. '''Pass in the file name. We currently do not support file objects.'''
  400. self.flacdec = None
  401. self._lasterror = None
  402. # We need to keep references to the callback functions
  403. # around so they won't be garbage collected.
  404. self.write_wrap = FLAC__StreamDecoderWriteCallback(
  405. clientdatawrapper(self._cb_write))
  406. self.metadata_wrap = FLAC__StreamDecoderMetadataCallback(
  407. clientdatawrapper(self._cb_metadata))
  408. self.error_wrap = FLAC__StreamDecoderErrorCallback(
  409. clientdatawrapper(self._cb_error))
  410. self.flacdec = flaclib.FLAC__stream_decoder_new()
  411. if self.flacdec == 0:
  412. raise RuntimeError('allocating decoded')
  413. flaclib.FLAC__stream_decoder_set_md5_checking(self.flacdec,
  414. True)
  415. flaclib.FLAC__stream_decoder_set_metadata_respond(self.flacdec,
  416. FLAC__METADATA_TYPE_VORBIS_COMMENT)
  417. flaclib.FLAC__stream_decoder_set_metadata_respond(self.flacdec,
  418. FLAC__METADATA_TYPE_CUESHEET)
  419. status = flaclib.FLAC__stream_decoder_init_file(self.flacdec,
  420. file, self.write_wrap, self.metadata_wrap,
  421. self.error_wrap, None)
  422. if status != FLAC__STREAM_DECODER_INIT_STATUS_OK:
  423. raise ValueError(
  424. FLAC__StreamDecoderInitStatusString[status])
  425. self._vorbis = None
  426. self._cuesheet = None
  427. flaclib.FLAC__stream_decoder_process_until_end_of_metadata(
  428. self.flacdec)
  429. if self._lasterror is not None:
  430. raise ValueError(
  431. FLAC__StreamDecoderErrorStatusString[
  432. self._lasterror])
  433. self.curcnt = 0
  434. self.cursamp = 0
  435. if self.vorbis is None:
  436. self.vorbis = '', VorbisComments()
  437. def close(self):
  438. '''Finish decoding and close all the objects.'''
  439. if self.flacdec is None:
  440. return
  441. #print 'close called'
  442. if not flaclib.FLAC__stream_decoder_finish(self.flacdec):
  443. md5invalid = True
  444. else:
  445. md5invalid = False
  446. flaclib.FLAC__stream_decoder_delete(self.flacdec)
  447. self.flacdec = None
  448. self.write_wrap = None
  449. self.metadata_wrap = None
  450. self.error_wrap = None
  451. if md5invalid:
  452. pass
  453. #raise ValueError('invalid md5')
  454. def _cb_write(self, frame_p, buffer_pp):
  455. frame = frame_p[0]
  456. #print 'write:', `frame`
  457. #for i in xrange(frame.header.channels):
  458. # print '%d:' % i, `frame.subframes[i]`
  459. nchan = frame.header.channels
  460. #print 'sample number:', frame.header.number.sample_number
  461. self.cursamp = frame.header.number.sample_number
  462. self.curcnt = frame.header.blocksize
  463. outtype, fun = inter[frame.header.bits_per_sample]
  464. if outtype is None:
  465. outbuf = None
  466. else:
  467. outbuf = (outtype * (nchan * frame.header.blocksize))()
  468. # nchan, chanmap, chansamps, data, out
  469. assert nchan == 2
  470. r = fun(nchan, (c_int * 2)(0, 1), frame.header.blocksize,
  471. buffer_pp, outbuf)
  472. if outtype is None:
  473. self.curdata = r
  474. else:
  475. self.curdata = array.array('h', outbuf)
  476. if is_little_endian:
  477. self.curdata.byteswap()
  478. return 0
  479. def _cb_metadata(self, metadata_p):
  480. md = metadata_p[0]
  481. #print 'metadata:', `md`
  482. if md.type == FLAC__METADATA_TYPE_STREAMINFO:
  483. si = md.data.stream_info
  484. self._channels = si.channels
  485. self._samplerate = si.sample_rate
  486. self._bitspersample = si.bits_per_sample
  487. self._totalsamples = si.total_samples
  488. self._bytespersample = si.channels * \
  489. si.bits_per_sample / 8
  490. #print `si`
  491. elif md.type == FLAC__METADATA_TYPE_VORBIS_COMMENT:
  492. self._vorbis = md.data.vorbis_comment.asobj()
  493. #print 'vc:', `md.data.vorbis_comment`
  494. #print 'v:', `self.vorbis`
  495. elif md.type == FLAC__METADATA_TYPE_CUESHEET:
  496. self._cuesheet = md.data.cue_sheet.asobj()
  497. #print 'cs:', `md.data.cue_sheet`
  498. #print 'c:', `self.cuesheet`
  499. else:
  500. print 'unknown metatype:', md.type
  501. def _cb_error(self, errstatus):
  502. #print 'error:', `errstatus`
  503. self._lasterror = errstatus
  504. def getstate(self):
  505. state = flaclib.FLAC__stream_decoder_get_state(self.flacdec)
  506. return state
  507. state = property(getstate)
  508. def goto(self, pos):
  509. '''Go to sample possition.'''
  510. if self.flacdec is None:
  511. raise ValueError('closed')
  512. pos = min(self.totalsamples - 1, pos)
  513. if flaclib.FLAC__stream_decoder_seek_absolute(self.flacdec,
  514. pos):
  515. return
  516. # the slow way
  517. state = self.state
  518. if state == FLAC__STREAM_DECODER_SEEK_ERROR:
  519. print 'WARNING: possibly invalid file!'
  520. if self.cursamp > pos or \
  521. state == FLAC__STREAM_DECODER_SEEK_ERROR:
  522. if not flaclib.FLAC__stream_decoder_reset(self.flacdec):
  523. raise RuntimeError('unable to seek to beginin')
  524. flaclib.FLAC__stream_decoder_process_until_end_of_metadata(self.flacdec)
  525. flaclib.FLAC__stream_decoder_process_single(
  526. self.flacdec)
  527. read = pos - self.cursamp
  528. while read:
  529. tread = min(read, 512*1024)
  530. self.read(tread)
  531. read -= tread
  532. def read(self, nsamp=16*1024, oneblk=False):
  533. '''If oneblk is True, we will only process data once.'''
  534. if self.flacdec is None:
  535. raise ValueError('closed')
  536. r = []
  537. nsamp = min(nsamp, self.totalsamples - self.cursamp)
  538. nchan = self.channels
  539. while nsamp:
  540. if self.curcnt == 0:
  541. flaclib.FLAC__stream_decoder_process_single(
  542. self.flacdec)
  543. continue
  544. cnt = min(nsamp, self.curcnt)
  545. sampcnt = cnt * nchan
  546. r.append(self.curdata[:sampcnt].tostring())
  547. self.cursamp += cnt
  548. self.curcnt -= cnt
  549. self.curdata = self.curdata[sampcnt:]
  550. nsamp -= cnt
  551. if oneblk:
  552. break
  553. return ''.join(r)
  554. def doprofile(d):
  555. def readtoend():
  556. while d.read():
  557. pass
  558. import hotshot.stats
  559. prof = hotshot.Profile('decread.prof')
  560. prof.runcall(readtoend)
  561. prof.close()
  562. stats = hotshot.stats.load('decread.prof')
  563. stats.strip_dirs()
  564. stats.sort_stats('time', 'calls')
  565. stats.print_stats(20)
  566. if __name__ == '__main__':
  567. import sys
  568. if len(sys.argv) == 1:
  569. print 'Usage: %s <file>' % sys.argv[0]
  570. sys.exit(1)
  571. d = FLACDec(sys.argv[1])
  572. print 'total samples:', d.totalsamples
  573. print 'channels:', d.channels
  574. print 'rate:', d.samplerate
  575. print 'bps:', d.bitspersample
  576. print `d.read(10)`
  577. print 'going'
  578. d.goto(d.totalsamples - 1000*1000)
  579. print 'here'
  580. print `d.read(10)`
  581. doprofile(d)