source: src/mlx/sound.py@ 341:c18840deae77

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

Some fixes

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