source: src/mlx/sound.py@ 585:1a277e09c6b9

Last change on this file since 585:1a277e09c6b9 was 585:1a277e09c6b9, checked in by István Váradi <ivaradi@…>, 9 years ago

Fixed the handling of the Gst library with PyGTK

File size: 10.0 KB
RevLine 
[401]1
2from util import utf2unicode
[298]3
4import os
5import traceback
[133]6
7#------------------------------------------------------------------------------
8
[298]9## @package mlx.sound
10#
11# Sound playback handling.
12#
13# This is the low level sound playback handling. The \ref initializeSound
14# function should be called to initialize the sound handling with the directory
[299]15# containing the sound files. Then the \ref startSound function should be
16# called to start the playback of a certain sound file. A callback may be
17# called when the playback of a certain file has finished.
[298]18#
19# See also the \ref mlx.soundsched module.
[133]20
21#------------------------------------------------------------------------------
22
23if os.name=="nt":
24 import time
25 import threading
26 from ctypes import windll, c_buffer
27
28 class MCIException(Exception):
29 """MCI exception."""
30 def __init__(self, mci, command, errorCode):
31 """Construct an MCI exception for the given error code."""
32 message = "MCI error: %s: %s" % (command, mci.getErrorString(errorCode))
33 super(MCIException, self).__init__(message)
34
35 class MCI:
36 """Interface for the Media Control Interface."""
37 def __init__(self):
38 """Construct the interface."""
39 self.w32mci = windll.winmm.mciSendStringA
40 self.w32mcierror = windll.winmm.mciGetErrorStringA
41
42 def send(self, command):
43 """Send the given command to the MCI."""
44 buffer = c_buffer(255)
45 errorCode = self.w32mci(str(command), buffer, 254, 0)
46 if errorCode:
47 raise MCIException(self, command, errorCode)
48 else:
49 return buffer.value
50
51 def getErrorString(self, errorCode):
52 """Get the string representation of the given error code."""
53 buffer = c_buffer(255)
54 self.w32mcierror(int(errorCode), buffer, 254)
55 return buffer.value
56
57 class SoundThread(threading.Thread):
58 """The thread controlling the playback of sounds."""
59 def __init__(self, soundsDirectory):
60 threading.Thread.__init__(self)
61
62 self._soundsDirectory = soundsDirectory
63 self._mci = MCI()
64
65 self._requestCondition = threading.Condition()
[170]66 self._requests = []
67 self._pending = []
[133]68 self._count = 0
69
70 self.daemon = True
71
[170]72 def requestSound(self, name, finishCallback = None, extra = None):
[133]73 """Request the playback of the sound with the given name."""
[176]74 path = name if os.path.isabs(name) \
75 else os.path.join(self._soundsDirectory, name)
[133]76 with self._requestCondition:
[170]77 self._requests.append((path, (finishCallback, extra)))
[133]78 self._requestCondition.notify()
79
80 def run(self):
81 """Perform the operation of the thread.
82
83 It waits for a request or a timeout. If a request is received, that
84 is started to be played back. If a timeout occurs, the file is
85 closed."""
86
87 while True:
88 with self._requestCondition:
[170]89 if not self._requests:
90 if self._pending:
[180]91 timeout = max(self._pending[0][0] - time.time(),
[170]92 0.0)
[133]93 else:
94 timeout = 10.0
[180]95
96 #print "Waiting", timeout
[133]97 self._requestCondition.wait(timeout)
98
[170]99 requests = []
100 for (path, finishData) in self._requests:
101 requests.append((path, finishData, self._count))
[133]102 self._count += 1
[170]103 self._requests = []
[133]104
105 now = time.time()
[170]106 toClose = []
107 while self._pending and \
108 self._pending[0][0]<=now:
109 toClose.append(self._pending[0][1])
110 del self._pending[0]
[133]111
[170]112 for (alias, (finishCallback, extra)) in toClose:
113 success = True
[133]114 try:
115 print "Closing", alias
116 self._mci.send("close " + alias)
117 print "Closed", alias
118 except Exception, e:
[401]119 print "Failed closing " + alias + ":",
120 print utf2unicode(str(e))
[170]121 success = False
[133]122
[170]123 if finishCallback is not None:
124 try:
125 finishCallback(success, extra)
126 except:
127 traceback.print_exc()
128
129 for (path, finishData, counter) in requests:
[133]130 try:
131 alias = "mlxsound%d" % (counter,)
132 print "Starting to play", path, "as", alias
133 self._mci.send("open \"%s\" alias %s" % \
134 (path, alias))
135 self._mci.send("set %s time format milliseconds" % \
136 (alias,))
137 lengthBuffer = self._mci.send("status %s length" % \
138 (alias,))
139 self._mci.send("play %s from 0 to %s" % \
140 (alias, lengthBuffer))
141 length = int(lengthBuffer)
142 timeout = time.time() + length / 1000.0
143 with self._requestCondition:
[170]144 self._pending.append((timeout, (alias, finishData)))
145 self._pending.sort()
[133]146 print "Started to play", path
147 except Exception, e:
[401]148 print "Failed to start playing " + path + ":",
149 print utf2unicode(str(e))
[170]150 (finishCallback, extra) = finishData
151 if finishCallback is not None:
152 try:
153 finishCallback(None, extra)
154 except:
155 traceback.print_exc()
[133]156
157 _thread = None
158
159 def initializeSound(soundsDirectory):
160 """Initialize the sound handling with the given directory containing
161 the sound files."""
162 global _thread
163 _thread = SoundThread(soundsDirectory)
164 _thread.start()
165
[170]166 def startSound(name, finishCallback = None, extra = None):
[133]167 """Start playing back the given sound.
[422]168
[133]169 name should be the name of a sound file relative to the sound directory
170 given in initializeSound."""
[170]171 _thread.requestSound(name, finishCallback = finishCallback,
172 extra = extra)
[422]173
[573]174 def finalizeSound():
175 """Finalize the sound handling."""
176 pass
177
[133]178#------------------------------------------------------------------------------
179
180else: # os.name!="nt"
[422]181 import threading
[576]182 import time
[422]183 try:
[585]184 from common import gst_init, gobject, gst_element_factory_make
[576]185 from common import GST_STATE_PLAYING, GST_MESSAGE_EOS
[422]186
[576]187 _soundsDirectory = None
188 _bins = set()
[422]189
190 def initializeSound(soundsDirectory):
191 """Initialize the sound handling with the given directory containing
192 the sound files."""
[585]193 gst_init()
[576]194 global _soundsDirectory
195 _soundsDirectory = soundsDirectory
[422]196
197 def startSound(name, finishCallback = None, extra = None):
198 """Start playing back the given sound."""
[576]199 try:
200 playBin = gst_element_factory_make("playbin")
201
202 bus = playBin.get_bus()
203 bus.enable_sync_message_emission()
204 bus.add_signal_watch()
205 bus.connect("message", _handlePlayBinMessage,
206 playBin, finishCallback, extra)
207
208 path = os.path.join(_soundsDirectory, name)
209 playBin.set_property( "uri", "file://%s" % (path,))
210
211 playBin.set_state(GST_STATE_PLAYING)
212 _bins.add(playBin)
213 except Exception, e:
214 print "mlx.sound.startSound: " + str(e)
215 if finishCallback is not None:
216 finishCallback(False, extra)
[422]217
[573]218 def finalizeSound():
219 """Finalize the sound handling."""
[576]220 pass
221
222 def _handlePlayBinMessage(bus, message, bin, finishCallback, extra):
223 """Handle the end of the playback of a sound file."""
224
225 # if message.type==GST_MESSAGE_EOS:
226 # _bins.remove(bin)
227 # if finishCallback is not None:
228 # finishCallback(True, extra)
[573]229
[422]230 except:
[576]231 print "The Gst library is missing from your system. It is needed for sound playback on Linux"
232 traceback.print_exc()
[422]233 def initializeSound(soundsDirectory):
234 """Initialize the sound handling with the given directory containing
235 the sound files."""
236 pass
237
238 def startSound(name, finishCallback = None, extra = None):
239 """Start playing back the given sound.
240
241 FIXME: it does not do anything currently, but it should."""
242 print "sound.startSound:", name
[133]243
[573]244 def finalizeSound():
245 """Finalize the sound handling."""
246 pass
247
[133]248#------------------------------------------------------------------------------
249#------------------------------------------------------------------------------
250
251if __name__ == "__main__":
[170]252 def callback(result, extra):
253 print "callback", result, extra
[422]254
[133]255 initializeSound("e:\\home\\vi\\tmp")
[170]256 startSound("malev.mp3", finishCallback = callback, extra="malev.mp3")
[133]257 time.sleep(5)
[170]258 startSound("ding.wav", finishCallback = callback, extra="ding1.wav")
[133]259 time.sleep(5)
[170]260 startSound("ding.wav", finishCallback = callback, extra="ding2.wav")
[133]261 time.sleep(5)
[170]262 startSound("ding.wav", finishCallback = callback, extra="ding3.wav")
[133]263 time.sleep(50)
264
265#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.