/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... code has been taken from http://www.devmaster.net/articles.php?catID=6 The code has been applied to our needs, and many things have been changed. */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_SOUND #include "sound_engine.h" #include "class_list.h" #include "p_node.h" #include "util/loading/resource_manager.h" #include "debug.h" #include "util/preferences.h" #include "globals.h" using namespace std; ////////////////// /* SOUND-ENGINE */ ////////////////// /** * @brief standard constructor */ SoundEngine::SoundEngine () { this->setClassID(CL_SOUND_ENGINE, "SoundEngine"); this->setName("SoundEngine"); this->listener = NULL; this->bufferList = NULL; this->sourceList = NULL; this->device = NULL; this->context = NULL; this->maxSourceCount = 32; this->effectsVolume = .80; this->musicVolume = .75; this->sourceMutex = SDL_CreateMutex(); } /** * @brief the singleton reference to this class */ SoundEngine* SoundEngine::singletonRef = NULL; /** * @brief standard destructor */ SoundEngine::~SoundEngine () { // deleting all the SoundSources if(this->sourceList != NULL) { while (this->sourceList->size() > 0) delete static_cast(this->sourceList->front()); } while(!this->ALSources.empty()) { if (alIsSource(this->ALSources.top())) { alDeleteSources(1, &this->ALSources.top()); SoundEngine::checkError("Deleting Source", __LINE__); } else PRINTF(1)("%d is not a Source\n", this->ALSources.top()); this->ALSources.pop(); } // deleting all the SoundBuffers if (this->bufferList != NULL) { while(this->bufferList->size() > 0) ResourceManager::getInstance()->unload(static_cast(this->bufferList->front())); } // removing openAL from AudioResource //! @todo this should be terminated through alc //alutExit(); SDL_DestroyMutex(this->sourceMutex); SoundEngine::singletonRef = NULL; } /** * @brief loads the settings of the SoundEngine from an ini-file */ void SoundEngine::loadSettings() { MultiType channels = Preferences::getInstance()->getString(CONFIG_SECTION_AUDIO, CONFIG_NAME_AUDIO_CHANNELS, "32"); this->maxSourceCount = channels.getInt(); MultiType effectsVolume = Preferences::getInstance()->getString(CONFIG_SECTION_AUDIO, CONFIG_NAME_EFFECTS_VOLUME, "80"); this->effectsVolume = effectsVolume.getFloat()/100.0; MultiType musicVolume = Preferences::getInstance()->getString(CONFIG_SECTION_AUDIO, CONFIG_NAME_MUSIC_VOLUME, "75"); this->musicVolume = musicVolume.getFloat()/100.0; } /** * @brief creates a new SoundSource. * @param fileName The Name to load the SoundBuffer from * @param sourceNode The sourceNode to bind this SoundSource to. * @returns The newly created SoundSource acctualy this is nothing more than a wrapper around the ResourceManager. */ SoundSource* SoundEngine::createSource(const std::string& fileName, PNode* sourceNode) { return new SoundSource(sourceNode, (SoundBuffer*)ResourceManager::getInstance()->load(fileName, WAV, RP_LEVEL)); } /** * @brief Sets the doppler values of openAL * @param dopplerFactor the extent of the doppler-effect * @param dopplerVelocity the Speed the sound travels */ void SoundEngine::setDopplerValues(ALfloat dopplerFactor, ALfloat dopplerVelocity) { alDopplerFactor(dopplerFactor); this->checkError("Setting Doppler Factor", __LINE__); alDopplerVelocity(dopplerVelocity); this->checkError("Setting Doppler Velocity", __LINE__); } /** * @brief retrieves an OpenAL Source from the availiable Sources. * @param source the Source to fill with the Value. */ void SoundEngine::popALSource(ALuint& source) { assert (source == 0); /// @TODO try to create more sources if needed if (!this->ALSources.empty()) { SDL_mutexP(this->sourceMutex); source = this->ALSources.top(); this->ALSources.pop(); SDL_mutexV(this->sourceMutex); } } /** * @brief Pushes an OpenAL Source back into the Stack of known sources * @param source the Source to push onto the top of the SourceStack */ void SoundEngine::pushALSource(ALuint& source) { if (source != 0) { SDL_mutexP(this->sourceMutex); this->ALSources.push(source); SDL_mutexV(this->sourceMutex); } }; /** * @brief updates all The positions, Directions and Velocities of all Sounds */ void SoundEngine::update() { // updating the Listeners Position if (likely(this->listener != NULL)) { alListener3f(AL_POSITION, this->listener->getAbsCoor().x, this->listener->getAbsCoor().y, this->listener->getAbsCoor().z); alListener3f(AL_VELOCITY, this->listener->getVelocity().x, this->listener->getVelocity().y, this->listener->getVelocity().z); Vector absDirV = this->listener->getAbsDirV(); ALfloat orientation [6] = {1,0,0, absDirV.x, absDirV.y, absDirV.z}; alListenerfv(AL_ORIENTATION, orientation); SoundEngine::checkError("SoundEngine::update() - Listener Error", __LINE__); } else PRINTF(2)("no listener defined\n"); // updating all the Sources positions if (likely(this->sourceList != NULL || (this->sourceList = ClassList::getList(CL_SOUND_SOURCE)) != NULL)) { list::const_iterator sourceIT; SoundSource* source; for (sourceIT = this->sourceList->begin(); sourceIT != this->sourceList->end(); sourceIT++) { source = static_cast(*sourceIT); if (source->isPlaying()) { int play = 0x000; alGetSourcei(source->getID(), AL_SOURCE_STATE, &play); if (DEBUG > 2) SoundEngine::checkError("SoundEngine::update() Play", __LINE__); if(play == AL_PLAYING) { if (likely(source->getNode() != NULL)) { alSource3f(source->getID(), AL_POSITION, source->getNode()->getAbsCoor().x, source->getNode()->getAbsCoor().y, source->getNode()->getAbsCoor().z); if (DEBUG > 2) SoundEngine::checkError("SoundEngine::update() Set Source Position", __LINE__); alSource3f(source->getID(), AL_VELOCITY, source->getNode()->getVelocity().x, source->getNode()->getVelocity().y, source->getNode()->getVelocity().z); if (DEBUG > 2) SoundEngine::checkError("SoundEngine::update() Set Source Velocity", __LINE__); } } else { source->stop(); } } } } SoundEngine::checkError("SoundEngine::update()", __LINE__); } /** * initializes Audio in general */ bool SoundEngine::initAudio() { // ALenum result; // PRINTF(3)("Initialisazing openAL sound engine\n"); // const char* defaultDevice =(const char*) alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); // const char* deviceList = (const char*)alcGetString(NULL,ALC_DEVICE_SPECIFIER); // const char* devWalk = deviceList; // // if (alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATION_EXT") == AL_TRUE) // { // try out enumeration extension // PRINTF(3)("Enumeration-extension found\n"); // // PRINTF(3)("Default device: %s\n", defaultDevice); // do // { // PRINTF(3)("%s\n", devWalk); // devWalk += strlen(devWalk)+1; // } // while (devWalk[0] != '\0'); // } // INITIALIZING THE DEVICE: // char deviceName[] = // #ifdef __WIN32__ // "Direct3D"; // #else // "'( ( devices '( native null ) ) )"; // #endif this->device = alcOpenDevice(NULL); this->checkALCError("opening Device", __LINE__); PRINTF(4)("Audio-Specifier: %s\n", (const char*)alcGetString(this->device, ALC_DEVICE_SPECIFIER)); PRINTF(4)("Audio-Extensions: %s\n", (const char*)alcGetString(this->device, ALC_EXTENSIONS)); this->context = alcCreateContext(this->device, NULL); this->checkALCError("creating Context", __LINE__); alcMakeContextCurrent(this->context); this->checkALCError("making Context Current", __LINE__); // #endif this->setDopplerValues(SOUND_DOPPLER_FACTOR, SOUND_DOPPLER_VELOCITY); this->allocateSources(this->maxSourceCount); this->checkError("Allocating Sources", __LINE__); } /** * Allocates openAL sources * @param count how many sources to allocate * @returns true on success, false if at least one source could not be allocated */ bool SoundEngine::allocateSources(unsigned int count) { unsigned int failCount = 0; for (unsigned int i = 0; i < count; i++) { ALuint source = 0; alGenSources(1, &source); this->checkError("allocate Source", __LINE__); if (!alIsSource(source)) { PRINTF(5)("not allocated Source\n"); failCount++; continue; } alSourcef (source, AL_PITCH, 1.0 ); alSourcef (source, AL_GAIN, this->getEffectsVolume() ); alSourcei (source, AL_LOOPING, AL_FALSE ); this->ALSources.push(source); } if (failCount == 0) return true; else { PRINTF(2)("Failed to allocate %d of %d SoundSources\n", failCount, count); } } /** * @brief checks for an OpenAL error * @param error the ErrorMessage to display * @param line on what line did the error occure. */ bool SoundEngine::checkError(const std::string& error, unsigned int line) { ALenum errorCode; if ((errorCode = alGetError()) != AL_NO_ERROR) { PRINTF(1)("Error %s (line:%d): '%s'\n", error.c_str(), line, SoundEngine::getALErrorString(errorCode)); return false; } else return true; } /** * @brief check for an ALC error. * @brief error the Error-String to display * @param line on that line, the error occured (debugging mode). */ bool SoundEngine::checkALCError(const std::string& error, unsigned int line) { ALenum errorCode; if ((errorCode = alcGetError(this->device)) != ALC_NO_ERROR) { PRINTF(1)("Error %s (line:%d): '%s'\n", error.c_str(), line, SoundEngine::getALCErrorString(errorCode)); return false; } else return true; } void SoundEngine::listDevices() { printf("%s\n",(const char*)alcGetString(NULL, ALC_DEVICE_SPECIFIER)); } /** * Transforms AL-errors into something readable * @param err The error found */ const char* SoundEngine::getALErrorString(ALenum err) { switch(err) { case AL_NO_ERROR: return ("AL_NO_ERROR"); case AL_INVALID_NAME: return ("AL_INVALID_NAME"); case AL_INVALID_ENUM: return ("AL_INVALID_ENUM"); case AL_INVALID_VALUE: return ("AL_INVALID_VALUE"); case AL_INVALID_OPERATION: return ("AL_INVALID_OPERATION"); case AL_OUT_OF_MEMORY: return ("AL_OUT_OF_MEMORY"); }; } const char* SoundEngine::getALCErrorString(ALenum err) { switch(err) { case ALC_NO_ERROR: return ("AL_NO_ERROR"); case ALC_INVALID_DEVICE: return ("ALC_INVALID_DEVICE"); case ALC_INVALID_CONTEXT: return("ALC_INVALID_CONTEXT"); case ALC_INVALID_ENUM: return("ALC_INVALID_ENUM"); case ALC_INVALID_VALUE: return ("ALC_INVALID_VALUE"); case ALC_OUT_OF_MEMORY: return("ALC_OUT_OF_MEMORY"); }; }