view engine/python/fife/extensions/soundmanager.py @ 492:16ceb3228324

Moved the SoundManager and the 2D math function (helpers) from the shooter demo to the fife extensions. Fixed fife_timer as it didn't work in the first place. Modified the SoundManager to use the now working fife_timer and removed the Timer class from the shooter demo entirely
author prock@33b003aa-7bff-0310-803a-e67f0ece8222
date Fri, 07 May 2010 21:07:27 +0000
parents
children e29853880e87
line wrap: on
line source

# -*- coding: utf-8 -*-

# ####################################################################
#  Copyright (C) 2005-2010 by the FIFE team
#  http://www.fifengine.net
#  This file is part of FIFE.
#
#  FIFE is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 2.1 of the License, or (at your option) any later version.
#
#  This library is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library; if not, write to the
#  Free Software Foundation, Inc.,
#  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
# ####################################################################

from fife import fife

import fife.extensions.fife_timer as fife_timer
from fife.extensions.pychan.tools import callbackWithArguments as cbwa

"""
Sound Manager
==================================

This is a simple implementation of a sound manager that was originaly
intended for the shooter demo.  It was functional enough that we decided
to include it in the FIFE extensions.  This is by no means a fully featured
implementation for several reasons.  It doesnt limit how many sounds can 
play at once or allow the positioning of sounds.  It does however provide 
a good starting point for a more advanced version of a sound manager.

"""

class SoundEmitter(object):
	"""
	SoundEmitter

	This class wraps an instance of a L{fife.SoundEmitter} class along 
	with some information about a sound clip (like gain and if its
	looping).  All instances of SoundEmitter should be created by SoundManager.
	
	@todo At some point this class will store positional information
	and also be responsible for updating the L{fife.SoundEmitter} position.
	"""
	def __init__(self, soundmanager, clipid, soundname, emitter):
		"""
		@param soundmanager A reference to the SoundManager
		@param clipid The FIFE sound clip ID from the sound clip pool
		@param soundname The filename of the sound
		@param emitter A reference to the L{fife.SoundEmitter} associated with this clip
		
		"""
		self._soundmanager = soundmanager
		self._name = soundname
			
		#The FIFE SoundEmitter associated with this SoundEmitter.
		#Note that we do NOT own the emitter.
		self._fifeemitter = emitter
		self._fifeemitter.thisown = 0
		self._fifeclipid = clipid
				
		#0 = mute, 255 = normal volume
		self._gain = 255.0
		self._looping = False
		
		#if you set the callback it will be executed after the sound
		#has finished playing.
		self._callback = None
		
		#length of the sound
		self._duration = 0
		
		self._timer = None
		
	def play(self):
		self._soundmanager.playClip(self)
		
	def stop(self):
		self._soundmanager.stopClip(self)
		
	def _getClipID(self):
		return self._fifeclipid

	def _getGain(self):
		return self._gain
		
	def _setGain(self, gain):
		self._gain = float(gain)
		
	def _getLooping(self):
		return self._looping
		
	def _setLooping(self, looping):
		self._looping = looping
		
	def _getFifeEmitter(self):
		return self._fifeemitter
		
	def _setFifeEmitter(self, emitter):
		self._fifeemitter = emitter
		if self._fifeemitter:
			self._fifeemitter.thisown = 0

	def _getName(self):
		return self._name

	def _getCallback(self):
		return self._callback

	def _setCallback(self, cb):
		self._callback = cb	
		
	def _getDuration(self):
		return self._duration
	
	def _setDuration(self, millliseconds):
		self._duration = millliseconds

	def _getTimer(self):
		return self._timer
		
	def _setTimer(self, timer):
		self._timer = timer

	timer = property(_getTimer, _setTimer)
	clipid = property(_getClipID)
	gain = property(_getGain, _setGain)
	looping = property(_getLooping, _setLooping)
	fifeemitter = property(_getFifeEmitter, _setFifeEmitter)
	name = property(_getName)
	callback = property(_getCallback, _setCallback)
	duration = property(_getDuration, _setDuration)

