Package fife :: Package extensions :: Module soundmanager
[hide private]
[frames] | no frames]

Source Code for Module fife.extensions.soundmanager

  1  # -*- coding: utf-8 -*-
 
  2  
 
  3  # ####################################################################
 
  4  #  Copyright (C) 2005-2010 by the FIFE team
 
  5  #  http://www.fifengine.net
 
  6  #  This file is part of FIFE.
 
  7  #
 
  8  #  FIFE is free software; you can redistribute it and/or
 
  9  #  modify it under the terms of the GNU Lesser General Public
 
 10  #  License as published by the Free Software Foundation; either
 
 11  #  version 2.1 of the License, or (at your option) any later version.
 
 12  #
 
 13  #  This library is distributed in the hope that it will be useful,
 
 14  #  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 15  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
 16  #  Lesser General Public License for more details.
 
 17  #
 
 18  #  You should have received a copy of the GNU Lesser General Public
 
 19  #  License along with this library; if not, write to the
 
 20  #  Free Software Foundation, Inc.,
 
 21  #  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 22  # ####################################################################
 
 23  
 
 24  """
 
 25  Sound Manager
 
 26  ==================================
 
 27  
 
 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
 
 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
 
 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.
 
 34  
 
 35  Usage::
 
 36    soundmanager = SoundManager(my_fife_engine)
 
 37  
 
 38    emitter = soundmanager.createSoundEmitter("path/filename.ogg")
 
 39    emitter.gain = 128
 
 40    emitter.play()
 
 41  
 
 42  """ 
 43  
 
 44  from fife import fife 
 45  
 
 46  import fife.extensions.fife_timer as fife_timer 
 47  from fife.extensions.pychan.tools import callbackWithArguments as cbwa 
 48  
 
