// Copyright (c) 2008-2011 Raynaldo (Wildicv) Rivera, Joshua (Dark_Kilauea) Jones, Murat (wolfmanfx) Sari
// This file is part of the "cAudio Engine"
// For conditions of distribution and use, see copyright notice in cAudio.h

#include "cAudioSource.h"
#include "cAudio.h"
#include "cLogger.h"
#include "cFilter.h"
#include "cEffect.h"
#include "cAudioSleep.h"
#include <string.h>
#include <algorithm>

#if CAUDIO_EFX_ENABLED == 1
#include "cOpenALDeviceContext.h"
#endif

namespace
{
    // return buffer length in seconds
    ALfloat GetBufferLength(ALuint buffer)
    {
        ALint size, bits, channels, freq;

        alGetBufferi(buffer, AL_SIZE, &size);
        alGetBufferi(buffer, AL_BITS, &bits);
        alGetBufferi(buffer, AL_CHANNELS, &channels);
        alGetBufferi(buffer, AL_FREQUENCY, &freq);
        if(alGetError() != AL_NO_ERROR || !bits || !channels || !freq)
            return -1.0f;

        return (ALfloat)((ALuint)size/channels/(bits/8)) / (ALfloat)freq;
    }

    ALint GetBufferSize(ALuint buffer)
    {
        ALint size;
        alGetBufferi(buffer, AL_SIZE, &size);
        return size;
    }
}

namespace cAudio
{

#if CAUDIO_EFX_ENABLED == 1
    cAudioSource::cAudioSource(IAudioDecoder* decoder, IAudioDeviceContext* context, cEFXFunctions* oALFunctions)
		: cAudioSourceBase(context), Decoder(decoder), Loop(false), Valid(false),
		EFX(oALFunctions), Filter(NULL), EffectSlotsAvailable(0), LastFilterTimeStamp(0)
#else
	cAudioSource::cAudioSource(IAudioDecoder* decoder, IAudioDeviceContext* context)
        : cAudioSourceBase(context), Decoder(decoder), Loop(false), Valid(false)
#endif
    {
		cAudioMutexBasicLock lock(Mutex);

        BufferPosition = 0;
        BufferTime = 0;
		for(int i=0; i<CAUDIO_SOURCE_NUM_BUFFERS; ++i) {
			Buffers[i] = 0;
        }

#if CAUDIO_EFX_ENABLED == 1
		for(int i=0; i<CAUDIO_SOURCE_MAX_EFFECT_SLOTS; ++i)
			Effects[i] = NULL;

		for(int i=0; i<CAUDIO_SOURCE_MAX_EFFECT_SLOTS; ++i)
			LastEffectTimeStamp[i] = 0;
#endif

		if(Decoder)
			Decoder->grab();

        ALboolean state = alIsSource(Source);

        if (state)
        {
            //Generates 3 buffers for the ogg file
            alGenBuffers(CAUDIO_SOURCE_NUM_BUFFERS, Buffers);
            state = !checkALError();
        }

#if CAUDIO_EFX_ENABLED == 1
		Valid = state && (Decoder != NULL) && (Context != NULL) && (EFX != NULL);

		int numSlots = 0;
		ALCdevice* device = alcGetContextsDevice(((cOpenALDeviceContext*)Context)->getOpenALContext());
		alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &numSlots);

