source: src/mlx/sound.py@ 180:a126eca82a37

Last change on this file since 180:a126eca82a37 was 180:a126eca82a37, checked in by István Váradi <ivaradi@…>, 12 years ago

Fixed busy waiting for finishing a playback

File size: 7.5 KB
Line 
1# Module to handle sound playback
2
3#------------------------------------------------------------------------------
4
5import os
6import traceback
7
8#------------------------------------------------------------------------------
9
10if os.name=="nt":
11 import time
12 import threading
13 from ctypes import windll, c_buffer
14
15 class MCIException(Exception):
16 """MCI exception."""
17 def __init__(self, mci, command, errorCode):
18 """Construct an MCI exception for the given error code."""
19 message = "MCI error: %s: %s" % (command, mci.getErrorString(errorCode))
20 super(MCIException, self).__init__(message)
21
22 class MCI:
23 """Interface for the Media Control Interface."""
24 def __init__(self):
25 """Construct the interface."""
26 self.w32mci = windll.winmm.mciSendStringA
27 self.w32mcierror = windll.winmm.mciGetErrorStringA
28
29 def send(self, command):
30 """Send the given command to the MCI."""
31 buffer = c_buffer(255)
32 errorCode = self.w32mci(str(command), buffer, 254, 0)
33 if errorCode:
34 raise MCIException(self, command, errorCode)
35 else:
36 return buffer.value
37
38 def getErrorString(self, errorCode):
39 """Get the string representation of the given error code."""
40 buffer = c_buffer(255)
41 self.w32mcierror(int(errorCode), buffer, 254)
42 return buffer.value
43
44 class SoundThread(threading.Thread):
45 """The thread controlling the playback of sounds."""
46 def __init__(self, soundsDirectory):
47 threading.Thread.__init__(self)
48
49 self._soundsDirectory = soundsDirectory
50 self._mci = MCI()
51
52 self._requestCondition = threading.Condition()
53 self._requests = []
54 self._pending = []
55 self._count = 0
56
57 self.daemon = True
58
59 def requestSound(self, name, finishCallback = None, extra = None):
60 """Request the playback of the sound with the given name."""
61 path = name if os.path.isabs(name) \
62 else os.path.join(self._soundsDirectory, name)
63 with self._requestCondition:
64 self._requests.append((path, (finishCallback, extra)))
65 self._requestCondition.notify()
66
67 def run(self):
68 """Perform the operation of the thread.
69
70 It waits for a request or a timeout. If a request is received, that
71 is started to be played back. If a timeout occurs, the file is
72 closed."""
73
74 while True:
75 with self._requestCondition:
76 if not self._requests:
77 if self._pending:
78 timeout = max(self._pending[0][0] - time.time(),
79 0.0)
80 else:
81 timeout = 10.0
82
83 #print "Waiting", timeout
84 self._requestCondition.wait(timeout)
85
86 requests = []
87 for (path, finishData) in self._requests:
88 requests.append((path, finishData, self._count))
89 self._count += 1
90 self._requests = []
91
92 now = time.time()
93 toClose = []
94 while self._pending and \
95 self._pending[0][0]<=now:
96 toClose.append(self._pending[0][1])
97 del self._pending[0]
98
99 for (alias, (finishCallback, extra)) in toClose:
100 success = True
101 try:
102 print "Closing", alias
103 self._mci.send("close " + alias)
104 print "Closed", alias
105 except Exception, e:
106 print "Failed closing " + alias + ":", str(e)
107 success = False
108
109 if finishCallback is not None:
110 try:
111 finishCallback(success, extra)
112 except:
113 traceback.print_exc()
114
115 for (path, finishData, counter) in requests:
116 try:
117 alias = "mlxsound%d" % (counter,)
118 print "Starting to play", path, "as", alias
119 self._mci.send("open \"%s\" alias %s" % \
120 (path, alias))
121 self._mci.send("set %s time format milliseconds" % \
122 (alias,))
123 lengthBuffer = self._mci.send("status %s length" % \
124 (alias,))
125 self._mci.send("play %s from 0 to %s" % \
126 (alias, lengthBuffer))
127 length = int(lengthBuffer)
128 timeout = time.time() + length / 1000.0
129 with self._requestCondition:
130 self._pending.append((timeout, (alias, finishData)))
131 self._pending.sort()
132 print "Started to play", path
133 except Exception, e:
134 print "Failed to start playing " + path + ":", str(e)
135 (finishCallback, extra) = finishData
136 if finishCallback is not None:
137 try:
138 finishCallback(None, extra)
139 except:
140 traceback.print_exc()
141
142 _thread = None
143
144 def initializeSound(soundsDirectory):
145 """Initialize the sound handling with the given directory containing
146 the sound files."""
147 global _thread
148 _thread = SoundThread(soundsDirectory)
149 _thread.start()
150
151 def startSound(name, finishCallback = None, extra = None):
152 """Start playing back the given sound.
153
154 name should be the name of a sound file relative to the sound directory
155 given in initializeSound."""
156 _thread.requestSound(name, finishCallback = finishCallback,
157 extra = extra)
158
159#------------------------------------------------------------------------------
160
161else: # os.name!="nt"
162 def initializeSound(soundsDirectory):
163 """Initialize the sound handling with the given directory containing
164 the sound files."""
165 pass
166
167 def startSound(name, finishCallback = None, extra = None):
168 """Start playing back the given sound.
169
170 FIXME: it does not do anything currently, but it should."""
171 print "sound.startSound:", name
172
173#------------------------------------------------------------------------------
174#------------------------------------------------------------------------------
175
176if __name__ == "__main__":
177 def callback(result, extra):
178 print "callback", result, extra
179
180 initializeSound("e:\\home\\vi\\tmp")
181 startSound("malev.mp3", finishCallback = callback, extra="malev.mp3")
182 time.sleep(5)
183 startSound("ding.wav", finishCallback = callback, extra="ding1.wav")
184 time.sleep(5)
185 startSound("ding.wav", finishCallback = callback, extra="ding2.wav")
186 time.sleep(5)
187 startSound("ding.wav", finishCallback = callback, extra="ding3.wav")
188 time.sleep(50)
189
190#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.