diff --git a/flac.py b/flac.py index 8cee1b9..2c71f9a 100644 --- a/flac.py +++ b/flac.py @@ -22,7 +22,11 @@ is_little_endian = _islittleendian() inter = {} try: - interleavelib = CDLL(os.path.join(os.path.dirname(__file__), '_interleave.so')) + dname = os.path.dirname(__file__) + if not dname: + dname = '.' + path = os.path.join(dname, '_interleave.so') + interleavelib = CDLL(path) for i in (16, ): f = getattr(interleavelib, 'interleave%d' % i) f.restype = None @@ -56,6 +60,9 @@ FLAC__MAX_CHANNELS = 8 FLAC__MAX_LPC_ORDER = 32 FLAC__MAX_FIXED_ORDER = 4 +FLAC__byte = c_uint8 +FLAC__byte_p = POINTER(FLAC__byte) + # Data FLAC__StreamDecoderStateString = (c_char_p * 10).in_dll(flaclib, 'FLAC__StreamDecoderStateString') @@ -63,6 +70,8 @@ FLAC__StreamDecoderInitStatusString = (c_char_p * 6).in_dll(flaclib, 'FLAC__StreamDecoderInitStatusString') FLAC__StreamDecoderErrorStatusString = (c_char_p * 4).in_dll(flaclib, 'FLAC__StreamDecoderErrorStatusString') +FLAC__StreamMetadata_Picture_TypeString = (c_char_p * 21).in_dll(flaclib, + 'FLAC__StreamMetadata_Picture_TypeString') # Enums FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0 @@ -84,6 +93,33 @@ FLAC__SUBFRAME_TYPE_VERBATIM = 1 FLAC__SUBFRAME_TYPE_FIXED = 2 FLAC__SUBFRAME_TYPE_LPC = 3 +FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0 # < Other */ +FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1 # < 32x32 pixels 'file icon' (PNG only) */ +FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2 # < Other file icon */ +FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3 # < Cover (front) */ +FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4 # < Cover (back) */ +FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5 # < Leaflet page */ +FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6 # < Media (e.g. label side of CD) */ +FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7 # < Lead artist/lead performer/soloist */ +FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8 # < Artist/performer */ +FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9 # < Conductor */ +FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10 # < Band/Orchestra */ +FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11 # < Composer */ +FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12 # < Lyricist/text writer */ +FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13 # < Recording Location */ +FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14 # < During recording */ +FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15 # < During performance */ +FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16 # < Movie/video screen capture */ +FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17 # < A bright coloured fish */ +FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18 # < Illustration */ +FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19 # < Band/artist logotype */ +FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20 # < Publisher/Studio logotype */ + +pictnamemap = dict(enumerate(('other', 'icon', 'icon', 'cover', 'backcover', + 'leaflet', 'media', 'lead', 'artist', 'conductor', 'band', 'composer', + 'lyricist', 'location', 'recording', 'performance', 'video', 'fish', + 'illustration', 'bandlogo', 'publogo'))) + OrigStructure = Structure class Structure(Structure): def getarray(self, base): @@ -128,7 +164,10 @@ class Structure(Structure): def __repr__(self, fields=None): cls = self.__class__ if fields is None: - fields = self._fields_ + if hasattr(self, '_material_'): + fields = self._material_ + else: + fields = self._fields_ return '<%s.%s: %s>' % (cls.__module__, cls.__name__, ', '.join([ '%s: %s' % (x[0], `getattr(self, x[0])`) for x in fields ])) @@ -213,9 +252,6 @@ class FLAC__StreamMetadata_VorbisComment(Structure): VorbisComments(x.asstring() for x in self.getarray('comments')) - def __repr__(self): - return Structure.__repr__(self, self._material_) - class FLAC__StreamMetadata_CueSheet_Index(Structure): _fields_ = [ ('offset', c_uint64), ('number', c_ubyte), @@ -233,9 +269,6 @@ class FLAC__StreamMetadata_CueSheet_Track(Structure): _material_ = (('offset', ), ('number', ), ('isrc', ), ('type', ), ('pre_emphasis', ), ('indices_array', )) - def __repr__(self): - return Structure.__repr__(self, self._material_) - class FLAC__StreamMetadata_CueSheet(Structure): _fields_ = [ ('media_catalog_number', c_char * 129), # string + nul ('lead_in', c_uint64), @@ -246,13 +279,34 @@ class FLAC__StreamMetadata_CueSheet(Structure): _material_ = (('media_catalog_number', ), ('lead_in', ), ('is_cd', ), ('tracks_array', )) - def __repr__(self): - return Structure.__repr__(self, self._material_) +class FLAC__StreamMetadata_Picture(Structure): + _fields_ = [ ('type', c_int), + ('mime_type', c_char_p), + + # description is listed as FLAC__byte_p, but + # documented as NUL terminated UTF-8 string + ('description', c_char_p), + ('width', c_uint32), + ('height', c_uint32), + ('depth', c_uint32), + ('colors', c_uint32), + ('data_length', c_uint32), + ('data', FLAC__byte_p), + ] + + _material_ = (('type', ), ('mime_type', ), ('width', ), ('height', ), ('depth', ), ('colors', )) + + def asobj(self): + return (self.type, self.mime_type, + self.description.decode('utf-8'), self.width, self.height, + self.depth, self.colors, + ''.join(chr(x) for x in self.data[:self.data_length])) class FLAC__StreamMetadataData(Union): _fields_ = [ ('stream_info', FLAC__StreamMetadata_StreamInfo), ('vorbis_comment', FLAC__StreamMetadata_VorbisComment), ('cue_sheet', FLAC__StreamMetadata_CueSheet), + ('picture', FLAC__StreamMetadata_Picture), ] class FLAC__StreamMetadata(Structure): @@ -472,6 +526,7 @@ class FLACDec(object): tags = property(lambda x: x._tags) vorbis = property(lambda x: x._vorbis) cuesheet = property(lambda x: x._cuesheet) + pictures = property(lambda x: x._pictures) def __len__(self): return self._total_samps @@ -497,10 +552,10 @@ class FLACDec(object): flaclib.FLAC__stream_decoder_set_md5_checking(self.flacdec, True) - flaclib.FLAC__stream_decoder_set_metadata_respond(self.flacdec, - FLAC__METADATA_TYPE_VORBIS_COMMENT) - flaclib.FLAC__stream_decoder_set_metadata_respond(self.flacdec, - FLAC__METADATA_TYPE_CUESHEET) + for i in (FLAC__METADATA_TYPE_VORBIS_COMMENT, + FLAC__METADATA_TYPE_CUESHEET, FLAC__METADATA_TYPE_PICTURE): + flaclib.FLAC__stream_decoder_set_metadata_respond( + self.flacdec, i) status = flaclib.FLAC__stream_decoder_init_file(self.flacdec, file, self.write_wrap, self.metadata_wrap, @@ -512,6 +567,7 @@ class FLACDec(object): self._tags = {} self._vorbis = None self._cuesheet = None + self._pictures = {} flaclib.FLAC__stream_decoder_process_until_end_of_metadata( self.flacdec) @@ -595,6 +651,13 @@ class FLACDec(object): self._cuesheet = md.data.cue_sheet.asobj() #print 'cs:', `md.data.cue_sheet` #print 'c:', `self.cuesheet` + elif md.type == FLAC__METADATA_TYPE_PICTURE: + pict = md.data.picture + #print 'p:', `md.data.picture` + #print 'po:', `pict.asobj()` + self._pictures.setdefault(pictnamemap[pict.type], + []).append(pict.asobj()) + #print 'pd:', `self._pictures` else: print 'unknown metatype:', md.type @@ -695,6 +758,7 @@ if __name__ == '__main__': print 'channels:', d.channels print 'rate:', d.samplerate print 'bps:', d.bitspersample + print 'pict types:', d.pictures.keys() print `d.read(10)` print 'going' d.goto(d.totalsamples - 1000*1000)