// 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/cAudioManager.h" #include "../Headers/cFileSource.h" #include "../Headers/cMemorySource.h" #include "../Headers/cUtils.h" #include "../Headers/cOggAudioDecoderFactory.h" #include "../Headers/cWavAudioDecoderFactory.h" #include "../Headers/cRawAudioDecoderFactory.h" #include "../Headers/cThread.h" #include "../include/cAudioSleep.h" #include "../Headers/cLogger.h" #include "../Headers/cPluginManager.h" #include "../include/cAudioPlatform.h" #include "../Headers/cFileSourceFactory.h" #include #include #include #ifdef CAUDIO_EFX_ENABLED #ifdef CAUDIO_PLATFORM_WIN #include #include #include #endif #ifdef CAUDIO_PLATFORM_LINUX #include #endif #endif namespace cAudio { static bool RunAudioManagerThread(false); #ifdef CAUDIO_COMPILE_WITH_OGG_DECODER static cOggAudioDecoderFactory OggDecoderFactory; #endif #ifdef CAUDIO_COMPILE_WITH_WAV_DECODER static cWavAudioDecoderFactory WavDecoderFactory; #endif static cRawAudioDecoderFactory RawDecoderFactory; static cFileSourceFactory FileSourceFactory; //Note: OpenAL is threadsafe, so a mutex only needs to protect the class state #ifdef CAUDIO_USE_INTERNAL_THREAD static cAudioMutex AudioManagerObjectsMutex; static std::set AudioManagerObjects; CAUDIO_DECLARE_THREAD_FUNCTION(AudioManagerUpdateThread) { while(RunAudioManagerThread) { AudioManagerObjectsMutex.lock(); std::set::iterator it; for ( it=AudioManagerObjects.begin() ; it != AudioManagerObjects.end(); it++ ) { (*it)->update(); } AudioManagerObjectsMutex.unlock(); cAudioSleep(1); } return 0; } #endif //!Initialize the listener,openal,and mikmod bool cAudioManager::initialize(const char* deviceName, int outputFrequency, int eaxEffectSlots) { cAudioMutexBasicLock lock(Mutex); if(Initialized) return false; //Stores the context attributes (MAX of 4, with 2 zeros to terminate) ALint attribs[6] = { 0 }; unsigned int currentAttrib = 0; if(outputFrequency > 0) { attribs[currentAttrib++] = ALC_FREQUENCY; attribs[currentAttrib++] = outputFrequency; } #ifdef CAUDIO_EFX_ENABLED if(eaxEffectSlots > 0) { attribs[currentAttrib++] = ALC_MAX_AUXILIARY_SENDS; attribs[currentAttrib++] = eaxEffectSlots; } #endif //Create a new device Device = alcOpenDevice(deviceName); //Check if device can be created if (Device == NULL) { getLogger()->logError("AudioManager", "Failed to Create OpenAL Device."); checkError(); return false; } Context = alcCreateContext(Device, attribs); if (Context == NULL) { getLogger()->logError("AudioManager", "Failed to Create OpenAL Context."); checkError(); alcCloseDevice(Device); Device = NULL; return false; } if(!alcMakeContextCurrent(Context)) { getLogger()->logError("AudioManager", "Failed to make OpenAL Context current."); checkError(); alcDestroyContext(Context); alcCloseDevice(Device); Context = NULL; Device = NULL; return false; } #ifdef CAUDIO_EFX_ENABLED initEffects.getEFXInterface()->Mutex.lock(); EFXSupported = initEffects.getEFXInterface()->CheckEFXSupport(Device); initEffects.getEFXInterface()->Mutex.unlock(); initEffects.checkEFXSupportDetails(); #endif getLogger()->logInfo("AudioManager", "OpenAL Version: %s", alGetString(AL_VERSION)); getLogger()->logInfo("AudioManager", "Vendor: %s", alGetString(AL_VENDOR)); getLogger()->logInfo("AudioManager", "Renderer: %s", alGetString(AL_RENDERER)); #ifdef CAUDIO_EFX_ENABLED if(EFXSupported) { int EFXMajorVersion = 0; int EFXMinorVersion = 0; alcGetIntegerv(Device, ALC_EFX_MAJOR_VERSION, 1, &EFXMajorVersion); alcGetIntegerv(Device, ALC_EFX_MINOR_VERSION, 1, &EFXMinorVersion); getLogger()->logInfo("AudioManager", "EFX Version: %i.%i", EFXMajorVersion, EFXMinorVersion); getLogger()->logInfo("AudioManager", "EFX supported and enabled."); } else { getLogger()->logWarning("AudioManager", "EFX is not supported, EFX disabled."); } #endif getLogger()->logInfo("AudioManager", "Supported Extensions: %s", alGetString(AL_EXTENSIONS)); #ifdef CAUDIO_COMPILE_WITH_OGG_DECODER registerAudioDecoder(&OggDecoderFactory, "ogg"); #endif #ifdef CAUDIO_COMPILE_WITH_WAV_DECODER registerAudioDecoder(&WavDecoderFactory, "wav"); #endif registerAudioDecoder(&RawDecoderFactory, "raw"); registerDataSource(&FileSourceFactory, "FileSystem", 0); Initialized = true; return true; } //!create a sound source IAudioSource* cAudioManager::create(const char* name, const char* filename, bool stream) { cAudioMutexBasicLock lock(Mutex); std::string audioName = safeCStr(name); std::string path = safeCStr(filename); std::string ext = getExt(path); IAudioDecoderFactory* factory = getAudioDecoderFactory(ext.c_str()); if(factory) { for(int i=0; iCreateDataSource(filename, stream); if(source && source->isValid()) { IAudioDecoder* decoder = factory->CreateAudioDecoder(source); source->drop(); if(decoder && decoder->isValid()) { #ifdef CAUDIO_EFX_ENABLED IAudioSource* audio = new cAudioSource(decoder, Context, initEffects.getEFXInterface()); #else IAudioSource* audio = new cAudioSource(decoder, Context); #endif decoder->drop(); if(audio && audio->isValid()) { if(!audioName.empty()) audioIndex[audioName] = audio; audioSources.push_back(audio); getLogger()->logInfo("AudioManager", "Audio Source (%s) created from file %s from Data Source %s.", audioName.c_str(), path.c_str(), dataSourcePriorityList[i].second.c_str()); return audio; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Error creating audio source.", audioName.c_str()); audio->drop(); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Audio data could not be decoded by (.%s) decoder.", audioName.c_str(), ext.c_str()); decoder->drop(); return NULL; } if(source) source->drop(); } } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): File could not be found (.%s).", audioName.c_str(), path.c_str()); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): No decoder could be found for (.%s).", audioName.c_str(), ext.c_str()); return NULL; } //!Loads the ogg file from memory *virtual file systems* IAudioSource* cAudioManager::createFromMemory(const char* name, const char* data, size_t length, const char* extension) { cAudioMutexBasicLock lock(Mutex); std::string audioName = safeCStr(name); std::string ext = safeCStr(extension); IAudioDecoderFactory* factory = getAudioDecoderFactory(ext.c_str()); if(factory) { cMemorySource* source = new cMemorySource(data, length, true); if(source) { if(source->isValid()) { IAudioDecoder* decoder = factory->CreateAudioDecoder(source); source->drop(); if(decoder) { if(decoder->isValid()) { #ifdef CAUDIO_EFX_ENABLED IAudioSource* audio = new cAudioSource(decoder, Context, initEffects.getEFXInterface()); #else IAudioSource* audio = new cAudioSource(decoder, Context); #endif decoder->drop(); if(audio) { if(audio->isValid()) { if(!audioName.empty()) audioIndex[audioName] = audio; audioSources.push_back(audio); getLogger()->logInfo("AudioManager", "Audio Source (%s) successfully created from memory.", audioName.c_str()); return audio; } audio->drop(); getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Error creating audio source.", audioName.c_str()); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Could not allocate enough memory.", audioName.c_str()); return NULL; } decoder->drop(); getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Audio data could not be decoded by (.%s) decoder.", audioName.c_str(), ext.c_str()); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Could not allocate enough memory for decoder.", audioName.c_str()); return NULL; } source->drop(); getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Audio data is corrupt.", audioName.c_str()); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Could not allocate enough memory.", audioName.c_str()); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Codec (.%s) is not supported.", audioName.c_str(), ext.c_str()); return NULL; } IAudioSource* cAudioManager::createFromRaw(const char* name, const char* data, size_t length, unsigned int frequency, AudioFormats format) { cAudioMutexBasicLock lock(Mutex); std::string audioName = safeCStr(name); IAudioDecoderFactory* factory = getAudioDecoderFactory("raw"); if(factory) { cMemorySource* source = new cMemorySource(data, length, true); if(source) { if(source->isValid()) { IAudioDecoder* decoder = ((cRawAudioDecoderFactory*)factory)->CreateAudioDecoder(source, frequency, format); source->drop(); if(decoder) { if(decoder->isValid()) { #ifdef CAUDIO_EFX_ENABLED IAudioSource* audio = new cAudioSource(decoder, Context, initEffects.getEFXInterface()); #else IAudioSource* audio = new cAudioSource(decoder, Context); #endif decoder->drop(); if(audio) { if(audio->isValid()) { if(!audioName.empty()) audioIndex[audioName] = audio; audioSources.push_back(audio); getLogger()->logInfo("AudioManager", "Audio Source (%s) successfully created from raw data.", audioName.c_str()); return audio; } audio->drop(); getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Error creating audio source.", audioName.c_str()); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Could not allocate enough memory.", audioName.c_str()); return NULL; } decoder->drop(); getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Audio data could not be decoded by (.%s) decoder.", audioName.c_str(), "raw"); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Could not allocate enough memory for decoder.", audioName.c_str()); return NULL; } source->drop(); getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Audio data is corrupt.", audioName.c_str()); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Could not allocate enough memory.", audioName.c_str()); return NULL; } getLogger()->logError("AudioManager", "Failed to create Audio Source (%s): Codec (.%s) is not supported.", audioName.c_str(), "raw"); return NULL; } bool cAudioManager::registerAudioDecoder(IAudioDecoderFactory* factory, const char* extension) { cAudioMutexBasicLock lock(Mutex); std::string ext = safeCStr(extension); decodermap[ext] = factory; getLogger()->logInfo("AudioManager", "Audio Decoder for extension .%s registered.", ext.c_str()); return true; } void cAudioManager::unRegisterAudioDecoder(const char* extension) { cAudioMutexBasicLock lock(Mutex); std::string ext = safeCStr(extension); std::map::iterator it = decodermap.find(ext); if(it != decodermap.end()) { decodermap.erase(it); getLogger()->logInfo("AudioManager", "Audio Decoder for extension .%s unregistered.", ext.c_str()); } } bool cAudioManager::isAudioDecoderRegistered(const char* extension) { cAudioMutexBasicLock lock(Mutex); std::string ext = safeCStr(extension); std::map::iterator it = decodermap.find(ext); return (it != decodermap.end()); } IAudioDecoderFactory* cAudioManager::getAudioDecoderFactory(const char* extension) { cAudioMutexBasicLock lock(Mutex); std::string ext = safeCStr(extension); std::map::iterator it = decodermap.find(ext); if(it != decodermap.end()) { return it->second; } return NULL; } void cAudioManager::unRegisterAllAudioDecoders() { cAudioMutexBasicLock lock(Mutex); decodermap.clear(); } bool compareDataSourcePriorities(std::pair left, std::pair right) { return (left.first > right.first); } bool cAudioManager::registerDataSource(IDataSourceFactory* factory, const char* name, int priority) { cAudioMutexBasicLock lock(Mutex); std::string safeName = safeCStr(name); datasourcemap[safeName] = factory; dataSourcePriorityList.push_back(std::pair(priority, safeName)); std::sort(dataSourcePriorityList.begin(), dataSourcePriorityList.end(), compareDataSourcePriorities); getLogger()->logInfo("AudioManager", "Data Source named %s registered (Priority %i).", safeName.c_str(), priority); return true; } void cAudioManager::unRegisterDataSource(const char* name) { cAudioMutexBasicLock lock(Mutex); std::string safeName = safeCStr(name); std::map::iterator it = datasourcemap.find(safeName); if(it != datasourcemap.end()) { datasourcemap.erase(it); getLogger()->logInfo("AudioManager", "Data Source named %s unregistered.", safeName.c_str()); } for(int i=0; i::iterator it = datasourcemap.find(safeName); return (it != datasourcemap.end()); } IDataSourceFactory* cAudioManager::getDataSourceFactory(const char* name) { cAudioMutexBasicLock lock(Mutex); std::string safeName = safeCStr(name); std::map::iterator it = datasourcemap.find(safeName); if(it != datasourcemap.end()) { return it->second; } return NULL; } void cAudioManager::unRegisterAllDataSources() { cAudioMutexBasicLock lock(Mutex); datasourcemap.clear(); dataSourcePriorityList.clear(); } void cAudioManager::registerEventHandler(IManagerEventHandler* handler) { if(handler) { eventHandlerList.push_back(handler); } } void cAudioManager::unRegisterEventHandler(IManagerEventHandler* handler) { if(handler) { eventHandlerList.remove(handler); } } void cAudioManager::unRegisterAllEventHandlers() { eventHandlerList.clear(); } void cAudioManager::signalEvent(Events sevent) { std::list::iterator it = eventHandlerList.begin(); if(it != eventHandlerList.end()) { switch(sevent) { case ON_INIT: for(it; it != eventHandlerList.end(); it++) { (*it)->onInit(); } break; 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_SOURCECREATE: for(it; it != eventHandlerList.end(); it++) { (*it)->onSourceCreate(); } break; case ON_DECODERREGISTER: for(it; it != eventHandlerList.end(); it++) { (*it)->onDecoderRegister(); } break; case ON_DATASOURCEREGISTER: for(it; it != eventHandlerList.end(); it++) { (*it)->onDataSourceRegister(); } break; } } } //!grabs the selected audio file via the identifier IAudioSource* cAudioManager::getSoundByName(const char* name) { cAudioMutexBasicLock lock(Mutex); std::string audioName = safeCStr(name); std::map::iterator i = audioIndex.find(audioName); if (i == audioIndex.end()) { return NULL; } return i->second; } //!Releases the selected audio source void cAudioManager::releaseAllSources() { cAudioMutexBasicLock lock(Mutex); for(unsigned int i=0; idrop(); } audioSources.clear(); audioIndex.clear(); } void cAudioManager::release(IAudioSource* source) { if(source) { cAudioMutexBasicLock lock(Mutex); std::map::iterator it = audioIndex.begin(); for ( it=audioIndex.begin(); it != audioIndex.end(); it++ ) { if( it->second == source ) { audioIndex.erase(it); break; } } for(unsigned int i=0; idrop(); audioSources.erase(audioSources.begin()+i); break; } } } } //!Updates all audiosources created void cAudioManager::update() { cAudioMutexBasicLock lock(Mutex); for(unsigned int i=0; iisValid()) { if (source->update()) { } } } } //!Shuts down cAudio. Deletes all audio sources in process void cAudioManager::shutDown() { if(Initialized) { cAudioMutexBasicLock lock(Mutex); releaseAllSources(); //Reset context to null alcMakeContextCurrent(NULL); //Delete the context alcDestroyContext(Context); Context = NULL; //Close the device alcCloseDevice(Device); Device = NULL; Initialized = false; getLogger()->logInfo("AudioManager", "Manager successfully shutdown."); } } bool cAudioManager::checkError() { int error = alGetError(); const char* errorString; if (error != AL_NO_ERROR) { errorString = alGetString(error); getLogger()->logError("AudioManager", "OpenAL Error: %s.", errorString); return true; } if(Device) { error = alcGetError(Device); if (error != AL_NO_ERROR) { errorString = alGetString(error); getLogger()->logError("AudioManager", "OpenAL Error: %s.", errorString); return true; } } return false; } void cAudioManager::getAvailableDevices() { // Get list of available Playback Devices cAudioMutexBasicLock lock(Mutex); if( alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") == AL_TRUE ) { const char* deviceList = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); if (deviceList) { while(*deviceList) { std::string device(deviceList); AvailableDevices.push_back(device); deviceList += strlen(deviceList) + 1; } } // Get the name of the 'default' capture device DefaultDevice = alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER); } else if( alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT") == AL_TRUE ) { const char* deviceList = alcGetString(NULL, ALC_DEVICE_SPECIFIER); if (deviceList) { while(*deviceList) { std::string device(deviceList); AvailableDevices.push_back(device); deviceList += strlen(deviceList) + 1; } } // Get the name of the 'default' capture device DefaultDevice = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); } } const char* cAudioManager::getAvailableDeviceName(unsigned int index) { cAudioMutexBasicLock lock(Mutex); if(!AvailableDevices.empty()) { //Bounds check if( index > (AvailableDevices.size()-1) ) index = (AvailableDevices.size()-1); const char* deviceName = AvailableDevices[index].c_str(); return deviceName; } return ""; } unsigned int cAudioManager::getAvailableDeviceCount() { cAudioMutexBasicLock lock(Mutex); return AvailableDevices.size(); } const char* cAudioManager::getDefaultDeviceName() { cAudioMutexBasicLock lock(Mutex); return DefaultDevice.empty() ? "" : DefaultDevice.c_str(); } CAUDIO_API IAudioManager* createAudioManager(bool initializeDefault) { cAudioManager* manager = new cAudioManager; if(manager) { if(initializeDefault) manager->initialize(); manager->getAvailableDevices(); std::vector plugins = cPluginManager::Instance()->getPluginList(); for(unsigned int i = 0; i < plugins.size(); ++i) { plugins[i]->onCreateAudioManager(manager); } #ifdef CAUDIO_USE_INTERNAL_THREAD AudioManagerObjectsMutex.lock(); AudioManagerObjects.insert(manager); //First time launch of thread if(!RunAudioManagerThread && AudioManagerObjects.size() > 0) RunAudioManagerThread = (cAudioThread::SpawnThread(AudioManagerUpdateThread, NULL) == 0); AudioManagerObjectsMutex.unlock(); #endif } return manager; } CAUDIO_API void destroyAudioManager(IAudioManager* manager) { if(manager) { #ifdef CAUDIO_USE_INTERNAL_THREAD AudioManagerObjectsMutex.lock(); AudioManagerObjects.erase(manager); //Kill the thread if there are no objects to process anymore if(RunAudioManagerThread && AudioManagerObjects.empty()) RunAudioManagerThread = false; AudioManagerObjectsMutex.unlock(); #endif std::vector plugins = cPluginManager::Instance()->getPluginList(); for(unsigned int i = 0; i < plugins.size(); ++i) { plugins[i]->onDestroyAudioManager(manager); } manager->shutDown(); delete manager; manager = NULL; } } CAUDIO_API bool isAudioManagerThreadRunning() { return RunAudioManagerThread; } };