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 "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;
void init()
//iterate through all our samples
std::vector<std::shared_ptr<Sound>>::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> 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> & 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_
#define _AUDIOMANAGER_H_
class Sound;
#include <vector>
#include <memory>
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<std::shared_ptr<Sound>> sSoundVector;
static std::shared_ptr<AudioManager> 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> & sound);
static void unregisterSound(std::shared_ptr<Sound> & sound);
static void play();
virtual ~AudioManager();
};
#endif

View file

@ -1,26 +1,19 @@
#include "Sound.h"
#include <iostream>
#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;
}

View file

@ -2,25 +2,35 @@
#define _SOUND_H_
#include <string>
#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

View file

@ -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();
}

View file

@ -8,7 +8,7 @@ const int GuiFastSelect::SCROLLSPEED = 100;
const int GuiFastSelect::SCROLLDELAY = 507;
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));
if(mLetterID == std::string::npos)

View file

@ -13,7 +13,7 @@ class GuiGameList;
class GuiFastSelect : public Gui
{
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();
void input(InputConfig* config, Input input);
@ -39,7 +39,7 @@ private:
int mScrollTimer, mScrollOffset;
bool mScrolling;
Sound* mScrollSound;
std::shared_ptr<Sound> mScrollSound;
Font* mFont;
};

View file

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

View file

@ -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> & sound);
void setTextOffsetX(int textoffsetx);
int getObjectCount();
@ -71,7 +71,7 @@ private:
std::vector<ListRow> mRowVector;
int mSelection;
Sound* mScrollSound;
std::shared_ptr<Sound> mScrollSound;
};
#include "GuiList.cpp"

View file

@ -23,7 +23,7 @@ float GuiTheme::getFloat(std::string name)
return mFloatMap[name];
}
Sound* GuiTheme::getSound(std::string name)
std::shared_ptr<Sound> & 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<Sound>(new Sound);
mSoundMap["menuSelect"] = std::shared_ptr<Sound>(new Sound);
mSoundMap["menuBack"] = std::shared_ptr<Sound>(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;
mDescFont = NULL;

View file

@ -1,10 +1,12 @@
#ifndef _GUITHEME_H_
#define _GUITHEME_H_
#include <memory>
#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<Sound> & getSound(std::string name);
std::string getString(std::string name);
Font* getListFont();
@ -53,7 +55,7 @@ private:
std::map<std::string, unsigned int> mColorMap;
std::map<std::string, bool> mBoolMap;
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;
GuiBoxData mBoxData;

View file

@ -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();

View file

@ -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;
}