source: src/mlx/sound.py@ 170:7cda0cc74e19

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

The background sounds play back properly

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