From af9b9f732fc5ae586e00b9b5fea9589292e4b594 Mon Sep 17 00:00:00 2001 From: Bim Overbohm Date: Tue, 14 May 2013 21:31:39 +0200 Subject: [PATCH] Remove the need for SDL_mixer SDL_mixer is not in the standard SDL distribution. The mixing is now done using regular SDL_Audio functions. AudioManager is converted to a singleton and std::shared_ptrs are used for all Sound objects. Note that for GCC "-std=c++11" might need to be added to the CMAKE_CXX_FLAGS. --- src/AudioManager.cpp | 141 +++++++++++++++++++------------ src/AudioManager.h | 30 +++++-- src/Sound.cpp | 116 +++++++++++++++++-------- src/Sound.h | 26 ++++-- src/Window.cpp | 2 - src/components/GuiFastSelect.cpp | 2 +- src/components/GuiFastSelect.h | 4 +- src/components/GuiList.cpp | 2 +- src/components/GuiList.h | 4 +- src/components/GuiTheme.cpp | 16 ++-- src/components/GuiTheme.h | 8 +- src/main.cpp | 7 +- src/platform.cpp | 15 ++-- 13 files changed, 236 insertions(+), 137 deletions(-) diff --git a/src/AudioManager.cpp b/src/AudioManager.cpp index 326490df5..d1cb02c17 100644 --- a/src/AudioManager.cpp +++ b/src/AudioManager.cpp @@ -1,69 +1,100 @@ #include "AudioManager.h" #include "Log.h" -#include "SDL.h" -#include "SDL_mixer.h" -#include -#include "Sound.h" -#include -namespace AudioManager + +std::vector> AudioManager::sSoundVector; +std::shared_ptr AudioManager::sInstance; +SDL_AudioSpec AudioManager::sAudioFormat; + + +void AudioManager::mixAudio(void *unused, Uint8 *stream, int len) { - std::vector sSoundVector; + bool stillPlaying = false; - bool sInitialized = false; - - void init() + //iterate through all our samples + std::vector>::const_iterator soundIt = sSoundVector.cbegin(); + while (soundIt != sSoundVector.cend()) { - int result = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, AUDIO_S16SYS, 2, 1024); - - if(result == -1) + std::shared_ptr sound = *soundIt; + if(sound->isPlaying()) { - LOG(LogError) << "Error initializing AudioManager!\n " << Mix_GetError(); - return; - } - - sInitialized = true; - - for(unsigned int i = 0; i < sSoundVector.size(); i++) - { - sSoundVector.at(i)->init(); - } - } - - void registerSound(Sound* sound) - { - sSoundVector.push_back(sound); - } - - void unregisterSound(Sound* sound) - { - for(unsigned int i = 0; i < sSoundVector.size(); i++) - { - if(sSoundVector.at(i) == sound) + //calculate rest length of current sample + Uint32 restLength = (sound->getLength() - sound->getPosition()); + if (restLength > len) { + //if stream length is smaller than smaple lenght, clip it + restLength = len; + } + //mix sample into stream + SDL_MixAudio(stream, &(sound->getData()[sound->getPosition()]), restLength, SDL_MIX_MAXVOLUME); + if (sound->getPosition() + restLength >= sound->getLength()) { - sSoundVector.erase(sSoundVector.begin() + i); - return; + //if the position is beyond the end of the buffer, stop playing the sample + sound->stop(); + } + else + { + //sample hasn't ended yet + sound->setPosition(sound->getPosition() + restLength); + stillPlaying = true; } } - - LOG(LogError) << "AudioManager Error - tried to unregister a sound that wasn't registered!"; + //advance to next sound + ++soundIt; } - - void deinit() - { - for(unsigned int i = 0; i < sSoundVector.size(); i++) - { - sSoundVector.at(i)->deinit(); - } - - Mix_CloseAudio(); - - sInitialized = false; - } - - bool isInitialized() - { - return sInitialized; + //we have processed all samples. check if some will still be playing + if (!stillPlaying) { + //no. pause audio till a Sound::play() wakes us up + SDL_PauseAudio(1); } } + +AudioManager::AudioManager() +{ + //Set up format and callback. Play 16-bit stereo audio at 44.1Khz + sAudioFormat.freq = 44100; + sAudioFormat.format = AUDIO_S16; + sAudioFormat.channels = 2; + sAudioFormat.samples = 1024; + sAudioFormat.callback = mixAudio; + sAudioFormat.userdata = NULL; + + //Open the audio device and pause + if (SDL_OpenAudio(&sAudioFormat, NULL) < 0) { + LOG(LogError) << "AudioManager Error - Unable to open SDL audio: " << SDL_GetError() << std::endl; + } +} + +AudioManager::~AudioManager() +{ + SDL_PauseAudio(1); + SDL_CloseAudio(); +} + +void AudioManager::registerSound(std::shared_ptr & sound) +{ + //check if an AudioManager instance is already created, if not create one + if (sInstance == nullptr) { + sInstance = std::shared_ptr(new AudioManager); + } + sSoundVector.push_back(sound); +} + +void AudioManager::unregisterSound(std::shared_ptr & sound) +{ + for(unsigned int i = 0; i < sSoundVector.size(); i++) + { + if(sSoundVector.at(i) == sound) + { + sSoundVector.erase(sSoundVector.begin() + i); + return; + } + } + LOG(LogError) << "AudioManager Error - tried to unregister a sound that wasn't registered!"; +} + +void AudioManager::play() +{ + //unpause audio, the mixer will figure out if samples need to be played... + SDL_PauseAudio(0); +} \ No newline at end of file diff --git a/src/AudioManager.h b/src/AudioManager.h index c3b264c87..b20a965b2 100644 --- a/src/AudioManager.h +++ b/src/AudioManager.h @@ -1,17 +1,31 @@ #ifndef _AUDIOMANAGER_H_ #define _AUDIOMANAGER_H_ -class Sound; +#include +#include -namespace AudioManager +#include "SDL_audio.h" + +#include "Sound.h" + + +class AudioManager { - void registerSound(Sound* sound); - void unregisterSound(Sound* sound); + static SDL_AudioSpec sAudioFormat; + static std::vector> sSoundVector; + static std::shared_ptr sInstance; - bool isInitialized(); + static void mixAudio(void *unused, Uint8 *stream, int len); - void init(); - void deinit(); -} + AudioManager(); + +public: + static void registerSound(std::shared_ptr & sound); + static void unregisterSound(std::shared_ptr & sound); + + static void play(); + + virtual ~AudioManager(); +}; #endif diff --git a/src/Sound.cpp b/src/Sound.cpp index abcc58d63..ee93d8067 100644 --- a/src/Sound.cpp +++ b/src/Sound.cpp @@ -1,26 +1,19 @@ #include "Sound.h" -#include #include "AudioManager.h" #include "Log.h" -Sound::Sound(std::string path) + +Sound::Sound(const std::string & path) : mSampleData(NULL), mSamplePos(0), mSampleLength(0), playing(false) { - mSound = NULL; - mChannel = -1; - - AudioManager::registerSound(this); - loadFile(path); } Sound::~Sound() { deinit(); - - AudioManager::unregisterSound(this); } -void Sound::loadFile(std::string path) +void Sound::loadFile(const std::string & path) { mPath = path; init(); @@ -28,51 +21,104 @@ void Sound::loadFile(std::string path) void Sound::init() { - if(!AudioManager::isInitialized()) - return; - - if(mSound != NULL) + if(mSampleData != NULL) deinit(); if(mPath.empty()) return; - mSound = Mix_LoadWAV(mPath.c_str()); - - if(mSound == NULL) - { - LOG(LogError) << "Error loading sound \"" << mPath << "\"!\n" << " " << Mix_GetError(); + //load wav file via SDL + SDL_AudioSpec wave; + Uint8 * data = NULL; + Uint32 dlen = 0; + if (SDL_LoadWAV(mPath.c_str(), &wave, &data, &dlen) == NULL) { + LOG(LogError) << "Error loading sound \"" << mPath << "\"!\n" << " " << SDL_GetError(); + return; } + //build conversion buffer + SDL_AudioCVT cvt; + SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_S16, 2, 44100); + //copy data to conversion buffer + cvt.len = dlen; + cvt.buf = new Uint8[cvt.len * cvt.len_mult]; + memcpy(cvt.buf, data, dlen); + //convert buffer to stereo, 16bit, 44.1kHz + if (SDL_ConvertAudio(&cvt) < 0) { + LOG(LogError) << "Error converting sound \"" << mPath << "\" to 44.1kHz, 16bit, stereo format!\n" << " " << SDL_GetError(); + delete[] cvt.buf; + } + else { + //worked. set up member data + SDL_LockAudio(); + mSampleData = cvt.buf; + mSampleLength = cvt.len_cvt; + mSamplePos = 0; + mSampleFormat.channels = 2; + mSampleFormat.freq = 44100; + mSampleFormat.format = AUDIO_S16; + SDL_UnlockAudio(); + } + //free wav data now + SDL_FreeWAV(data); } void Sound::deinit() { - if(mSound != NULL) + playing = false; + + if(mSampleData != NULL) { - Mix_FreeChunk(mSound); - mSound = NULL; + SDL_LockAudio(); + delete[] mSampleData; + mSampleData = NULL; + mSampleLength = 0; + mSamplePos = 0; + SDL_UnlockAudio(); } } void Sound::play() { - if(mSound == NULL) + if(mSampleData == NULL) return; - mChannel = -1; + //flag our sample as playing + playing = true; + //tell the AudioManager to start playing samples + AudioManager::play(); +} - mChannel = Mix_PlayChannel(-1, mSound, 0); - if(mChannel == -1) - { - LOG(LogError) << "Error playing sound!\n " << Mix_GetError(); +bool Sound::isPlaying() const +{ + return playing; +} + +void Sound::stop() +{ + //flag our sample as playing and rewind its position + playing = false; + mSamplePos = 0; +} + +const Uint8 * Sound::getData() const +{ + return mSampleData; +} + +Uint32 Sound::getPosition() const +{ + return mSamplePos; +} + +void Sound::setPosition(Uint32 newPosition) +{ + mSamplePos = newPosition; + if (mSamplePos >= mSampleLength) { + mSamplePos = 0; } } -bool Sound::isPlaying() +Uint32 Sound::getLength() const { - if(mChannel != -1 && Mix_Playing(mChannel)) - return true; - else - return false; -} - + return mSampleLength; +} \ No newline at end of file diff --git a/src/Sound.h b/src/Sound.h index 7301626b3..d4858c11b 100644 --- a/src/Sound.h +++ b/src/Sound.h @@ -2,25 +2,35 @@ #define _SOUND_H_ #include -#include "SDL_mixer.h" +#include "SDL_audio.h" + class Sound { + std::string mPath; + SDL_AudioSpec mSampleFormat; + Uint8 * mSampleData; + Uint32 mSamplePos; + Uint32 mSampleLength; + bool playing; + public: - Sound(std::string path = ""); + Sound(const std::string & path = ""); ~Sound(); void init(); void deinit(); - void loadFile(std::string path); + void loadFile(const std::string & path); void play(); - bool isPlaying(); -private: - std::string mPath; - int mChannel; - Mix_Chunk* mSound; + bool isPlaying() const; + void stop(); + + const Uint8 * getData() const; + Uint32 getPosition() const; + void setPosition(Uint32 newPosition); + Uint32 getLength() const; }; #endif diff --git a/src/Window.cpp b/src/Window.cpp index 6bb15bff3..119ac9c5f 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -51,7 +51,6 @@ void Window::render() void Window::init() { - AudioManager::init(); mInputManager->init(); Renderer::init(0, 0); @@ -68,7 +67,6 @@ void Window::deinit() mGuiStack.at(i)->deinit(); } - AudioManager::deinit(); mInputManager->deinit(); Renderer::deinit(); } diff --git a/src/components/GuiFastSelect.cpp b/src/components/GuiFastSelect.cpp index 93735c217..488125a0c 100644 --- a/src/components/GuiFastSelect.cpp +++ b/src/components/GuiFastSelect.cpp @@ -8,7 +8,7 @@ const int GuiFastSelect::SCROLLSPEED = 100; const int GuiFastSelect::SCROLLDELAY = 507; GuiFastSelect::GuiFastSelect(Window* window, GuiGameList* parent, GuiList* list, char startLetter, GuiBoxData data, - int textcolor, Sound* scrollsound, Font* font) : Gui(window) + int textcolor, std::shared_ptr & scrollsound, Font* font) : Gui(window) { mLetterID = LETTERS.find(toupper(startLetter)); if(mLetterID == std::string::npos) diff --git a/src/components/GuiFastSelect.h b/src/components/GuiFastSelect.h index 421173419..350e78282 100644 --- a/src/components/GuiFastSelect.h +++ b/src/components/GuiFastSelect.h @@ -13,7 +13,7 @@ class GuiGameList; class GuiFastSelect : public Gui { public: - GuiFastSelect(Window* window, GuiGameList* parent, GuiList* list, char startLetter, GuiBoxData data, int textcolor, Sound* scrollsound, Font* font); + GuiFastSelect(Window* window, GuiGameList* parent, GuiList* list, char startLetter, GuiBoxData data, int textcolor, std::shared_ptr & scrollsound, Font* font); ~GuiFastSelect(); void input(InputConfig* config, Input input); @@ -39,7 +39,7 @@ private: int mScrollTimer, mScrollOffset; bool mScrolling; - Sound* mScrollSound; + std::shared_ptr mScrollSound; Font* mFont; }; diff --git a/src/components/GuiList.cpp b/src/components/GuiList.cpp index e2f9a6b61..89217fee8 100644 --- a/src/components/GuiList.cpp +++ b/src/components/GuiList.cpp @@ -269,7 +269,7 @@ void GuiList::setSelection(int i) } template -void GuiList::setScrollSound(Sound* sound) +void GuiList::setScrollSound(std::shared_ptr & sound) { mScrollSound = sound; } diff --git a/src/components/GuiList.h b/src/components/GuiList.h index fc68d39aa..076f77386 100644 --- a/src/components/GuiList.h +++ b/src/components/GuiList.h @@ -34,7 +34,7 @@ public: void setSelectorColor(unsigned int selectorColor); void setSelectedTextColor(unsigned int selectedColor); void setCentered(bool centered); - void setScrollSound(Sound* sound); + void setScrollSound(std::shared_ptr & sound); void setTextOffsetX(int textoffsetx); int getObjectCount(); @@ -71,7 +71,7 @@ private: std::vector mRowVector; int mSelection; - Sound* mScrollSound; + std::shared_ptr mScrollSound; }; #include "GuiList.cpp" diff --git a/src/components/GuiTheme.cpp b/src/components/GuiTheme.cpp index 7a9793ca8..6bc27a07a 100644 --- a/src/components/GuiTheme.cpp +++ b/src/components/GuiTheme.cpp @@ -23,7 +23,7 @@ float GuiTheme::getFloat(std::string name) return mFloatMap[name]; } -Sound* GuiTheme::getSound(std::string name) +std::shared_ptr & GuiTheme::getSound(std::string name) { return mSoundMap[name]; } @@ -63,10 +63,16 @@ GuiTheme::GuiTheme(Window* window, bool detailed, std::string path) : Gui(window { mDetailed = detailed; - mSoundMap["menuScroll"] = new Sound(); - mSoundMap["menuSelect"] = new Sound(); - mSoundMap["menuBack"] = new Sound(); - mSoundMap["menuOpen"] = new Sound(); + mSoundMap["menuScroll"] = std::shared_ptr(new Sound); + mSoundMap["menuSelect"] = std::shared_ptr(new Sound); + mSoundMap["menuBack"] = std::shared_ptr(new Sound); + mSoundMap["menuOpen"] = std::shared_ptr(new Sound); + + //register all sound with the audiomanager + AudioManager::registerSound(mSoundMap["menuScroll"]); + AudioManager::registerSound(mSoundMap["menuSelect"]); + AudioManager::registerSound(mSoundMap["menuBack"]); + AudioManager::registerSound(mSoundMap["menuOpen"]); mListFont = NULL; mDescFont = NULL; diff --git a/src/components/GuiTheme.h b/src/components/GuiTheme.h index 722eee7a0..a0e8a8900 100644 --- a/src/components/GuiTheme.h +++ b/src/components/GuiTheme.h @@ -1,10 +1,12 @@ #ifndef _GUITHEME_H_ #define _GUITHEME_H_ +#include + #include "../Gui.h" #include "../pugiXML/pugixml.hpp" #include "GuiBox.h" -#include "../Sound.h" +#include "../AudioManager.h" #include "../Font.h" //This class loads an XML-defined list of Guis. @@ -26,7 +28,7 @@ public: unsigned int getColor(std::string name); bool getBool(std::string name); float getFloat(std::string name); - Sound* getSound(std::string name); + std::shared_ptr & getSound(std::string name); std::string getString(std::string name); Font* getListFont(); @@ -53,7 +55,7 @@ private: std::map mColorMap; std::map mBoolMap; std::map mFloatMap; - std::map mSoundMap; + std::map> mSoundMap; std::map mStringMap; GuiBoxData mBoxData; diff --git a/src/main.cpp b/src/main.cpp index 6d0c324ee..d0f347903 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -119,10 +119,6 @@ int main(int argc, char* argv[]) return 1; } - - //initialize audio - AudioManager::init(); - Window window; //don't call Window.init() because we manually pass the resolution to Renderer::init window.getInputManager()->init(); @@ -139,7 +135,7 @@ int main(int argc, char* argv[]) if(SystemData::sSystemVector.size() == 0) //if it exists but was empty, notify the user and quit { std::cerr << "A system config file in " << SystemData::getConfigPath() << " was found, but contained no systems.\n"; - std::cerr << "Does at least one system have a game presesnt?\n"; + std::cerr << "Does at least one system have a game present?\n"; running = false; }else{ //choose which GUI to open depending on Input configuration @@ -222,7 +218,6 @@ int main(int argc, char* argv[]) Log::flush(); } - AudioManager::deinit(); Renderer::deinit(); SystemData::deleteSystems(); diff --git a/src/platform.cpp b/src/platform.cpp index 350ab70f0..bf88d70b2 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -3,13 +3,10 @@ std::string getHomePath() { - #ifdef _WIN32 - return "C:\\"; - #else - const char* home = getenv("HOME"); - if(home == NULL) - return ""; - else - return home; - #endif + //this gives you something like "/home/YOUR_USERNAME" on Linux and "C:\Users\YOUR_USERNAME\" on Windows + const char* home = getenv("HOME"); + if(home == NULL) + return ""; + else + return home; }