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.
This commit is contained in:
Bim Overbohm 2013-05-14 21:31:39 +02:00
parent a1353def89
commit af9b9f732f
13 changed files with 236 additions and 137 deletions

View file

@ -1,69 +1,100 @@
#include "AudioManager.h" #include "AudioManager.h"
#include "Log.h" #include "Log.h"
#include "SDL.h"
#include "SDL_mixer.h"
#include <iostream>
#include "Sound.h"
#include <vector>
namespace AudioManager
std::vector<std::shared_ptr<Sound>> AudioManager::sSoundVector;
std::shared_ptr<AudioManager> AudioManager::sInstance;
SDL_AudioSpec AudioManager::sAudioFormat;
void AudioManager::mixAudio(void *unused, Uint8 *stream, int len)
{ {
std::vector<Sound*> sSoundVector; bool stillPlaying = false;
bool sInitialized = false; //iterate through all our samples
std::vector<std::shared_ptr<Sound>>::const_iterator soundIt = sSoundVector.cbegin();
void init() while (soundIt != sSoundVector.cend())
{ {
int result = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, AUDIO_S16SYS, 2, 1024); std::shared_ptr<Sound> sound = *soundIt;
if(sound->isPlaying())
if(result == -1)
{ {
LOG(LogError) << "Error initializing AudioManager!\n " << Mix_GetError(); //calculate rest length of current sample
return; Uint32 restLength = (sound->getLength() - sound->getPosition());
} if (restLength > len) {
//if stream length is smaller than smaple lenght, clip it
sInitialized = true; restLength = len;
}
for(unsigned int i = 0; i < sSoundVector.size(); i++) //mix sample into stream
{ SDL_MixAudio(stream, &(sound->getData()[sound->getPosition()]), restLength, SDL_MIX_MAXVOLUME);
sSoundVector.at(i)->init(); if (sound->getPosition() + restLength >= sound->getLength())
}
}
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)
{ {
sSoundVector.erase(sSoundVector.begin() + i); //if the position is beyond the end of the buffer, stop playing the sample
return; sound->stop();
}
else
{
//sample hasn't ended yet
sound->setPosition(sound->getPosition() + restLength);
stillPlaying = true;
} }
} }
//advance to next sound
LOG(LogError) << "AudioManager Error - tried to unregister a sound that wasn't registered!"; ++soundIt;
} }
//we have processed all samples. check if some will still be playing
void deinit() if (!stillPlaying) {
{ //no. pause audio till a Sound::play() wakes us up
for(unsigned int i = 0; i < sSoundVector.size(); i++) SDL_PauseAudio(1);
{
sSoundVector.at(i)->deinit();
}
Mix_CloseAudio();
sInitialized = false;
}
bool isInitialized()
{
return sInitialized;
} }
} }
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> & sound)
{
//check if an AudioManager instance is already created, if not create one
if (sInstance == nullptr) {
sInstance = std::shared_ptr<AudioManager>(new AudioManager);
}
sSoundVector.push_back(sound);
}
void AudioManager::unregisterSound(std::shared_ptr<Sound> & 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);
}

View file

@ -1,17 +1,31 @@
#ifndef _AUDIOMANAGER_H_ #ifndef _AUDIOMANAGER_H_
#define _AUDIOMANAGER_H_ #define _AUDIOMANAGER_H_
class Sound; #include <vector>
#include <memory>
namespace AudioManager #include "SDL_audio.h"
#include "Sound.h"
class AudioManager
{ {
void registerSound(Sound* sound); static SDL_AudioSpec sAudioFormat;
void unregisterSound(Sound* sound); static std::vector<std::shared_ptr<Sound>> sSoundVector;
static std::shared_ptr<AudioManager> sInstance;
bool isInitialized(); static void mixAudio(void *unused, Uint8 *stream, int len);
void init(); AudioManager();
void deinit();
} public:
static void registerSound(std::shared_ptr<Sound> & sound);
static void unregisterSound(std::shared_ptr<Sound> & sound);
static void play();
virtual ~AudioManager();
};
#endif #endif

View file

