Theme changes, fix for unicode conversion errors, misc
[rox-musicbox.git] / plugins / _flac.py
blob67fc41203d4c61e9d57ad39966a44c80d3dcbbc5
1 """
3 Copyright 2005 Kenneth Hayber <ken@hayber.us>, All rights reserved.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License.
9 This program is distributed in the hope that it will be useful
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 """
19 try:
20 import flac
21 import flac.metadata as metadata
22 import flac.decoder as fldecoder
23 HAVE_FLAC = True
24 except:
25 HAVE_FLAC = False
26 print "No FLAC support!"
28 def get_info(song):
29 #TODO
30 pass
32 class FLACDecoder:
33 """
34 A decoder to process FLAC sound files. Utilizes the standard pyflac module
35 Called from the Player class to read and decode FLAC files.
36 """
37 def __init__(self, filename, buffersize):
38 """Initialize the decoder"""
39 self.buffersize = buffersize
40 self.filename = filename
41 self.last_decode = 0
43 chain = metadata.Chain()
44 chain.read(filename)
45 it = metadata.Iterator()
46 it.init(chain)
47 while 1:
48 block = it.get_block()
49 if block.type == metadata.STREAMINFO:
50 streaminfo = block.data.stream_info
51 self.total_samples = streaminfo.total_samples
52 self.sample_rate = streaminfo.sample_rate
53 self.nchannels = streaminfo.channels
55 if not it.next():
56 break
58 def open(self):
59 """Open the file and prepare for decoding"""
61 def metadata_callback(dec, block): pass
62 def error_callback(dec, status): pass
63 def write_callback(dec, buff, size):
64 if self.buff == '':
65 self.buff = buff[:-1]
66 else:
67 self.buff += buff[:-1]
68 return fldecoder.FLAC__FILE_DECODER_OK
70 # create a new file decoder
71 mydec = fldecoder.FileDecoder()
73 # set some properties
74 mydec.set_md5_checking(False);
75 mydec.set_filename(self.filename)
76 mydec.set_metadata_respond_all()
78 # set the callbacks
79 mydec.set_write_callback(write_callback)
80 mydec.set_error_callback(error_callback)
81 mydec.set_metadata_callback(metadata_callback)
83 # initialise, process metadata
84 mydec.init()
85 mydec.process_until_end_of_metadata()
87 self.mydec = mydec
89 def close(self):
90 """Close the file and do any needed cleanup"""
91 self.mydec.finish()
93 def length(self):
94 """Return the length of the file in seconds as an integer"""
95 return self.total_samples / self.sample_rate
97 def samplerate(self):
98 """Return the sample rate of the file in samples per second"""
99 return self.sample_rate
101 def channels(self):
102 """Return the number of channels in the file"""
103 return self.nchannels
105 def read(self):
107 Read data from the file and decode to PCM data. Return a buffer
108 of data and length, or (None, 0) at EOF
110 self.buff = ''
111 self.mydec.process_single()
112 bytes = len(self.buff)
113 if bytes:
114 return (self.buff, bytes)
115 else:
116 return (None, 0)
118 def tell(self):
119 """Return the current playback position in seconds"""
120 curr_decode = self.mydec.get_decode_position()
121 return int(curr_decode / (self.sample_rate * self.channels()))
123 def seek(self, pos):
124 """Jump to pos as a percentage of the total length of the file"""
125 pass #TODO
127 def info(self):
128 """Display some FLAC information"""
129 # create a chain
130 chain = metadata.Chain()
131 chain.read(self.filename)
133 # get iterator, initialise
134 it = metadata.Iterator()
135 it.init(chain)
137 cur_block = 0;
138 while 1:
139 block = it.get_block()
140 # print some common fields
141 print 'METADATA BLOCK #%d' % cur_block
142 print ' type: %d (%s)' % (block.type, metadata.TypeString(block.type))
143 print ' is_last: %d' % block.is_last
144 print ' length: %d' % block.length
145 cur_block += 1
147 if block.type == metadata.STREAMINFO:
148 # print STREAMINFO fields
149 streaminfo = block.data.stream_info
150 print ' minimum blocksize: %d' % streaminfo.min_blocksize
151 print ' maximum blocksize: %d' % streaminfo.max_blocksize
152 print ' minimum framesize: %d' % streaminfo.min_framesize
153 print ' maximum framesize: %d' % streaminfo.max_framesize
154 print ' sample_rate: %d' % streaminfo.sample_rate
155 print ' channels: %d' % streaminfo.channels
156 print ' bits-per-sample: %d' % streaminfo.bits_per_sample
157 print ' total samples: %d' % streaminfo.total_samples
158 #print ' md5sum: %s' % streaminfo.md5sum
160 elif block.type == metadata.SEEKTABLE:
161 # print SEEKTABLE fields
162 seektable = block.data.seek_table
163 print ' seek points: %d' % seektable.num_points
164 for i in range(seektable.num_points):
165 pt = seektable.points[i]
166 print ' point %d: sample_number=%d, stream_offset=%d, frame_samples=%d' % (i, pt.sample_number, pt.stream_offset, pt.frame_samples)
168 elif block.type == metadata.CUESHEET:
169 # print CUESHEET
170 cuesheet = block.data.cue_sheet
171 print ' media catalog number: %s' % cuesheet.media_catalog_number
172 print ' lead-in: %d' % cuesheet.lead_in
173 print ' is CD: %d' % cuesheet.is_cd
174 print ' number of tracks: %d' % cuesheet.num_tracks
175 for i in range(cuesheet.num_tracks):
176 tr = cuesheet.tracks[i]
177 print ' track[%d]' % i
178 print ' offset: %d' % tr.offset
179 print ' number: %d' % ord(tr.number)
180 print ' ISRC: %s' % tr.isrc
181 if tr.type == 0:
182 print ' type: AUDIO'
183 else:
184 print ' type: NON-AUDIO'
185 if tr.pre_emphasis == 1:
186 print ' pre-emphasis: true'
187 else:
188 print ' pre-emphasis: false'
189 print ' number of index points: %d' % ord(tr.num_indices)
190 for j in range(ord(tr.num_indices)):
191 print ' index[%d]' % j
192 print ' offset: %d' % tr.indices[j].offset
193 print ' number: %d' % ord(tr.indices[j].number)
195 elif block.type == metadata.VORBIS_COMMENT:
196 # print vorbis tags
197 comment = block.data.vorbis_comment
198 print ' vendor string: %s' % comment.vendor_string
199 print ' comments: %d' % comment.num_comments
200 for i in range(comment.num_comments):
201 print ' comment[%d]: %s' % (i, comment.comments[i])
203 if not it.next():
204 break