		EffectSlotsAvailable = (numSlots <= CAUDIO_SOURCE_MAX_EFFECT_SLOTS) ? numSlots : CAUDIO_SOURCE_MAX_EFFECT_SLOTS;
#else
		Valid = state && (Decoder != NULL) && (Context != NULL);
#endif
    }

    cAudioSource::~cAudioSource()
    {
		cAudioMutexBasicLock lock(Mutex);

#if CAUDIO_EFX_ENABLED == 1
		for(int i=0; i<CAUDIO_SOURCE_MAX_EFFECT_SLOTS; ++i)
		{
			if(Effects[i])
				Effects[i]->drop();
			Effects[i] = NULL;
		}

		if(Filter)
			Filter->drop();
		Filter = NULL;
#endif
		
		//Stops the audio Source
		alSourceStop(Source);
        checkALError();
		empty();
        alSourcei(Source, AL_BUFFER, 0);
		//deletes the last filled buffer
		alDeleteBuffers(CAUDIO_SOURCE_NUM_BUFFERS, Buffers);
		checkALError();

		if(Decoder) {
			Decoder->drop();
            Decoder = NULL;
        }
    }

    cAudioSourceBase::cAudioSourceBase(IAudioDeviceContext* context) : 
        Context(context), Volume(1.f), Source(0) 
    {
        alGenSources(1, &Source);
        checkALError();
        setVolume(Volume);
    }

    cAudioSourceBase::~cAudioSourceBase()
    {
        alSourceStop(Source);
        checkALError();
		alDeleteSources(1, &Source);
        checkALError();

		getLogger()->logDebug("Audio Source", "Audio source released.");
		signalEvent(ON_RELEASE);
    }


	bool cAudioSource::drop()
	{
		--RefCount;
		if (RefCount == 0)
		{
			Context->getAudioManager()->release(this);
			return true;
		}
		return false;
	}

	bool cAudioSource::play()
	{
		cAudioMutexBasicLock lock(Mutex);
		if (!isPaused())
        {
            int queueSize = 0;
			//Purges all buffers from the source
			alSourcei(Source, AL_BUFFER, 0);
            BufferPosition = Decoder->getCurrentPosition();
            BufferTime = Decoder->getCurrentTime();
			checkALError();
            for(int u = 0; u < CAUDIO_SOURCE_NUM_BUFFERS; u++)
            {
                int val = stream(Buffers[u]);

                if(val < 0)
                {
                    return false;
                }
                else if(val > 0)
                    ++queueSize;
            }
            //Stores the sources 3 buffers to be used in the queue
            alSourceQueueBuffers(Source, queueSize, Buffers);
			checkALError();
        }
#if CAUDIO_EFX_ENABLED == 1
		updateFilter();
		for(unsigned int i=0; i<CAUDIO_SOURCE_MAX_EFFECT_SLOTS; ++i)
			updateEffect(i);
#endif
        alSourcePlay(Source);
		checkALError();
		getLogger()->logDebug("Audio Source", "Source playing.");
		signalEvent(ON_PLAY);
		oldState = AL_PLAYING;
        return true;
    }

	bool cAudioSource::play2d(const bool& toLoop)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcei(Source, AL_SOURCE_RELATIVE, true);
        loop(toLoop);
        bool state = play();
		checkALError();
		return state;
    }

	bool cAudioSource::play3d(const cVector3& position, const float& soundstr, const bool& toLoop)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcei(Source, AL_SOURCE_RELATIVE, false);
        setPosition(position);
        setStrength(soundstr);
        loop(toLoop);
        bool state = play();
		checkALError();
		return state;
    }

	void cAudioSource::pause()
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcePause(Source);
		checkALError();
		getLogger()->logDebug("Audio Source", "Source paused.");
		signalEvent(ON_PAUSE);
		oldState = AL_PAUSED;
    }

	void cAudioSource::stop()
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourceStop(Source);

		//INFO:FIXED EXTREME SLOWDOWN ON IPHONE
		int queued = 0;
		alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued);
		while ( queued-- )
		{
			ALuint buffer;
			alSourceUnqueueBuffers(Source, 1, &buffer);
		}

		//Resets the audio to the beginning
		Decoder->setPosition(0, false);
		checkALError();
		getLogger()->logDebug("Audio Source", "Source stopped.");
		signalEvent(ON_STOP);
		oldState = AL_STOPPED;
    }

	void cAudioSource::loop(const bool& loop)
	{
		cAudioMutexBasicLock lock(Mutex);
        Loop = loop;
    }

	bool cAudioSource::seek(const float& seconds, bool relative)
	{
		bool state = false;
		cAudioMutexBasicLock lock(Mutex);
        if(Decoder->isSeekingSupported())
        {
			state = Decoder->seek(seconds, relative);

            BufferPosition = Decoder->getCurrentPosition();
            BufferTime = Decoder->getCurrentTime();

            int queued = 0;
            alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued);
            if (queued) {
                BufferPosition -= queued * GetBufferSize(Buffers[0]);
                BufferTime -= queued * GetBufferLength(Buffers[0]);
            }
        }
		return state;
    }

	float cAudioSource::getTotalAudioTime()
	{
		return Decoder->getTotalTime();
	}

	int cAudioSource::getTotalAudioSize()
	{
		return Decoder->getTotalSize();
	}

	int cAudioSource::getCompressedAudioSize()
	{
		return Decoder->getCompressedSize();
	}

	float cAudioSource::getCurrentAudioTime()
	{
        float time = -1;
        alGetSourcef(Source, AL_SEC_OFFSET, &time);
        return BufferTime + time;
	}

	int cAudioSource::getCurrentAudioPosition()
	{
        int offset = -1;
        alGetSourcei(Source, AL_BYTE_OFFSET, &offset);
		return BufferPosition + offset;
	}

	int cAudioSource::getCurrentCompressedAudioPosition()
	{
		return Decoder->getCurrentCompressedPosition();
	}

	bool cAudioSource::update()
	{
		cAudioMutexBasicLock lock(Mutex);

		int processed = 0;
		bool active = true;
        if(isValid() || isPlaying())
		{
#if CAUDIO_EFX_ENABLED == 1
			updateFilter();
			for(unsigned int i=0; i<CAUDIO_SOURCE_MAX_EFFECT_SLOTS; ++i)
				updateEffect(i);
#endif

			//gets the sound source processed buffers
			alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed);
            checkALError();
            
			//while there is more data refill buffers with audio data.
			while (processed--)
			{
				ALuint buffer;
				alSourceUnqueueBuffers(Source, 1, &buffer);
                BufferPosition += GetBufferSize(buffer);
                BufferTime += GetBufferLength(buffer);

				if (checkALError()) 
				{
					processed++;
					cAudioSleep(1);
					continue;
				}

				active = stream(buffer);

				//if more in stream continue playing.
				if(active)
				{
					alSourceQueueBuffers(Source, 1, &buffer);
				}

				if (checkALError()) 
				{
					processed++;
					cAudioSleep(1);
					continue;
				}
			}

			signalEvent(ON_UPDATE);
		}

		ALenum state;
		alGetSourcei(Source, AL_SOURCE_STATE, &state);
        checkALError();
		if(state == AL_STOPPED && oldState != state)
		{
			//Resets the audio to the beginning
			Decoder->setPosition(0, false);
			getLogger()->logDebug("Audio Source", "Source stopped.");
			signalEvent(ON_STOP);
			oldState = state;
		}

		return active;
    }

	bool cAudioSource::isValid() const
	{
        return Valid;
	}

	bool cAudioSourceBase::isPlaying() const
	{
		ALenum state = 0;
        alGetSourcei(Source, AL_SOURCE_STATE, &state);
        checkALError();
        return (state == AL_PLAYING);
    }

	bool cAudioSourceBase::isPaused() const
	{
		ALenum state = 0;
        alGetSourcei(Source, AL_SOURCE_STATE, &state);
        checkALError();
        return (state == AL_PAUSED);
    }

	bool cAudioSourceBase::isStopped() const
	{
		ALenum state = 0;
        alGetSourcei(Source, AL_SOURCE_STATE, &state);
        checkALError();
		return (state == AL_STOPPED);
    }

	bool cAudioSource::isLooping() const
	{
		return Loop;
	}

	void cAudioSourceBase::setPosition(const cVector3& position)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSource3f(Source, AL_POSITION, position.x, position.y, position.z);
		checkALError();
    }

	void cAudioSourceBase::setVelocity(const cVector3& velocity)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSource3f(Source, AL_VELOCITY, velocity.x, velocity.y, velocity.z);
		checkALError();
    }

	void cAudioSourceBase::setDirection(const cVector3& direction)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSource3f(Source, AL_DIRECTION, direction.x, direction.y, direction.z);
		checkALError();
    }

	void cAudioSourceBase::setRolloffFactor(const float& rolloff)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_ROLLOFF_FACTOR, rolloff);
		checkALError();
    }

	void cAudioSourceBase::setStrength(const float& soundstrength)
	{
		float inverseStrength = 0.0f;
		if(soundstrength > 0.0f)
			inverseStrength = 1.0f / soundstrength;

		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_ROLLOFF_FACTOR, inverseStrength);
		checkALError();
    }

	void cAudioSourceBase::setMinDistance(const float& minDistance)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_REFERENCE_DISTANCE, minDistance);
		checkALError();
	}

	void cAudioSourceBase::setMaxAttenuationDistance(const float& maxDistance)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_MAX_DISTANCE, maxDistance);
		checkALError();
	}

	void cAudioSourceBase::setPitch(const float& pitch)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef (Source, AL_PITCH, pitch);
		checkALError();
    }

	void cAudioSourceBase::setVolume(const float& volume)
	{
		cAudioMutexBasicLock lock(Mutex);
		Volume = volume;
        alSourcef(Source, AL_GAIN, Volume * Context->getAudioManager()->getMasterVolume());
		checkALError();
    }

	void cAudioSourceBase::setMinVolume(const float& minVolume)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_MIN_GAIN, minVolume);
		checkALError();
	}

	void cAudioSourceBase::setMaxVolume(const float& maxVolume)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_MAX_GAIN, maxVolume);
		checkALError();
	}

	void cAudioSourceBase::setInnerConeAngle(const float& innerAngle)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_CONE_INNER_ANGLE, innerAngle);
		checkALError();
	}

	void cAudioSourceBase::setOuterConeAngle(const float& outerAngle)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_CONE_OUTER_ANGLE, outerAngle);
		checkALError();
	}

	void cAudioSourceBase::setOuterConeVolume(const float& outerVolume)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_CONE_OUTER_GAIN, outerVolume);
		checkALError();
	}

	void cAudioSourceBase::setDopplerStrength(const float& dstrength)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSourcef(Source, AL_DOPPLER_FACTOR, dstrength);
		checkALError();
    }

	void cAudioSourceBase::setDopplerVelocity(const cVector3& dvelocity)
	{
		cAudioMutexBasicLock lock(Mutex);
        alSource3f(Source, AL_DOPPLER_VELOCITY, dvelocity.x, dvelocity.y, dvelocity.z);
		checkALError();
    }

	void cAudioSourceBase::move(const cVector3& position)
	{
		cAudioMutexBasicLock lock(Mutex);
		cVector3 oldPos = getPosition();
		cVector3 velocity = position - oldPos;

        alSource3f(Source, AL_VELOCITY, velocity.x, velocity.y, velocity.z);
		alSource3f(Source, AL_POSITION, position.x, position.y, position.z);
		checkALError();
	}

	cVector3 cAudioSourceBase::getPosition() const
	{
		cVector3 position;
		alGetSourcefv(Source, AL_POSITION, &position.x);
        checkALError();
		return position;
	}

	cVector3 cAudioSourceBase::getVelocity() const
	{
		cVector3 velocity;
		alGetSourcefv(Source, AL_VELOCITY, &velocity.x);
		return velocity;
	}

	cVector3 cAudioSourceBase::getDirection() const
	{
		cVector3 direction;
		alGetSourcefv(Source, AL_DIRECTION, &direction.x);
        checkALError();
		return direction;
	}

	float cAudioSourceBase::getRolloffFactor() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_ROLLOFF_FACTOR, &value);
        checkALError();
		return value;
	}

	float cAudioSourceBase::getStrength() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_ROLLOFF_FACTOR, &value);
        checkALError();
        
		float inverseStrength = 0.0f;
		if(value > 0.0f)
			inverseStrength = 1.0f / value;

		return inverseStrength;
	}

	float cAudioSourceBase::getMinDistance() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_REFERENCE_DISTANCE, &value);
        checkALError();
		return value;
	}

	float cAudioSourceBase::getMaxDistance() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_MAX_DISTANCE, &value);
        checkALError();
		return value;
	}

    bool cAudioSourceBase::isRelative() const
    {
        int relative = 0;
        alGetSourcei(Source, AL_SOURCE_RELATIVE, &relative);
        return relative;
    }

    float cAudioSourceBase::calculateGain() const
    {
        // OpenAL Inverse Distance Clamped Model
        // distance = max(distance,AL_REFERENCE_DISTANCE);
        // distance = min(distance,AL_MAX_DISTANCE);
        // gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE +
        //                                 AL_ROLLOFF_FACTOR *
        //                                 (distance – AL_REFERENCE_DISTANCE));

        cVector3 lpos = Context->getAudioManager()->getListener()->getPosition();
        cVector3 pos  = getPosition();

        float refDist =  getMinDistance();
        float dist = 0.f;
        dist = isRelative() ? pos.length() : (pos - lpos).length();
        dist = std::max(dist, refDist);
        dist = std::min(dist, getMaxDistance());
        float gain = refDist / (refDist + getRolloffFactor() * (dist - refDist));
        return gain * getVolume();
    }

	float cAudioSourceBase::getPitch() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_PITCH, &value);
        checkALError();
		return value;
	}

	float cAudioSourceBase::getVolume() const
	{
		return Volume;
	}

	float cAudioSourceBase::getMinVolume() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_MIN_GAIN, &value);
        checkALError();
		return value;
	}

	float cAudioSourceBase::getMaxVolume() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_MAX_GAIN, &value);
        checkALError();
		return value;
	}

	float cAudioSourceBase::getInnerConeAngle() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_CONE_INNER_ANGLE, &value);
        checkALError();
		return value;
	}

	float cAudioSourceBase::getOuterConeAngle() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_CONE_OUTER_ANGLE, &value);
        checkALError();
		return value;
	}

	float cAudioSourceBase::getOuterConeVolume() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_CONE_OUTER_GAIN, &value);
        checkALError();
		return value;
	}

	float cAudioSourceBase::getDopplerStrength() const
	{
		float value = 0.0f;
		alGetSourcef(Source, AL_DOPPLER_FACTOR, &value);
        checkALError();
		return value;
	}

	cVector3 cAudioSourceBase::getDopplerVelocity() const
	{
		cVector3 velocity;
		alGetSourcefv(Source, AL_DOPPLER_VELOCITY, &velocity.x);
        checkALError();
		return velocity;
	}

