source: src/mlx/sound.py@ 176:cb593ff93551

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

Checklist playback seems to work

File size: 7.4 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(time.time() - self._pending[0][0],
79 0.0)
80 else:
81 timeout = 10.0
82
83 self._requestCondition.wait(timeout)
84
85 requests = []
86 for (path, finishData) in self._requests:
87 requests.append((path, finishData, self._count))
88 self._count += 1
89 self._requests = []
90
91 now = time.time()
92 toClose = []
93 while self._pending and \
94 self._pending[0][0]<=now:
95 toClose.append(self._pending[0][1])
96 del self._pending[0]
97
98 for (alias, (finishCallback, extra)) in toClose:
99 success = True
100 try:
101 print "Closing", alias
102 self._mci.send("close " + alias)
103 print "Closed", alias
104 except Exception, e:
105 print "Failed closing " + alias + ":", str(e)
106 success = False
107
108 if finishCallback is not None:
109 try:
110 finishCallback(success, extra)
111 except:
112 traceback.print_exc()
113
114 for (path, finishData, counter) in requests:
115 try:
116 alias = "mlxsound%d" % (counter,)
117 print "Starting to play", path, "as", alias
118 self._mci.send("open \"%s\" alias %s" % \
119 (path, alias))
120 self._mci.send("set %s time format milliseconds" % \
121 (alias,))
122 lengthBuffer = self._mci.send("status %s length" % \
123 (alias,))
124 self._mci.send("play %s from 0 to %s" % \
125 (alias, lengthBuffer))
126 length = int(lengthBuffer)
127 timeout = time.time() + length / 1000.0
128 with self._requestCondition:
129 self._pending.append((timeout, (alias, finishData)))
130 self._pending.sort()
131 print "Started to play", path
132 except Exception, e:
133 print "Failed to start playing " + path + ":", str(e)
134 (finishCallback, extra) = finishData
135 if finishCallback is not None:
136 try:
137 finishCallback(None, extra)
138 except:
139 traceback.print_exc()
140
141 _thread = None
142
143 def initializeSound(soundsDirectory):
144 """Initialize the sound handling with the given directory containing
145 the sound files."""
146 global _thread
147 _thread = SoundThread(soundsDirectory)
148 _thread.start()
149
150 def startSound(name, finishCallback = None, extra = None):
151 """Start playing back the given sound.
152
153 name should be the name of a sound file relative to the sound directory
154 given in initializeSound."""
155 _thread.requestSound(name, finishCallback = finishCallback,
156 extra = extra)
157
158#------------------------------------------------------------------------------
159
160else: # os.name!="nt"
161 def initializeSound(soundsDirectory):
162 """Initialize the sound handling with the given directory containing
163 the sound files."""
164 pass
165
166 def startSound(name, finishCallback = None, extra = None):
167 """Start playing back the given sound.
168
169 FIXME: it does not do anything currently, but it should."""
170 print "sound.startSound:", name
171
172#------------------------------------------------------------------------------
173#------------------------------------------------------------------------------
174
175if __name__ == "__main__":
176 def callback(result, extra):
177 print "callback", result, extra
178
179 initializeSound("e:\\home\\vi\\tmp")
180 startSound("malev.mp3", finishCallback = callback, extra="malev.mp3")
181 time.sleep(5)
182 startSound("ding.wav", finishCallback = callback, extra="ding1.wav")
183 time.sleep(5)
184 startSound("ding.wav", finishCallback = callback, extra="ding2.wav")
185 time.sleep(5)
186 startSound("ding.wav", finishCallback = callback, extra="ding3.wav")
187 time.sleep(50)
188
189#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.