2 player.py (play various sound/music 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 Copyright 2005 Kenneth Hayber <ken@hayber.us>, All rights reserved.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License.
13 This program is distributed in the hope that it will be useful
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 import os
, time
, string
, sys
, Queue
30 print 'No ALSA support'
37 print 'No OSS support!!'
46 if not HAVE_AO
and not HAVE_OSS
:
47 print 'No OSS and no AO support Falling back to linuxaudiodev which sucks!!'
56 """A class to playback sound files to an audio device."""
64 def __init__(self
, driver
='ao', id='esd', buffersize
=4096, device
='/dev/dsp',):
65 """Initialize the Player instance"""
68 self
.buffersize
= buffersize
71 self
.queue
= Queue
.Queue(5)
73 def open(self
, rate
=44100, channels
=2):
74 """Open and configure the audio device driver"""
76 #try 3 times to open the device, then give up.
78 if self
.driver
== 'alsa':
79 self
.dev
= alsaaudio
.PCM(cardname
=self
.device
)
80 self
.dev
.setchannels(channels
)
81 self
.dev
.setrate(rate
)
82 if sys
.byteorder
== 'big':
83 format
= alsaaudio
.PCM_FORMAT_S16_BE
85 format
= alsaaudio
.PCM_FORMAT_S16_LE
86 self
.dev
.setformat(format
)
87 elif self
.driver
== 'ao':
88 self
.dev
= ao
.AudioDevice(self
.id, bits
, rate
, channels
)
89 elif self
.driver
== 'oss':
90 self
.dev
= ossaudiodev
.open(self
.device
, 'w')
91 if sys
.byteorder
== 'big':
92 format
= ossaudiodev
.AFMT_S16_BE
94 format
= ossaudiodev
.AFMT_S16_LE
95 self
.dev
.setparameters(format
, channels
, rate
)
96 elif self
.driver
== 'linux':
97 self
.dev
= linuxaudiodev
.open('w')
98 if sys
.byteorder
== 'big':
99 format
= linuxaudiodev
.AFMT_S16_BE
101 format
= linuxaudiodev
.AFMT_S16_LE
102 self
.dev
.setparameters(rate
, bits
, channels
, format
)
107 """Close the current device if open and delete it"""
109 if self
.driver
in ('ao', 'alsa', 'linux'):
111 elif self
.driver
in ('oss',):
115 def write(self
, buff
, bytes
):
116 """Write data to the audio device"""
117 if self
.driver
in ('ao',):
118 self
.dev
.play(buff
, bytes
)
119 elif self
.driver
in ('oss', 'alsa'):
122 while self
.dev
.obuffree() < bytes
:
124 self
.dev
.write(buff
[:bytes
])
126 def play(self
, name
, type):
127 """Push the song info on the queue"""
128 if not os
.path
.isfile(name
):
129 raise SyntaxError, "File not found or not accessible (%s)." % name
130 self
.queue
.put((name
, type))
133 """Check the filename and type, create a decoder and start playing"""
135 (name
, type) = self
.queue
.get()
136 if os
.path
.isfile(name
):
138 self
.decoder
= plugins
.get_decoder(name
, type, self
.buffersize
)
140 continue #get the next song
142 raise SyntaxError, 'play takes a filename.'
146 """Start playing a file calling the current decoder to get file info and data"""
150 self
.total_time
= self
.decoder
.length()
151 self
.remain
= self
.total_time
156 self
.open(self
.decoder
.samplerate(), self
.decoder
.channels())
158 while self
.state
== 'play' or self
.state
== 'pause':
159 if self
.state
== 'pause':
162 self
.decoder
.seek(self
.seek_val
)
165 (buff
, bytes
) = self
.decoder
.read()
168 self
.elapse
= self
.total_time
172 self
.elapse
= self
.decoder
.tell()
173 self
.remain
= max(0, self
.total_time
- self
.elapse
)
174 self
.write(buff
, bytes
)
175 if self
.elapse
!= last_elapse
or self
.state
== 'pause':
176 last_elapse
= self
.elapse
183 """Set a flag telling the current play-loop to exit and close the device"""
186 try: self
.queue
.get_nowait()
187 except Queue
.Empty
: break
190 """Pause playback (works as a toggle between play and pause)"""
191 if self
.state
== 'play':
193 elif self
.state
== 'pause':
196 def seek(self
, percent
):
197 """Jump to a specific point in the song by percent"""
198 self
.seek_val
= percent
200 def set_volume(self
, volume
, device
=None, channel
='PCM'):
201 """Set the PCM volume to a new value"""
205 mixer
= alsaaudio
.Mixer(channel
, 0, device
)
208 print >>sys
.stderr
, "Failed to open mixer device %s" % device
212 mixer
= ossaudiodev
.openmixer(device
)
213 mixer
.set(ossaudiodev
.SOUND_MIXER_PCM
, (vol
, vol
))
215 print >>sys
.stderr
, "Failed to open mixer device %s" % device
219 def get_volume(self
, device
=None, channel
='PCM'):
220 """Return the current volume setting"""
223 mixer
= alsaaudio
.Mixer(channel
, 0, device
)
224 vol
= mixer
.getvolume()
225 return float(max(vol
[0], vol
[1]))
227 print >>sys
.stderr
, "Failed to open mixer device %s, channel %s" % (device
, channel
)
232 mixer
= ossaudiodev
.openmixer(device
)
233 vol
= mixer
.get(ossaudiodev
.SOUND_MIXER_PCM
)
234 return float(max(vol
[0], vol
[1]))
236 print >>sys
.stderr
, "Failed to open mixer device %s" % device