Mercurial > fife-parpg
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'] |