#if CAUDIO_EFX_ENABLED == 1
	unsigned int cAudioSource::getNumEffectSlotsAvailable() const
	{
		return EffectSlotsAvailable;
	}

	bool cAudioSource::attachEffect(unsigned int slot, IEffect* effect)
	{
		cAudioMutexBasicLock lock(Mutex);
		if(slot < EffectSlotsAvailable)
		{
			Effects[slot] = effect;

			if(Effects[slot])
				Effects[slot]->grab();

			updateEffect(slot);
			return true;
		}
		return false;
	}

	void cAudioSource::removeEffect(unsigned int slot)
	{
		cAudioMutexBasicLock lock(Mutex);
		if(slot < EffectSlotsAvailable)
		{
			if(Effects[slot])
				Effects[slot]->drop();

			Effects[slot] = NULL;
			LastEffectTimeStamp[slot] = 0;
			updateEffect(slot, true);
		}
	}

	bool cAudioSource::attachFilter(IFilter* filter)
	{
		cAudioMutexBasicLock lock(Mutex);
		Filter = filter;

		if(Filter)
			Filter->grab();

		updateFilter();
		return true;
	}

	void cAudioSource::removeFilter()
	{
		cAudioMutexBasicLock lock(Mutex);
		if(Filter)
			Filter->drop();
		Filter = NULL;
		LastFilterTimeStamp = 0;
		updateFilter(true);
	}
