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.

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