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.

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