@ -1,26 +1,19 @@
#include "Sound.h" #include "Sound.h"
#include <iostream>
#include "AudioManager.h" #include "AudioManager.h"
#include "Log.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); loadFile(path);
} }
Sound::~Sound() Sound::~Sound()
{ {
deinit(); deinit();
AudioManager::unregisterSound(this);
} }
void Sound::loadFile(std::string path) void Sound::loadFile(const std::string & path)
{ {
mPath = path; mPath = path;
init(); init();
@ -28,51 +21,104 @@ void Sound::loadFile(std::string path)
void Sound::init() void Sound::init()
{ {
if(!AudioManager::isInitialized()) if(mSampleData != NULL)
return;
if(mSound != NULL)
deinit(); deinit();
if(mPath.empty()) if(mPath.empty())
return; return;
mSound = Mix_LoadWAV(mPath.c_str()); //load wav file via SDL
SDL_AudioSpec wave;
if(mSound == NULL) Uint8 * data = NULL;
{ Uint32 dlen = 0;
LOG(LogError) << "Error loading sound \"" << mPath << "\"!\n" << " " << Mix_GetError(); 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() void Sound::deinit()
{ {
if(mSound != NULL) playing = false;
if(mSampleData != NULL)
{ {
Mix_FreeChunk(mSound); SDL_LockAudio();
mSound = NULL; delete[] mSampleData;
mSampleData = NULL;
mSampleLength = 0;
mSamplePos = 0;
SDL_UnlockAudio();
} }
} }
void Sound::play() void Sound::play()
{ {
if(mSound == NULL) if(mSampleData == NULL)
return; 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); bool Sound::isPlaying() const
if(mChannel == -1) {
{ return playing;
LOG(LogError) << "Error playing sound!\n " << Mix_GetError(); }
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 mSampleLength;
return true; }
else
return false;
}

View file

@ -2,25 +2,35 @@
#define _SOUND_H_ #define _SOUND_H_
#include <string> #include <string>
#include "SDL_mixer.h" #include "SDL_audio.h"
class Sound class Sound
{ {
std::string mPath;
SDL_AudioSpec mSampleFormat;
Uint8 * mSampleData;
Uint32 mSamplePos;
Uint32 mSampleLength;
bool playing;
public: public:
Sound(std::string path = ""); Sound(const std::string & path = "");
~Sound(); ~Sound();
void init(); void init();
void deinit(); void deinit();
void loadFile(std::string path); void loadFile(const std::string & path);
void play(); void play();
bool isPlaying(); bool isPlaying() const;
private: void stop();
std::string mPath;
int mChannel; const Uint8 * getData() const;
Mix_Chunk* mSound; Uint32 getPosition() const;
void setPosition(Uint32 newPosition);
Uint32 getLength() const;
}; };
#endif #endif

View file

@ -51,7 +51,6 @@ void Window::render()
void Window::init() void Window::init()
{ {
AudioManager::init();
mInputManager->init(); mInputManager->init();
Renderer::init(0, 0); Renderer::init(0, 0);
@ -68,7 +67,6 @@ void Window::deinit()
mGuiStack.at(i)->deinit(); mGuiStack.at(i)->deinit();
} }
AudioManager::deinit();
mInputManager->deinit(); mInputManager->deinit();
Renderer::deinit(); Renderer::deinit();
} }

View file

@ -8,7 +8,7 @@ const int GuiFastSelect::SCROLLSPEED = 100;
const int GuiFastSelect::SCROLLDELAY = 507; const int GuiFastSelect::SCROLLDELAY = 507;
GuiFastSelect::GuiFastSelect(Window* window, GuiGameList* parent, GuiList<FileData*>* list, char startLetter, GuiBoxData data, GuiFastSelect::GuiFastSelect(Window* window, GuiGameList* parent, GuiList<FileData*>* list, char startLetter, GuiBoxData data,
int textcolor, Sound* scrollsound, Font* font) : Gui(window) int textcolor, std::shared_ptr<Sound> & scrollsound, Font* font) : Gui(window)
{ {
mLetterID = LETTERS.find(toupper(startLetter)); mLetterID = LETTERS.find(toupper(startLetter));
if(mLetterID == std::string::npos) if(mLetterID == std::string::npos)

View file

@ -13,7 +13,7 @@ class GuiGameList;
class GuiFastSelect : public Gui class GuiFastSelect : public Gui
{ {
public: public:
GuiFastSelect(Window* window, GuiGameList* parent, GuiList<FileData*>* list, char startLetter, GuiBoxData data, int textcolor, Sound* scrollsound, Font* font); GuiFastSelect(Window* window, GuiGameList* parent, GuiList<FileData*>* list, char startLetter, GuiBoxData data, int textcolor, std::shared_ptr<Sound> & scrollsound, Font* font);
~GuiFastSelect(); ~GuiFastSelect();
void input(InputConfig* config, Input input); void input(InputConfig* config, Input input);
@ -39,7 +39,7 @@ private:
int mScrollTimer, mScrollOffset; int mScrollTimer, mScrollOffset;
bool mScrolling; bool mScrolling;
Sound* mScrollSound; std::shared_ptr<Sound> mScrollSound;
Font* mFont; Font* mFont;
}; };

View file

@ -269,7 +269,7 @@ void GuiList<listType>::setSelection(int i)
} }
template <typename listType> template <typename listType>
void GuiList<listType>::setScrollSound(Sound* sound) void GuiList<listType>::setScrollSound(std::shared_ptr<Sound> & sound)
{ {
mScrollSound = sound; mScrollSound = sound;
} }

View file

@ -34,7 +34,7 @@ public:
void setSelectorColor(unsigned int selectorColor); void setSelectorColor(unsigned int selectorColor);
void setSelectedTextColor(unsigned int selectedColor); void setSelectedTextColor(unsigned int selectedColor);
void setCentered(bool centered); void setCentered(bool centered);
void setScrollSound(Sound* sound); void setScrollSound(std::shared_ptr<Sound> & sound);
void setTextOffsetX(int textoffsetx); void setTextOffsetX(int textoffsetx);
int getObjectCount(); int getObjectCount();
@ -71,7 +71,7 @@ private:
std::vector<ListRow> mRowVector; std::vector<ListRow> mRowVector;
int mSelection; int mSelection;
Sound* mScrollSound; std::shared_ptr<Sound> mScrollSound;
}; };
#include "GuiList.cpp" #include "GuiList.cpp"

View file

@ -23,7 +23,7 @@ float GuiTheme::getFloat(std::string name)
return mFloatMap[name]; return mFloatMap[name];
} }
Sound* GuiTheme::getSound(std::string name) std::shared_ptr<Sound> & GuiTheme::getSound(std::string name)
{ {
return mSoundMap[name]; return mSoundMap[name];
} }
@ -63,10 +63,16 @@ GuiTheme::GuiTheme(Window* window, bool detailed, std::string path) : Gui(window
{ {
mDetailed = detailed; mDetailed = detailed;
mSoundMap["menuScroll"] = new Sound(); mSoundMap["menuScroll"] = std::shared_ptr<Sound>(new Sound);
mSoundMap["menuSelect"] = new Sound(); mSoundMap["menuSelect"] = std::shared_ptr<Sound>(new Sound);
mSoundMap["menuBack"] = new Sound(); mSoundMap["menuBack"] = std::shared_ptr<Sound>(new Sound);
mSoundMap["menuOpen"] = new Sound(); mSoundMap["menuOpen"] = std::shared_ptr<Sound>(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; mListFont = NULL;
mDescFont = NULL; mDescFont = NULL;

View file

@ -1,10 +1,12 @@
#ifndef _GUITHEME_H_ #ifndef _GUITHEME_H_
#define _GUITHEME_H_ #define _GUITHEME_H_
#include <memory>
#include "../Gui.h" #include "../Gui.h"
#include "../pugiXML/pugixml.hpp" #include "../pugiXML/pugixml.hpp"
#include "GuiBox.h" #include "GuiBox.h"
#include "../Sound.h" #include "../AudioManager.h"
#include "../Font.h" #include "../Font.h"
//This class loads an XML-defined list of Guis. //This class loads an XML-defined list of Guis.
@ -26,7 +28,7 @@ public:
unsigned int getColor(std::string name); unsigned int getColor(std::string name);
bool getBool(std::string name); bool getBool(std::string name);
float getFloat(std::string name); float getFloat(std::string name);
Sound* getSound(std::string name); std::shared_ptr<Sound> & getSound(std::string name);
std::string getString(std::string name); std::string getString(std::string name);
Font* getListFont(); Font* getListFont();
@ -53,7 +55,7 @@ private:
std::map<std::string, unsigned int> mColorMap; std::map<std::string, unsigned int> mColorMap;
std::map<std::string, bool> mBoolMap; std::map<std::string, bool> mBoolMap;
std::map<std::string, float> mFloatMap; std::map<std::string, float> mFloatMap;
std::map<std::string, Sound*> mSoundMap; std::map<std::string, std::shared_ptr<Sound>> mSoundMap;
std::map<std::string, std::string> mStringMap; std::map<std::string, std::string> mStringMap;
GuiBoxData mBoxData; GuiBoxData mBoxData;

View file

@ -119,10 +119,6 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
//initialize audio
AudioManager::init();
Window window; //don't call Window.init() because we manually pass the resolution to Renderer::init Window window; //don't call Window.init() because we manually pass the resolution to Renderer::init
window.getInputManager()->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 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 << "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; running = false;
}else{ }else{
//choose which GUI to open depending on Input configuration //choose which GUI to open depending on Input configuration
@ -222,7 +218,6 @@ int main(int argc, char* argv[])
Log::flush(); Log::flush();
} }
AudioManager::deinit();
Renderer::deinit(); Renderer::deinit();
SystemData::deleteSystems(); SystemData::deleteSystems();

View file

@ -3,13 +3,10 @@
std::string getHomePath() std::string getHomePath()
{ {
#ifdef _WIN32 //this gives you something like "/home/YOUR_USERNAME" on Linux and "C:\Users\YOUR_USERNAME\" on Windows
return "C:\\"; const char* home = getenv("HOME");
#else if(home == NULL)
const char* home = getenv("HOME"); return "";
if(home == NULL) else
return ""; return home;
else
return home;
#endif
} }