class SoundManager(object):
	"""
	SoundManger

	This class manages and plays all the sounds of the game.  
	It creates SoundEmitters and ensures that there is only one 
	L{fife.SoundEmitter} per unique sound.
	"""
	def __init__(self, engine):
		"""
		@param engine A reference to the FIFE engine
		"""
		
		self._engine = engine
		
		self._fifesoundmanager = self._engine.getSoundManager()
		self._fifesoundmanager.init()
		
		#A dict of fife emitters
		self._loadedclips = {}
		
	def createSoundEmitter(self, filename, forceUnique=False):
		"""
		Returns a valid SoundEmitter instance.
		
		@param filename The relative path and filename of the sound file
		@parm forceUnique This forces a new L{fife.SoundEmitter} to be created.  
		This is useful if you want more than one instance of the same sound 
		to be played at the same time.
		
		@return Returns a new SoundEmitter instance.
		"""
		if not self._loadedclips.has_key(filename):
			clipid = self._engine.getSoundClipPool().addResourceFromFile(filename)
			fifeemitter = self._fifesoundmanager.createEmitter()
			fifeemitter.thisown = 0
			fifeemitter.setSoundClip(clipid)
			
			self._loadedclips[filename] = [fifeemitter]
			clip = SoundEmitter(self, clipid, filename, fifeemitter)
			clip.duration = fifeemitter.getDuration()
		else:
			if forceUnique:
				clipid = self._engine.getSoundClipPool().addResourceFromFile(filename)
				fifeemitter = self._fifesoundmanager.createEmitter()
				fifeemitter.thisown = 0
				fifeemitter.setSoundClip(clipid)
				self._loadedclips[filename].append(fifeemitter)	
			else:
				fifeemitter = self._loadedclips[filename][0]
				
			clip = SoundEmitter(self, fifeemitter.getID(), filename, fifeemitter)
			clip.duration = fifeemitter.getDuration()
		
		return clip
		
	def playClip(self, clip):
		"""
		Plays a sound clip.  
		
		This function does not use the L{fife.SoundEmitter}
		"looping" property to loop a sound.  Instead it registers 
		a new timer and uses the duration of the clip as the timer length.
		
		If the SoundEmitter is invalid (no fifeemitter) then it attempts 
		to load it before playing it.
		
		@note This will stop any clips that use the same L{fife.SoundEmitter}.
		You cannot play the same sound more than once at a time unless you create
		the SoundEmitter with the forceUnique paramater set to True.
		
		@param clip The SoundEmitter to be played
		"""
		
		if clip.fifeemitter:
			if clip.callback:
				if clip.timer:
					clip.timer.stop()
					timer = None
					
				if clip.looping:
					repeat = 0
					def real_callback(c, e, g):
						c()
						e.stop()
						e.setGain(g)
						e.play()

					clip.callback = cbwa(real_callback, clip.callback, clip.fifeemitter, clip.gain)

				else:
					repeat = 1
					
				clip.timer = fife_timer.delayCall(clip.duration, clip.callback)
				#clip.timer.start()
				
			clip.fifeemitter.setGain(clip.gain)
			clip.fifeemitter.play()	
		else:
			clip = self.createSoundEmitter(clip.name)
			self.playClip(clip)
				
	def stopClip(self, clip):
		"""
		Stops playing the sound clip.   Note that this will stop all clips that 
		use the same FIFE emitter.
		
		@parm clip The SoundEmitter to stop.
		"""
		if clip.fifeemitter:
			clip.fifeemitter.stop()
			
		if clip.timer:
			clip.timer.stop()
			clip.timer = None

	def stopAllSounds(self):
		for emitterlist in self._loadedclips.values():
			for emitter in emitterlist:
				emitter.stop()
			
	def destroy(self):
		"""
		Releases all instances of L{fife.SoundEmitter}.  This does not free 
		the resources from the FIFE sound clip pool.
		"""
		self.stopAllSounds()
	
		for emitterlist in self._loadedclips.values():
			for emitter in emitterlist:
				self._fifesoundmanager.releaseEmitter(emitter.getID())
				emitter = None
		
		self._loadedclips.clear()

__all__ = ['SoundEmitter','SoundManager']