caudio/Source/cAudioManager.cpp
Joshua Jones 3f06749573 Minor API changes to the manager, release is now releaseAllSources, createFromFile is now create.
Added the ability for the user to provide a DataSourceFactory, which cAudio will use to get audio data.  This interface can be overridden to provide cAudio the ability to read from zip archives, TCP streams, potentially any location.  DataSourceFactories can also be prioritized.
Made the existing FileSource a factory and registered it by default with the manager.  If the user wishes to prevent cAudio from reading from disk, unRegister the "FileSystem" data source.
Made tutorials conform with the changes to the API.
2010-02-13 05:48:19 +00:00

810 lines
23 KiB
C++

// 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 <set>
#include <string.h>
#include <algorithm>
#ifdef CAUDIO_EFX_ENABLED
#ifdef CAUDIO_PLATFORM_WIN
#include <AL/efx.h>
#include <AL/efx-creative.h>
#include <AL/xram.h>
#endif
#ifdef CAUDIO_PLATFORM_LINUX
#include <AL/alext.h>
#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<IAudioManager*> AudioManagerObjects;
CAUDIO_DECLARE_THREAD_FUNCTION(AudioManagerUpdateThread)
{
while(RunAudioManagerThread)
{
AudioManagerObjectsMutex.lock();
std::set<IAudioManager*>::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; i<dataSourcePriorityList.size(); ++i)
{
IDataSourceFactory* dataFactory = datasourcemap[dataSourcePriorityList[i].second];
if(dataFactory)
{
IDataSource* source = dataFactory->CreateDataSource(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<std::string, IAudioDecoderFactory*>::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<std::string, IAudioDecoderFactory*>::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<std::string, IAudioDecoderFactory*>::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<int, std::string> left, std::pair<int, std::string> 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<int, std::string>(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<std::string, IDataSourceFactory*>::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<dataSourcePriorityList.size(); ++i)
{
if(dataSourcePriorityList[i].second == safeName)
{
dataSourcePriorityList.erase(dataSourcePriorityList.begin()+i);
break;
}
}
std::sort(dataSourcePriorityList.begin(), dataSourcePriorityList.end(), compareDataSourcePriorities);
}
bool cAudioManager::isDataSourceRegistered(const char* name)
{
cAudioMutexBasicLock lock(Mutex);
std::string safeName = safeCStr(name);
std::map<std::string, IDataSourceFactory*>::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<std::string, IDataSourceFactory*>::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()
{
std::list<IManagerEventHandler*>::iterator it = eventHandlerList.begin();
if(it != eventHandlerList.end())
{
for(it; it != eventHandlerList.end(); it++)
{
eventHandlerList.remove((*it));
}
}
}
void cAudioManager::signalEvent(Events sevent)
{
std::list<IManagerEventHandler*>::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<std::string,IAudioSource*>::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; i<audioSources.size(); ++i)
{
IAudioSource* source = audioSources[i];
if(source)
source->drop();
}
audioSources.clear();
audioIndex.clear();
}
void cAudioManager::release(IAudioSource* source)
{
if(source)
{
cAudioMutexBasicLock lock(Mutex);
std::map<std::string,IAudioSource*>::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; i<audioSources.size(); ++i)
{
if(source == audioSources[i])
{
source->drop();
audioSources.erase(audioSources.begin()+i);
break;
}
}
}
}
//!Updates all audiosources created
void cAudioManager::update()
{
cAudioMutexBasicLock lock(Mutex);
for(unsigned int i=0; i<audioSources.size(); ++i)
{
IAudioSource* source = audioSources[i];
if (source->isValid())
{
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<IAudioPlugin*> 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<IAudioPlugin*> 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;
}
};