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.

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