// Copyright (c) 2008-2010 Raynaldo (Wildicv) Rivera, Joshua (Dark_Kilauea) Jones // This file is part of the "cAudio Engine" // For conditions of distribution and use, see copyright notice in cAudio.h #include "../Headers/cAudioSource.h" #include "../Headers/cLogger.h" #include "../Headers/cFilter.h" #include "../Headers/cEffect.h" #include namespace cAudio { #ifdef CAUDIO_EFX_ENABLED cAudioSource::cAudioSource(IAudioDecoder* decoder, ALCcontext* context, cEFXFunctions* oALFunctions) : Context(context), Source(0), Decoder(decoder), Loop(false), Valid(false), EFX(oALFunctions), Filter(NULL), EffectSlotsAvailable(0), LastFilterTimeStamp(0) #else cAudioSource::cAudioSource(IAudioDecoder* decoder, ALCcontext* context) : Context(context), Source(0), Decoder(decoder), Loop(false), Valid(false) #endif { cAudioMutexBasicLock lock(Mutex); for(int i=0; igrab(); //Generates 3 buffers for the ogg file alGenBuffers(CAUDIO_SOURCE_NUM_BUFFERS, Buffers); bool state = !checkError(); if(state) { //Creates one source to be stored. alGenSources(1, &Source); state = !checkError(); } #ifdef CAUDIO_EFX_ENABLED Valid = state && (Decoder != NULL) && (Context != NULL) && (EFX != NULL); #else Valid = state && (Decoder != NULL) && (Context != NULL); #endif #ifdef CAUDIO_EFX_ENABLED int numSlots = 0; ALCdevice* device = alcGetContextsDevice(Context); alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &numSlots); EffectSlotsAvailable = (numSlots <= CAUDIO_SOURCE_MAX_EFFECT_SLOTS) ? numSlots : CAUDIO_SOURCE_MAX_EFFECT_SLOTS; #endif } cAudioSource::~cAudioSource() { cAudioMutexBasicLock lock(Mutex); if(Decoder) Decoder->drop(); #ifdef CAUDIO_EFX_ENABLED for(int i=0; idrop(); Effects[i] = NULL; } if(Filter) Filter->drop(); Filter = NULL; #endif unRegisterAllEventHandlers(); } bool cAudioSource::play() { cAudioMutexBasicLock lock(Mutex); if (!isPaused()) { int queueSize = 0; //Purges all buffers from the source alSourcei(Source, AL_BUFFER, 0); checkError(); 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); checkError(); } #ifdef CAUDIO_EFX_ENABLED updateFilter(); for(unsigned int i=0; ilogDebug("Audio Source", "Source playing."); signalEvent(ON_PLAY); return true; } bool cAudioSource::play2d(const bool& toLoop) { cAudioMutexBasicLock lock(Mutex); alSourcei(Source, AL_SOURCE_RELATIVE, true); loop(toLoop); bool state = play(); checkError(); 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(); checkError(); return state; } void cAudioSource::pause() { cAudioMutexBasicLock lock(Mutex); alSourcePause(Source); checkError(); getLogger()->logDebug("Audio Source", "Source paused."); signalEvent(ON_PAUSE); } void cAudioSource::stop() { cAudioMutexBasicLock lock(Mutex); alSourceStop(Source); //Resets the audio to the beginning Decoder->setPosition(0, false); checkError(); getLogger()->logDebug("Audio Source", "Source stopped."); signalEvent(ON_STOP); } 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); } return state; } bool cAudioSource::update() { cAudioMutexBasicLock lock(Mutex); if(!isValid() || !isPlaying()) { return false; } int processed = 0; bool active = true; #ifdef CAUDIO_EFX_ENABLED updateFilter(); for(unsigned int i=0; ilogDebug("Audio Source", "Audio source released."); signalEvent(ON_RELEASE); } const bool cAudioSource::isValid() const { return Valid; } const bool cAudioSource::isPlaying() const { ALenum state = 0; alGetSourcei(Source, AL_SOURCE_STATE, &state); return (state == AL_PLAYING); } const bool cAudioSource::isPaused() const { ALenum state = 0; alGetSourcei(Source, AL_SOURCE_STATE, &state); return (state == AL_PAUSED); } const bool cAudioSource::isStopped() const { ALenum state = 0; alGetSourcei(Source, AL_SOURCE_STATE, &state); return (state == AL_STOPPED); } const bool cAudioSource::isLooping() const { return Loop; } void cAudioSource::setPosition(const cVector3& position) { cAudioMutexBasicLock lock(Mutex); alSource3f(Source, AL_POSITION, position.x, position.y, position.z); checkError(); } void cAudioSource::setVelocity(const cVector3& velocity) { cAudioMutexBasicLock lock(Mutex); alSource3f(Source, AL_VELOCITY, velocity.x, velocity.y, velocity.z); checkError(); } void cAudioSource::setDirection(const cVector3& direction) { cAudioMutexBasicLock lock(Mutex); alSource3f(Source, AL_DIRECTION, direction.x, direction.y, direction.z); checkError(); } void cAudioSource::setRolloffFactor(const float& rolloff) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_ROLLOFF_FACTOR, rolloff); checkError(); } void cAudioSource::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); checkError(); } void cAudioSource::setMinDistance(const float& minDistance) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_REFERENCE_DISTANCE, minDistance); checkError(); } void cAudioSource::setMaxDistance(const float& maxDistance) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_MAX_DISTANCE, maxDistance); checkError(); } void cAudioSource::setPitch(const float& pitch) { cAudioMutexBasicLock lock(Mutex); alSourcef (Source, AL_PITCH, pitch); checkError(); } void cAudioSource::setVolume(const float& volume) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_GAIN, volume); checkError(); } void cAudioSource::setMinVolume(const float& minVolume) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_MIN_GAIN, minVolume); checkError(); } void cAudioSource::setMaxVolume(const float& maxVolume) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_MAX_GAIN, maxVolume); checkError(); } void cAudioSource::setInnerConeAngle(const float& innerAngle) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_CONE_INNER_ANGLE, innerAngle); checkError(); } void cAudioSource::setOuterConeAngle(const float& outerAngle) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_CONE_OUTER_ANGLE, outerAngle); checkError(); } void cAudioSource::setOuterConeVolume(const float& outerVolume) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_CONE_OUTER_GAIN, outerVolume); checkError(); } void cAudioSource::setDopplerStrength(const float& dstrength) { cAudioMutexBasicLock lock(Mutex); alSourcef(Source, AL_DOPPLER_FACTOR, dstrength); checkError(); } void cAudioSource::setDopplerVelocity(const cVector3& dvelocity) { cAudioMutexBasicLock lock(Mutex); alSource3f(Source, AL_DOPPLER_VELOCITY, dvelocity.x, dvelocity.y, dvelocity.z); checkError(); } void cAudioSource::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); checkError(); } const cVector3 cAudioSource::getPosition() const { cVector3 position; alGetSourcefv(Source, AL_POSITION, &position.x); return position; } const cVector3 cAudioSource::getVelocity() const { cVector3 velocity; alGetSourcefv(Source, AL_VELOCITY, &velocity.x); return velocity; } const cVector3 cAudioSource::getDirection() const { cVector3 direction; alGetSourcefv(Source, AL_DIRECTION, &direction.x); return direction; } const float cAudioSource::getRolloffFactor() const { float value = 0.0f; alGetSourcef(Source, AL_ROLLOFF_FACTOR, &value); return value; } const float cAudioSource::getStrength() const { float value = 0.0f; alGetSourcef(Source, AL_ROLLOFF_FACTOR, &value); float inverseStrength = 0.0f; if(value > 0.0f) inverseStrength = 1.0f / value; return inverseStrength; } const float cAudioSource::getMinDistance() const { float value = 0.0f; alGetSourcef(Source, AL_REFERENCE_DISTANCE, &value); return value; } const float cAudioSource::getMaxDistance() const { float value = 0.0f; alGetSourcef(Source, AL_MAX_DISTANCE, &value); return value; } const float cAudioSource::getPitch() const { float value = 0.0f; alGetSourcef(Source, AL_PITCH, &value); return value; } const float cAudioSource::getVolume() const { float value = 0.0f; alGetSourcef(Source, AL_GAIN, &value); return value; } const float cAudioSource::getMinVolume() const { float value = 0.0f; alGetSourcef(Source, AL_MIN_GAIN, &value); return value; } const float cAudioSource::getMaxVolume() const { float value = 0.0f; alGetSourcef(Source, AL_MAX_GAIN, &value); return value; } const float cAudioSource::getInnerConeAngle() const { float value = 0.0f; alGetSourcef(Source, AL_CONE_INNER_ANGLE, &value); return value; } const float cAudioSource::getOuterConeAngle() const { float value = 0.0f; alGetSourcef(Source, AL_CONE_OUTER_ANGLE, &value); return value; } const float cAudioSource::getOuterConeVolume() const { float value = 0.0f; alGetSourcef(Source, AL_CONE_OUTER_GAIN, &value); return value; } const float cAudioSource::getDopplerStrength() const { float value = 0.0f; alGetSourcef(Source, AL_DOPPLER_FACTOR, &value); return value; } const cVector3 cAudioSource::getDopplerVelocity() const { cVector3 velocity; alGetSourcefv(Source, AL_DOPPLER_VELOCITY, &velocity.x); return velocity; } #ifdef CAUDIO_EFX_ENABLED 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); while (queued--) { ALuint buffer; alSourceUnqueueBuffers(Source, 1, &buffer); checkError(); } } bool cAudioSource::checkError() { int error = alGetError(); const char* errorString; if (error != AL_NO_ERROR) { errorString = alGetString(error); if(error == AL_OUT_OF_MEMORY) getLogger()->logCritical("Audio Source", "OpenAL Error: %s.", errorString); else getLogger()->logError("Audio Source", "OpenAL Error: %s.", errorString); return true; } return false; } bool cAudioSource::stream(ALuint buffer) { if(Decoder) { //stores the caculated 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]; int actualread = Decoder->readAudioData(tempbuffer2, CAUDIO_SOURCE_BUFFER_SIZE-totalread); 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) 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()); checkError(); return true; } return false; } ALenum cAudioSource::convertAudioFormatEnum(AudioFormats format) { switch(format) { case EAF_8BIT_MONO: return AL_FORMAT_MONO8; case EAF_16BIT_MONO: return AL_FORMAT_MONO16; case EAF_8BIT_STEREO: return AL_FORMAT_STEREO8; case EAF_16BIT_STEREO: return AL_FORMAT_STEREO16; default: return AL_FORMAT_MONO8; }; } #ifdef CAUDIO_EFX_ENABLED void cAudioSource::updateFilter(bool remove) { if(!remove) { if(Filter && Filter->isValid()) { if(LastFilterTimeStamp != Filter->getLastUpdated()) { LastFilterTimeStamp = Filter->getLastUpdated(); cFilter* theFilter = static_cast(Filter); if(theFilter) { alSourcei(Source, AL_DIRECT_FILTER, theFilter->getOpenALFilter()); checkError(); return; } } return; } } alSourcei(Source, AL_DIRECT_FILTER, AL_FILTER_NULL); checkError(); } 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(Effects[slot]); if(theEffect) { ALuint filterID = AL_FILTER_NULL; cFilter* theFilter = static_cast(theEffect->getFilter()); if(theFilter) { filterID = theFilter->getOpenALFilter(); } alSource3i(Source, AL_AUXILIARY_SEND_FILTER, theEffect->getOpenALEffectSlot(), slot, filterID); checkError(); return; } } return; } } alSource3i(Source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, slot, AL_FILTER_NULL); checkError(); } } #endif void cAudioSource::registerEventHandler(ISourceEventHandler* handler) { if(handler) { eventHandlerList.push_back(handler); } } void cAudioSource::unRegisterEventHandler(ISourceEventHandler* handler) { if(handler) { eventHandlerList.remove(handler); } } void cAudioSource::unRegisterAllEventHandlers() { eventHandlerList.clear(); } void cAudioSource::signalEvent(Events sevent) { std::list::iterator it = eventHandlerList.begin(); if(it != eventHandlerList.end()){ switch(sevent){ case ON_UPDATE: for(it; it != eventHandlerList.end(); it++){ (*it)->onUpdate(); } break; case ON_RELEASE: for(it; it != eventHandlerList.end(); it++){ (*it)->onRelease(); } break; case ON_PLAY: for(it; it != eventHandlerList.end(); it++){ (*it)->onPlay(); } break; case ON_PAUSE: for(it; it != eventHandlerList.end(); it++){ (*it)->onPause(); } break; case ON_STOP: for(it; it != eventHandlerList.end(); it++){ (*it)->onStop(); } break; } } } }