comparison engine/python/fife/extensions/soundmanager.py @ 498:5ff83f209333

Added positioning to the soundmanager classes Made the player ship explosions positional as usage demonstration
author nihathrael@33b003aa-7bff-0310-803a-e67f0ece8222
date Fri, 14 May 2010 16:23:52 +0000
parents ae9f5383f5b1
children 5e6ff32a46fb
comparison
equal deleted inserted replaced
497:559a26347730 498:5ff83f209333
26 ================================== 26 ==================================
27 27
28 This is a simple implementation of a sound manager that was originaly 28 This is a simple implementation of a sound manager that was originaly
29 intended for the shooter demo. It was functional enough that we decided 29 intended for the shooter demo. It was functional enough that we decided
30 to include it in the FIFE extensions. This is by no means a fully featured 30 to include it in the FIFE extensions. This is by no means a fully featured
31 implementation for several reasons. It doesnt limit how many sounds can 31 implementation for several reasons. It doesnt limit how many sounds can
32 play at once or allow the positioning of sounds. It does however provide 32 play at once or allow the positioning of sounds. It does however provide
33 a good starting point for a more advanced version of a sound manager. 33 a good starting point for a more advanced version of a sound manager.
34 34
35 Usage:: 35 Usage::
36 soundmanager = SoundManager(my_fife_engine) 36 soundmanager = SoundManager(my_fife_engine)
37 37
38 emitter = soundmanager.createSoundEmitter("path/filename.ogg") 38 emitter = soundmanager.createSoundEmitter("path/filename.ogg")
39 emitter.gain = 128 39 emitter.gain = 128
40 emitter.play() 40 emitter.play()
41 41
42 """ 42 """
48 48
49 class SoundEmitter(object): 49 class SoundEmitter(object):
50 """ 50 """
51 Wraps the L{fife.SoundEmitter} class. 51 Wraps the L{fife.SoundEmitter} class.
52 52
53 This class wraps an instance of a L{fife.SoundEmitter} class along 53 This class wraps an instance of a L{fife.SoundEmitter} class along
54 with some information about a sound clip (like gain and if its 54 with some information about a sound clip (like gain and if its
55 looping). All instances of SoundEmitter should be created by SoundManager. 55 looping). All instances of SoundEmitter should be created by SoundManager.
56 56
57 @todo: At some point this class will store positional information 57 @todo: At some point this class will store positional information
58 and also be responsible for updating the L{fife.SoundEmitter} position. 58 and also be responsible for updating the L{fife.SoundEmitter} position.
59 """ 59 """
60 def __init__(self, soundmanager, clipid, soundname, emitter): 60 def __init__(self, soundmanager, clipid, soundname, emitter):
61 """ 61 """
64 @param clipid: The FIFE sound clip ID from the sound clip pool 64 @param clipid: The FIFE sound clip ID from the sound clip pool
65 @type clipid: C{int} 65 @type clipid: C{int}
66 @param soundname: The filename of the sound 66 @param soundname: The filename of the sound
67 @type soundname: C{string} 67 @type soundname: C{string}
68 @param emitter: A reference to the L{fife.SoundEmitter} associated with this clip 68 @param emitter: A reference to the L{fife.SoundEmitter} associated with this clip
69 @type emitter: L{fife.SoundEmitter} 69 @type emitter: L{fife.SoundEmitter}
70 70
71 """ 71 """
72 self._soundmanager = soundmanager 72 self._soundmanager = soundmanager
73 self._name = soundname 73 self._name = soundname
74 74
75 #The FIFE SoundEmitter associated with this SoundEmitter. 75 #The FIFE SoundEmitter associated with this SoundEmitter.
76 #Note that we do NOT own the emitter. 76 #Note that we do NOT own the emitter.
77 self._fifeemitter = emitter 77 self._fifeemitter = emitter
78 self._fifeemitter.thisown = 0 78 self._fifeemitter.thisown = 0
79 self._fifeclipid = clipid 79 self._fifeclipid = clipid
80 80
81 #0 = mute, 255 = normal volume 81 #0 = mute, 255 = normal volume
82 self._gain = 255.0 82 self._gain = 255.0
83 self._looping = False 83 self._looping = False
84 84
85 #if you set the callback it will be executed after the sound 85 #if you set the callback it will be executed after the sound
86 #has finished playing. 86 #has finished playing.
87 self._callback = None 87 self._callback = None
88 88
89 #length of the sound 89 #length of the sound
90 self._duration = 0 90 self._duration = 0
91 91
92 self._timer = None 92 self._timer = None
93 93
94 self._position = None
95 self._rolloff = 0
96
94 def play(self): 97 def play(self):
95 self._soundmanager.playClip(self) 98 self._soundmanager.playClip(self)
96 99
97 def stop(self): 100 def stop(self):
98 self._soundmanager.stopClip(self) 101 self._soundmanager.stopClip(self)
99 102
100 def _getClipID(self): 103 def _getClipID(self):
101 return self._fifeclipid 104 return self._fifeclipid
102 105
103 def _getGain(self): 106 def _getGain(self):
104 return self._gain 107 return self._gain
105 108
106 def _setGain(self, gain): 109 def _setGain(self, gain):
107 """ 110 """
108 Sets the volume of the L{SoundEmitter}. 111 Sets the volume of the L{SoundEmitter}.
109 112
110 @param gain: Value should be from 0-255. 0 being mute and 255 being the normal 113 @param gain: Value should be from 0-255. 0 being mute and 255 being the normal
111 volume of the clip. 114 volume of the clip.
112 @type gain: C{int} 115 @type gain: C{int}
113 """ 116 """
114 self._gain = float(gain) 117 self._gain = float(gain)
115 118
116 def _getLooping(self): 119 def _getLooping(self):
117 return self._looping 120 return self._looping
118 121
119 def _setLooping(self, looping): 122 def _setLooping(self, looping):
120 self._looping = looping 123 self._looping = looping
121 124
122 def _getFifeEmitter(self): 125 def _getFifeEmitter(self):
123 return self._fifeemitter 126 return self._fifeemitter
124 127
125 def _setFifeEmitter(self, emitter): 128 def _setFifeEmitter(self, emitter):
126 self._fifeemitter = emitter 129 self._fifeemitter = emitter
127 if self._fifeemitter: 130 if self._fifeemitter:
128 self._fifeemitter.thisown = 0 131 self._fifeemitter.thisown = 0
129 132
132 135
133 def _getCallback(self): 136 def _getCallback(self):
134 return self._callback 137 return self._callback
135 138
136 def _setCallback(self, cb): 139 def _setCallback(self, cb):
137 self._callback = cb 140 self._callback = cb
138 141
139 def _getDuration(self): 142 def _getDuration(self):
140 return self._duration 143 return self._duration
141 144
142 def _setDuration(self, millliseconds): 145 def _setDuration(self, millliseconds):
143 self._duration = millliseconds 146 self._duration = millliseconds
144 147
145 def _getTimer(self): 148 def _getTimer(self):
146 return self._timer 149 return self._timer
147 150
148 def _setTimer(self, timer): 151 def _setTimer(self, timer):
149 self._timer = timer 152 self._timer = timer
150 153
154 def _setPosition(self, position):
155 self._position = position
156
157 def _getPosition(self):
158 return self._position
159
160 def _setRolloff(self, rolloff):
161 self._rolloff = rolloff
162
163 def _getRolloff(self):
164 return self._rolloff
165
166 rolloff = property(_getRolloff, _setRolloff)
167 position = property(_getPosition, _setPosition)
151 timer = property(_getTimer, _setTimer) 168 timer = property(_getTimer, _setTimer)
152 clipid = property(_getClipID) 169 clipid = property(_getClipID)
153 gain = property(_getGain, _setGain) 170 gain = property(_getGain, _setGain)
154 looping = property(_getLooping, _setLooping) 171 looping = property(_getLooping, _setLooping)
155 fifeemitter = property(_getFifeEmitter, _setFifeEmitter) 172 fifeemitter = property(_getFifeEmitter, _setFifeEmitter)
159 176
160 class SoundManager(object): 177 class SoundManager(object):
161 """ 178 """
162 A simple sound manager class. 179 A simple sound manager class.
163 180
164 This class manages and plays all the sounds of the game. 181 This class manages and plays all the sounds of the game.
165 It creates SoundEmitters and ensures that there is only one 182 It creates SoundEmitters and ensures that there is only one
166 L{fife.SoundEmitter} per unique sound. 183 L{fife.SoundEmitter} per unique sound.
167 """ 184 """
168 def __init__(self, engine): 185 def __init__(self, engine):
169 """ 186 """
170 @param engine: A reference to the FIFE engine 187 @param engine: A reference to the FIFE engine
171 @type engine: L{fife.Engine} 188 @type engine: L{fife.Engine}
172 """ 189 """
173 190
174 self._engine = engine 191 self._engine = engine
175 192
176 self._fifesoundmanager = self._engine.getSoundManager() 193 self._fifesoundmanager = self._engine.getSoundManager()
177 self._fifesoundmanager.init() 194 self._fifesoundmanager.init()
178 195
196 # basic rolloff used for positional sounds
197 self._rolloff = 1.9
198
179 #A dict of fife emitters 199 #A dict of fife emitters
180 self._loadedclips = {} 200 self._loadedclips = {}
181 201
182 def createSoundEmitter(self, filename, forceUnique=False): 202 def createSoundEmitter(self, filename, forceUnique=False, position=None):
183 """ 203 """
184 Returns a valid SoundEmitter instance. 204 Returns a valid SoundEmitter instance.
185 205
186 @param filename: The relative path and filename of the sound file 206 @param filename: The relative path and filename of the sound file
187 @type clip: C{string} 207 @type clip: C{string}
188 @param forceUnique: This forces a new L{fife.SoundEmitter} to be created. 208 @param forceUnique: This forces a new L{fife.SoundEmitter} to be created.
189 This is useful if you want more than one instance of the same sound 209 This is useful if you want more than one instance of the same sound
190 to be played at the same time. 210 to be played at the same time.
191 @type forceUnique: C{boolean} 211 @type forceUnique: C{boolean}
192 212 @param position: The position on the map that the sound emitter
213 is to be created at.
214 @type position: L{tuple}
215
193 @return: Returns a new L{SoundEmitter} instance. 216 @return: Returns a new L{SoundEmitter} instance.
194 @rtype: L{SoundEmitter} 217 @rtype: L{SoundEmitter}
195 """ 218 """
196 if not self._loadedclips.has_key(filename): 219 if not self._loadedclips.has_key(filename):
197 clipid = self._engine.getSoundClipPool().addResourceFromFile(filename) 220 clipid = self._engine.getSoundClipPool().addResourceFromFile(filename)
198 fifeemitter = self._fifesoundmanager.createEmitter() 221 fifeemitter = self._fifesoundmanager.createEmitter()
199 fifeemitter.thisown = 0 222 fifeemitter.thisown = 0
200 fifeemitter.setSoundClip(clipid) 223 fifeemitter.setSoundClip(clipid)
201
202 self._loadedclips[filename] = [fifeemitter] 224 self._loadedclips[filename] = [fifeemitter]
203 clip = SoundEmitter(self, clipid, filename, fifeemitter) 225 clip = SoundEmitter(self, clipid, filename, fifeemitter)
204 clip.duration = fifeemitter.getDuration() 226 clip.duration = fifeemitter.getDuration()
205 else: 227 else:
206 if forceUnique: 228 if forceUnique:
207 clipid = self._engine.getSoundClipPool().addResourceFromFile(filename) 229 clipid = self._engine.getSoundClipPool().addResourceFromFile(filename)
208 fifeemitter = self._fifesoundmanager.createEmitter() 230 fifeemitter = self._fifesoundmanager.createEmitter()
209 fifeemitter.thisown = 0 231 fifeemitter.thisown = 0
210 fifeemitter.setSoundClip(clipid) 232 fifeemitter.setSoundClip(clipid)
211 self._loadedclips[filename].append(fifeemitter) 233 self._loadedclips[filename].append(fifeemitter)
212 else: 234 else:
213 fifeemitter = self._loadedclips[filename][0] 235 fifeemitter = self._loadedclips[filename][0]
214 236
215 clip = SoundEmitter(self, fifeemitter.getID(), filename, fifeemitter) 237 clip = SoundEmitter(self, fifeemitter.getID(), filename, fifeemitter)
216 clip.duration = fifeemitter.getDuration() 238 clip.duration = fifeemitter.getDuration()
217 239
240 if position is not None:
241 clip.position = position
242 clip.rolloff = self.rolloff
218 return clip 243 return clip
219 244
220 def playClip(self, clip): 245 def playClip(self, clip):
221 """ 246 """
222 Plays a sound clip. 247 Plays a sound clip.
223 248
224 This function does not use the L{fife.SoundEmitter} 249 This function does not use the L{fife.SoundEmitter}
225 "looping" property to loop a sound. Instead it registers 250 "looping" property to loop a sound. Instead it registers
226 a new timer and uses the duration of the clip as the timer length. 251 a new timer and uses the duration of the clip as the timer length.
227 252
228 If the SoundEmitter is invalid (no fifeemitter) then it attempts 253 If the SoundEmitter is invalid (no fifeemitter) then it attempts
229 to load it before playing it. 254 to load it before playing it.
230 255
231 @note: This will stop any clips that use the same L{fife.SoundEmitter}. 256 @note: This will stop any clips that use the same L{fife.SoundEmitter}.
232 You cannot play the same sound more than once at a time unless you create 257 You cannot play the same sound more than once at a time unless you create
233 the SoundEmitter with the forceUnique paramater set to True. 258 the SoundEmitter with the forceUnique paramater set to True.
234 259
235 @param clip: The L{SoundEmitter} to be played 260 @param clip: The L{SoundEmitter} to be played
236 @type clip: L{SoundEmitter} 261 @type clip: L{SoundEmitter}
237 """ 262 """
238 if clip.fifeemitter: 263 if clip.fifeemitter:
239 if clip.callback: 264 if clip.callback:
240 if clip.timer: 265 if clip.timer:
241 clip.timer.stop() 266 clip.timer.stop()
242 timer = None 267 timer = None
243 268
244 if clip.looping: 269 if clip.looping:
245 repeat = 0 270 repeat = 0
246 def real_callback(c, e, g): 271 def real_callback(c, e, g):
247 c() 272 c()
248 e.stop() 273 e.stop()
251 276
252 clip.callback = cbwa(real_callback, clip.callback, clip.fifeemitter, clip.gain) 277 clip.callback = cbwa(real_callback, clip.callback, clip.fifeemitter, clip.gain)
253 278
254 else: 279 else:
255 repeat = 1 280 repeat = 1
256 281
257 clip.timer = fife_timer.Timer(clip.duration, clip.callback, repeat) 282 clip.timer = fife_timer.Timer(clip.duration, clip.callback, repeat)
258 #clip.timer.start() 283 #clip.timer.start()
259 else: 284 else:
260 if clip.looping: 285 if clip.looping:
261 def real_callback(e, g): 286 def real_callback(e, g):
264 e.play() 289 e.play()
265 290
266 clip.callback = cbwa(real_callback, clip.fifeemitter, clip.gain) 291 clip.callback = cbwa(real_callback, clip.fifeemitter, clip.gain)
267 clip.timer = fife_timer.Timer(clip.duration, clip.callback, 0) 292 clip.timer = fife_timer.Timer(clip.duration, clip.callback, 0)
268 #clip.timer.start() 293 #clip.timer.start()
269 294
270 295
271 clip.fifeemitter.setGain(float(clip.gain)/255.0) 296 clip.fifeemitter.setGain(float(clip.gain)/255.0)
297 if clip.position is not None:
298 # Use 1 as z coordinate, no need to specify it
299 clip.fifeemitter.setPosition(clip.position[0], clip.position[1], 1)
300 clip.fifeemitter.setRolloff(clip.rolloff)
272 clip.fifeemitter.play() 301 clip.fifeemitter.play()
273 if clip.timer: 302 if clip.timer:
274 clip.timer.start() 303 clip.timer.start()
275 else: 304 else:
276 clip = self.createSoundEmitter(clip.name) 305 clip = self.createSoundEmitter(clip.name)
277 self.playClip(clip) 306 self.playClip(clip)
278 307
279 def stopClip(self, clip): 308 def stopClip(self, clip):
280 """ 309 """
281 Stops playing the sound clip. Note that this will stop all clips that 310 Stops playing the sound clip. Note that this will stop all clips that
282 use the same FIFE emitter. 311 use the same FIFE emitter.
283 312
284 @param clip: The SoundEmitter to stop. 313 @param clip: The SoundEmitter to stop.
285 @type clip: L{SoundEmitter} 314 @type clip: L{SoundEmitter}
286 """ 315 """
287 if clip.fifeemitter: 316 if clip.fifeemitter:
288 clip.fifeemitter.stop() 317 clip.fifeemitter.stop()
289 318
290 if clip.timer: 319 if clip.timer:
291 clip.timer.stop() 320 clip.timer.stop()
292 clip.timer = None 321 clip.timer = None
293 322
294 def stopAllSounds(self): 323 def stopAllSounds(self):
295 for emitterlist in self._loadedclips.values(): 324 for emitterlist in self._loadedclips.values():
296 for emitter in emitterlist: 325 for emitter in emitterlist:
297 emitter.stop() 326 emitter.stop()
298 327
299 def destroy(self): 328 def destroy(self):
300 """ 329 """
301 Releases all instances of L{fife.SoundEmitter}. 330 Releases all instances of L{fife.SoundEmitter}.
302 331
303 @note: This does not free the resources from the FIFE sound clip pool. 332 @note: This does not free the resources from the FIFE sound clip pool.
304 """ 333 """
305 self.stopAllSounds() 334 self.stopAllSounds()
306 335
307 for emitterlist in self._loadedclips.values(): 336 for emitterlist in self._loadedclips.values():
308 for emitter in emitterlist: 337 for emitter in emitterlist:
309 self._fifesoundmanager.releaseEmitter(emitter.getID()) 338 self._fifesoundmanager.releaseEmitter(emitter.getID())
310 emitter = None 339 emitter = None
311 340
312 self._loadedclips.clear() 341 self._loadedclips.clear()
313 342
343 def _getRolloff(self):
344 return self._rolloff
345
346 def _setRolloff(self, rolloff):
347 self._rolloff = rolloff
348
349 rolloff = property(_getRolloff, _setRolloff)
350
314 __all__ = ['SoundEmitter','SoundManager'] 351 __all__ = ['SoundEmitter','SoundManager']