#endif

    void cAudioSource::empty()
    {
        int queued = 0;
        alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued);
        checkALError();
        
        while (queued--)
        {
            ALuint buffer;
            alSourceUnqueueBuffers(Source, 1, &buffer);
			checkALError();
        }
    }

    bool cAudioSource::stream(ALuint buffer)
    {
        if(!Decoder)
            return false;
        
        //stores the calculated data into buffer that is passed to output.
        size_t totalread = 0;
        unsigned int errorcount = 0;
        char tempbuffer[CAUDIO_SOURCE_BUFFER_SIZE];
        while( totalread < CAUDIO_SOURCE_BUFFER_SIZE )
        {
            char tempbuffer2[CAUDIO_SOURCE_BUFFER_SIZE];
            Mutex.unlock();    // this can take a long time
            int actualread = Decoder->readAudioData(tempbuffer2, CAUDIO_SOURCE_BUFFER_SIZE-totalread);
            Mutex.lock();
            
            if(actualread > 0)
            {
                memcpy(tempbuffer+totalread,tempbuffer2,actualread);
                totalread += actualread;
            }
            if(actualread < 0)
            {
                ++errorcount;
                getLogger()->logDebug("Audio Source", "Decoder returned an error: %i (%i of 3)", actualread, errorcount);
                if(errorcount >= 3)
                {
                    stop();
                    break;
                }
            }
            if(actualread == 0)
            {
                if(isLooping())
                {
                    //If we are to loop, set to the beginning and reload from the start
                    Decoder->setPosition(0, false);
                    getLogger()->logDebug("Audio Source", "Buffer looping.");
                }
                else
                    break;
            }
        }

        //Second check, in case looping is not enabled, we will return false for end of stream
        if(totalread == 0)
        {
            return false;
        }
        getLogger()->logDebug("Audio Source", "Buffered %i bytes of data into buffer %i at %i hz.", totalread, buffer, Decoder->getFrequency());
        alBufferData(buffer, convertAudioFormatEnum(Decoder->getFormat()), tempbuffer, totalread, Decoder->getFrequency());
        checkALError();
        return true;
    }