49 -class SoundEmitter(object):
50 """ 51 Wraps the L{fife.SoundEmitter} class. 52 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 55 looping). All instances of SoundEmitter should be created by SoundManager. 56 57 @todo: At some point this class will store positional information 58 and also be responsible for updating the L{fife.SoundEmitter} position. 59 """
60 - def __init__(self, soundmanager, clip, soundname, emitter):
61 """ 62 @param soundmanager: A reference to the SoundManager 63 @type soundmanager: L{SoundManager} 64 @param clipid: The FIFE sound clip ID from the sound clip pool 65 @type clipid: C{int} 66 @param soundname: The filename of the sound 67 @type soundname: C{string} 68 @param emitter: A reference to the L{fife.SoundEmitter} associated with this clip 69 @type emitter: L{fife.SoundEmitter} 70 71 """ 72 self._soundmanager = soundmanager 73 self._name = soundname 74 75 #The FIFE SoundEmitter associated with this SoundEmitter. 76 #Note that we do NOT own the emitter. 77 self._fifeemitter = emitter 78 self._fifeemitter.thisown = 0 79 self._fifeclip = clip 80 81 #0 = mute, 255 = normal volume 82 self._gain = 255.0 83 self._looping = False 84 85 #if you set the callback it will be executed after the sound 86 #has finished playing. 87 self._callback = None 88 89 #length of the sound 90 self._duration = 0 91 92 self._timer = None 93 94 self._position = None 95 self._rolloff = 0
96
97 - def __del__(self):
98 self._soundmanager.unregisterClip(self)
99
100 - def play(self):
101 self._soundmanager.playClip(self)
102
103 - def stop(self):
104 self._soundmanager.stopClip(self)
105
106 - def _getClip(self):
107 return self._fifeclip
108
109 - def _getGain(self):
110 return self._gain
111
112 - def _setGain(self, gain):
113 """ 114 Sets the volume of the L{SoundEmitter}. 115 116 @param gain: Value should be from 0-255. 0 being mute and 255 being the normal 117 volume of the clip. 118 @type gain: C{int} 119 """ 120 self._gain = float(gain)
121
122 - def _getLooping(self):
123 return self._looping
124
125 - def _setLooping(self, looping):
126 self._looping = looping
127
128 - def _getFifeEmitter(self):
129 return self._fifeemitter
130
131 - def _setFifeEmitter(self, emitter):
132 self._fifeemitter = emitter 133 if self._fifeemitter: 134 self._fifeemitter.thisown = 0
135
136 - def _getName(self):
137 return self._name
138
139 - def _getCallback(self):
140 return self._callback
141
142 - def _setCallback(self, cb):
143 self._callback = cb
144
145 - def _getDuration(self):
146 return self._duration
147
148 - def _setDuration(self, millliseconds):
149 self._duration = millliseconds
150
151 - def _getTimer(self):
152 return self._timer
153
154 - def _setTimer(self, timer):
155 self._timer = timer
156
157 - def _setPosition(self, position):
158 self._position = position
159
160 - def _getPosition(self):
161 return self._position
162
163 - def _setRolloff(self, rolloff):
164 self._rolloff = rolloff
165
166 - def _getRolloff(self):
167 return self._rolloff
168 169 rolloff = property(_getRolloff, _setRolloff) 170 position = property(_getPosition, _setPosition) 171 timer = property(_getTimer, _setTimer) 172 clip = property(_getClip) 173 gain = property(_getGain, _setGain) 174 looping = property(_getLooping, _setLooping) 175 fifeemitter = property(_getFifeEmitter, _setFifeEmitter) 176 name = property(_getName) 177 callback = property(_getCallback, _setCallback) 178 duration = property(_getDuration, _setDuration)
179
180 -class SoundManager(object):
181 """ 182 A simple sound manager class. 183 184 This class manages and plays all the sounds of the game. 185 It creates SoundEmitters and ensures that there is only one 186 L{fife.SoundEmitter} per unique sound. 187 """
188 - def __init__(self, engine):
189 """ 190 @param engine: A reference to the FIFE engine 191 @type engine: L{fife.Engine} 192 """ 193 194 self._engine = engine 195 196 self._fifesoundclipmanager = self._engine.getSoundClipManager() 197 198 self._fifesoundmanager = self._engine.getSoundManager() 199 self._fifesoundmanager.init() 200 201 self._fifesoundmanager.setListenerOrientation(0,1,0) 202 203 # basic rolloff used for positional sounds 204 self._rolloff = 1 205 206 #A dict of fife emitters 207 self._loadedclips = {} 208 209 #A list of created clips 210 self._soundclips = [] 211 212 #A tuple representing the listener position (x,y) 213 self._listenerposition = None
214
215 - def createSoundEmitter(self, filename, forceUnique=False, position=None):
216 """ 217 Returns a valid SoundEmitter instance. 218 219 @param filename: The relative path and filename of the sound file 220 @type clip: C{string} 221 @param forceUnique: This forces a new L{fife.SoundEmitter} to be created. 222 This is useful if you want more than one instance of the same sound 223 to be played at the same time. 224 @type forceUnique: C{boolean} 225 @param position: The position on the map that the sound emitter 226 is to be created at. 227 @type position: L{tuple} 228 229 @return: Returns a new L{SoundEmitter} instance. 230 @rtype: L{SoundEmitter} 231 """ 232 if not self._loadedclips.has_key(filename): 233 soundclipptr = self._fifesoundclipmanager.get(filename) 234 fifeemitter = self._fifesoundmanager.createEmitter() 235 fifeemitter.thisown = 0 236 fifeemitter.setSoundClip(soundclipptr) 237 self._loadedclips[filename] = [fifeemitter] 238 clip = SoundEmitter(self, soundclipptr, filename, fifeemitter) 239 clip.duration = fifeemitter.getDuration() 240 else: 241 if forceUnique: 242 soundclipptr = self._fifesoundclipmanager.get(filename) 243 fifeemitter = self._fifesoundmanager.createEmitter() 244 fifeemitter.thisown = 0 245 fifeemitter.setSoundClip(soundclipptr) 246 self._loadedclips[filename].append(fifeemitter) 247 else: 248 fifeemitter = self._loadedclips[filename][0] 249 250 clip = SoundEmitter(self, fifeemitter.getSoundClip(), filename, fifeemitter) 251 clip.duration = fifeemitter.getDuration() 252 253 if position is not None: 254 clip.position = position 255 clip.rolloff = self.rolloff 256 257 self._soundclips.append(clip) 258 259 return clip
260
261 - def playClip(self, clip):
262 """ 263 Plays a sound clip. 264 265 This function does not use the L{fife.SoundEmitter} 266 "looping" property to loop a sound. Instead it registers 267 a new timer and uses the duration of the clip as the timer length. 268 269 If the SoundEmitter is invalid (no fifeemitter) then it attempts 270 to load it before playing it. 271 272 @note: This will stop any clips that use the same L{fife.SoundEmitter}. 273 You cannot play the same sound more than once at a time unless you create 274 the SoundEmitter with the forceUnique paramater set to True. 275 276 @param clip: The L{SoundEmitter} to be played 277 @type clip: L{SoundEmitter} 278 """ 279 if clip.fifeemitter: 280 if clip.callback: 281 if clip.timer: 282 clip.timer.stop() 283 timer = None 284 285 if clip.looping: 286 repeat = 0 287 def real_callback(clip): 288 clip.fifeemitter.stop() 289 clip.fifeemitter.setGain(float(clip.gain)/255.0) 290 if self.listenerposition and clip.position: 291 # Use 1 as z coordinate, no need to specify it 292 clip.fifeemitter.setPosition(clip.position[0], clip.position[1], 1) 293 clip.fifeemitter.setRolloff(clip.rolloff) 294 elif self.listenerposition and not clip.position: 295 clip.fifeemitter.setPosition(self._listenerposition[0], self._listenerposition[1], 1) 296 clip.fifeemitter.setRolloff(self.rolloff) 297 298 clip.fifeemitter.play()
299 300 clip.callback = cbwa(real_callback, clip) 301 302 else: 303 repeat = 1 304 305 clip.timer = fife_timer.Timer(clip.duration, clip.callback, repeat) 306 307 else: 308 if clip.looping: 309 def real_callback(clip): 310 clip.fifeemitter.stop() 311 clip.fifeemitter.setGain(float(clip.gain)/255.0) 312 if self.listenerposition and clip.position: 313 # Use 1 as z coordinate, no need to specify it 314 clip.fifeemitter.setPosition(clip.position[0], clip.position[1], 1) 315 clip.fifeemitter.setRolloff(clip.rolloff) 316 elif self.listenerposition and not clip.position: 317 clip.fifeemitter.setPosition(self._listenerposition[0], self._listenerposition[1], 1) 318 clip.fifeemitter.setRolloff(self.rolloff) 319 320 clip.fifeemitter.play()
321 322 clip.callback = cbwa(real_callback, clip) 323 clip.timer = fife_timer.Timer(clip.duration, clip.callback, 0) 324 325 clip.fifeemitter.setGain(float(clip.gain)/255.0) 326 327 if self.listenerposition and clip.position: 328 # Use 1 as z coordinate, no need to specify it 329 clip.fifeemitter.setPosition(clip.position[0], clip.position[1], 1) 330 clip.fifeemitter.setRolloff(clip.rolloff) 331 elif self.listenerposition and not clip.position: 332 clip.fifeemitter.setPosition(self._listenerposition[0], self._listenerposition[1], 1) 333 clip.fifeemitter.setRolloff(self.rolloff) 334 335 clip.fifeemitter.play() 336 if clip.timer: 337 clip.timer.start() 338 339 else: 340 clip = self.createSoundEmitter(clip.name) 341 self.playClip(clip) 342
343 - def unregisterClip(self, clip):
344 self.stopClip(clip) 345 346 if clip in self._soundclips: 347 self._soundclips.remove(clip)
348
349 - def stopClip(self, clip):
350 """ 351 Stops playing the sound clip. Note that this will stop all clips that 352 use the same FIFE emitter. 353 354 @param clip: The SoundEmitter to stop. 355 @type clip: L{SoundEmitter} 356 """ 357 if clip.fifeemitter: 358 clip.fifeemitter.stop() 359 360 if clip.timer: 361 clip.timer.stop() 362 clip.timer = None
363
364 - def stopAllSounds(self):
365 for clip in self._soundclips: 366 self.stopClip(clip)
367
368 - def destroy(self):
369 """ 370 Releases all instances of L{fife.SoundEmitter}. 371 372 @note: This does not free the resources from the FIFE sound clip pool. 373 """ 374 self.stopAllSounds() 375 376 for emitterlist in self._loadedclips.values(): 377 for emitter in emitterlist: 378 self._fifesoundmanager.releaseEmitter(emitter.getId()) 379 emitter = None 380 381 self._loadedclips.clear() 382 def _getRolloff(self): 383 return self._rolloff
384
385 - def _setRolloff(self, rolloff):
386 self._rolloff = rolloff
387
388 - def _getListenerPosition(self):
389 return self._listenerposition
390
391 - def _setListenerPosition(self, position):
392 self._listenerposition = position 393 self._fifesoundmanager.setListenerPosition(self._listenerposition[0], self._listenerposition[1], 10)
394 395 rolloff = property(_getRolloff, _setRolloff) 396 listenerposition = property(_getListenerPosition, _setListenerPosition) 397 398 __all__ = ['SoundEmitter','SoundManager'] 399