2 player.py (play either ogg or mp3 files)
4 based on ogg123.py By Andrew Chatham Based on ogg123.c by Keneth Arnold.
5 also based on mpg123.py from the pymad module (no attribution in those sources)
7 Portions, Copyright 2004 Kenneth Hayber <khayber@socal.rr.com>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License.
14 This program is distributed in the hope that it will be useful
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 import os
, time
, string
, sys
, Queue
31 print 'No OGG support!'
38 print 'No MP3 support!'
44 print 'No OSS support!!'
53 if not HAVE_AO
and not HAVE_OSS
:
54 print 'No OSS and no AO support Falling back to linuxaudiodev which sucks!!'
58 TYPE_OGG
= 'application/ogg'
59 TYPE_MP3
= 'audio/x-mp3'
60 TYPE_LIST
= [TYPE_OGG
, TYPE_MP3
]
65 A class to playback MP3 and OGG sound files to an audio device.
66 Supports/depends on libmad, libvorbis, libogg, libao, pyao,
67 linuxaudiodev and/or ossaudiodev
76 def __init__(self
, id=None, buffersize
=4096):
77 """Initialize the Player instance"""
80 self
.id = ao
.driver_id('esd') #also can be 'oss', 'alsa', 'alsa09', etc.
84 self
.buffersize
= buffersize
86 self
.queue
= Queue
.Queue(5)
88 def open(self
, rate
=44100, channels
=2):
89 """Open and configure the audio device driver"""
91 #try 3 times to open the device, then give up.
95 self
.dev
= ao
.AudioDevice(self
.id, bits
, rate
, channels
)
97 self
.dev
= ossaudiodev
.open('w')
98 self
.dev
.setparameters(ossaudiodev
.AFMT_S16_LE
, channels
, rate
)
100 self
.dev
= linuxaudiodev
.open('w')
101 self
.dev
.setparameters(rate
, bits
, channels
, linuxaudiodev
.AFMT_S16_NE
)
107 """Close the current device if open and delete it"""
117 def write(self
, buff
, bytes
):
118 """Write data to the audio device"""
120 self
.dev
.play(buff
, bytes
)
124 while self
.dev
.obuffree() < bytes
:
126 self
.dev
.write(buff
[:bytes
])
128 def play(self
, name
, type):
129 """Push the song info on the queue"""
130 if (type == TYPE_OGG
and not HAVE_OGG
):
131 raise TypeError, _('You must have OGG support to play ogg files (%s).') % name
132 if (type == TYPE_MP3
and not HAVE_MAD
):
133 raise TypeError, _('You must have MAD support to play mp3 files (%s).') % name
134 self
.queue
.put((name
, type))
137 """Check the filename and type and start the appropriate play-loop"""
139 (name
, type) = self
.queue
.get()
140 if os
.path
.isfile(name
):
141 if (type == TYPE_OGG
and HAVE_OGG
):
142 vf
= ogg
.vorbis
.VorbisFile(name
)
146 elif (type == TYPE_MP3
and HAVE_MAD
):
147 mf
= mad
.MadFile(name
, self
.buffersize
)
152 raise ValueError, 'Unsupported file (%s).' % name
154 raise SyntaxError, 'play takes a filename.'
157 """Set a flag telling the current play-loop to exit and close the device"""
160 try: self
.queue
.get_nowait()
161 except Queue
.Empty
: break
164 """Pause playback (works as a toggle between play and pause)"""
165 if self
.state
== 'play':
167 elif self
.state
== 'pause':
170 def seek(self
, percent
):
171 """Jump to a specific point in the song by percent"""
172 self
.seek_val
= percent
174 def set_volume(self
, volume
):
175 """Set the PCM volume to a new value"""
178 mixer
= ossaudiodev
.openmixer()
180 mixer
.set(ossaudiodev
.SOUND_MIXER_PCM
, (vol
, vol
))
184 def get_volume(self
):
185 """Return the current volume setting"""
187 mixer
= ossaudiodev
.openmixer()
189 vol
= mixer
.get(ossaudiodev
.SOUND_MIXER_PCM
)
190 return float(max(vol
[0], vol
[1]))
195 #############################
197 #############################
198 def info_ogg(self
, vf
):
199 """Display some OGG information"""
200 print vf
.comment().as_dict()
202 def start_ogg(self
, vf
):
203 """Open the audio device and start playing an OGG file"""
206 self
.total_time
= int(vf
.time_total(0))
207 self
.remain
= self
.total_time
213 self
.open(vi
.rate
, vi
.channels
)
215 while self
.state
== 'play' or self
.state
== 'pause':
216 if self
.state
== 'pause':
219 vf
.time_seek(float(self
.total_time
* self
.seek_val
))
222 #for some reason with ossaudiodev a buffer greater
223 #than 512 bytes causes problems with ogg, smaller seems better
224 # but I'm afraid performance will suck.
225 if not HAVE_AO
and HAVE_OSS
:
226 self
.buffersize
= 256
227 (buff
, bytes
, bit
) = vf
.read(self
.buffersize
)
230 self
.elapse
= self
.total_time
234 self
.elapse
= int(vf
.time_tell())
235 self
.remain
= max(0, self
.total_time
- self
.elapse
)
236 self
.write(buff
, bytes
)
237 if self
.elapse
!= last_elapse
or self
.state
== 'pause':
238 last_elapse
= self
.elapse
245 #############################
246 # MAD/MP3-specific stuff
247 #############################
248 def info_mad(self
, mf
):
249 """Display some MP3 information"""
250 if mf
.layer() == mad
.LAYER_I
:
252 elif mf
.layer() == mad
.LAYER_II
:
253 print "MPEG Layer II"
254 elif mf
.layer() == mad
.LAYER_III
:
255 print "MPEG Layer III"
257 print "unexpected layer value"
259 if mf
.mode() == mad
.MODE_SINGLE_CHANNEL
:
260 print "single channel"
261 elif mf
.mode() == mad
.MODE_DUAL_CHANNEL
:
263 elif mf
.mode() == mad
.MODE_JOINT_STEREO
:
264 print "joint (MS/intensity) stereo"
265 elif mf
.mode() == mad
.MODE_STEREO
:
266 print "normal L/R stereo"
268 print "unexpected mode value"
270 if mf
.emphasis() == mad
.EMPHASIS_NONE
:
272 elif mf
.emphasis() == mad
.EMPHASIS_50_15_US
:
273 print "50/15us emphasis"
274 elif mf
.emphasis() == mad
.EMPHASIS_CCITT_J_17
:
275 print "CCITT J.17 emphasis"
277 print "unexpected emphasis value"
279 print "bitrate %lu bps" % mf
.bitrate()
280 print "samplerate %d Hz" % mf
.samplerate()
281 millis
= mf
.total_time()
283 print "total time %d ms (%dm%2ds)" % (millis
, secs
/ 60, secs
% 60)
285 def start_mad(self
, mf
):
286 """Open the audio device and start playing an MP3 file"""
289 self
.total_time
= mf
.total_time()/1000
290 self
.remain
= self
.total_time
294 if mf
.mode() == mad
.MODE_SINGLE_CHANNEL
:
300 self
.open(mf
.samplerate(), channels
)
302 while self
.state
== 'play' or self
.state
== 'pause':
303 if self
.state
== 'pause':
306 mf
.seek_time(long(self
.total_time
* self
.seek_val
* 1000))
312 self
.elapse
= self
.total_time
316 self
.elapse
= mf
.current_time() / 1000
317 self
.remain
= max(0, self
.total_time
- self
.elapse
)
318 self
.write(buff
, len(buff
))
319 if self
.elapse
!= last_elapse
or self
.state
== 'pause':
320 last_elapse
= self
.elapse