#if CAUDIO_EFX_ENABLED == 1
	void cAudioSource::updateFilter(bool remove)
	{
		if(!remove)
		{
			if(Filter && Filter->isValid())
			{
				if(LastFilterTimeStamp != Filter->getLastUpdated())
				{
					LastFilterTimeStamp = Filter->getLastUpdated();
					cFilter* theFilter = static_cast<cFilter*>(Filter);
					if(theFilter)
					{
						alSourcei(Source, AL_DIRECT_FILTER, theFilter->getOpenALFilter());
						checkALError();
						return;
					}
				}
				return;
			}
		}
		alSourcei(Source, AL_DIRECT_FILTER, AL_FILTER_NULL);
		checkALError();
	}

	void cAudioSource::updateEffect(unsigned int slot, bool remove)
	{
		if(slot < EffectSlotsAvailable)
		{
			if(!remove)
			{
				if(Effects[slot] && Effects[slot]->isValid())
				{
					if(LastEffectTimeStamp[slot] != Effects[slot]->getLastUpdated())
					{
						LastEffectTimeStamp[slot] = Effects[slot]->getLastUpdated();
						cEffect* theEffect = static_cast<cEffect*>(Effects[slot]);
						if(theEffect)
						{
							ALuint filterID = AL_FILTER_NULL;
							cFilter* theFilter = static_cast<cFilter*>(theEffect->getFilter());
							if(theFilter)
							{
								filterID = theFilter->getOpenALFilter();
							}
							alSource3i(Source, AL_AUXILIARY_SEND_FILTER, theEffect->getOpenALEffectSlot(), slot, filterID);
							checkALError();
							return;
						}
					}
					return;
				}
			}
			alSource3i(Source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, slot, AL_FILTER_NULL);
			checkALError();
		}
	}
#endif

	void cAudioSourceBase::registerEventHandler(ISourceEventHandler* handler)
	{
		if(handler)
		{
		    cAudioMutexBasicLock lock(Mutex);
			eventHandlerList.push_back(handler);
		}
	}

	void cAudioSourceBase::unRegisterEventHandler(ISourceEventHandler* handler)
	{
		if(handler)
		{
		    cAudioMutexBasicLock lock(Mutex);
            for(int i=0; i<eventHandlerList.size(); i++) {
                if(eventHandlerList[i] == handler)
                    eventHandlerList.erase(eventHandlerList.begin() + i);
            }
		}
	}

	void cAudioSourceBase::unRegisterAllEventHandlers()
	{
	    cAudioMutexBasicLock lock(Mutex);
		eventHandlerList.clear();
	}

	void cAudioSourceBase::signalEvent(Events sevent)
	{
		cAudioMutexBasicLock lock(Mutex);
		if(eventHandlerList.empty())
            return;

        size_t size = eventHandlerList.size();

        for(int i=0; i<size; )
        {
            ISourceEventHandler *handler = eventHandlerList[i];

            switch(sevent)
            {
            case ON_UPDATE:  handler->onUpdate(); break;
            case ON_RELEASE: handler->onRelease(); break;
            case ON_PLAY:    handler->onPlay(); break;
            case ON_PAUSE:   handler->onPause(); break;
            case ON_STOP:    handler->onStop(); break;
            }
     
            // handler may have unregistered itself
            if(size == eventHandlerList.size()) {
                i++;
            } else {
                size = eventHandlerList.size();
            }
        }
    }
}
