Refactored SDL mixer manager. Created a SdlMixerManager subclass for Mac OSX.

svn-id: r50198
This commit is contained in:
Alejandro Marzini 2010-06-24 04:11:54 +00:00
parent 3029e50528
commit d89cb33bcb
6 changed files with 257 additions and 177 deletions

View file

@ -0,0 +1,130 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#if defined(MACOSX)
#include "backends/mixer/macosx/macosx-mixer.h"
MacOSXMixerManager::MacOSXMixerManager()
:
_soundMutex(0), _soundCond(0), _soundThread(0),
_soundThreadIsRunning(false), _soundThreadShouldQuit(false) {
}
MacOSXMixerManager::~MacOSXMixerManager() {
deinitThreadedMixer();
}
void MacOSXMixerManager::startAudio() {
_soundThreadIsRunning = false;
_soundThreadShouldQuit = false;
// Create mutex and condition variable
_soundMutex = SDL_CreateMutex();
_soundCond = SDL_CreateCond();
// Create two sound buffers
_activeSoundBuf = 0;
uint bufSize = _obtainedRate.samples * 4;
_soundBufSize = bufSize;
_soundBuffers[0] = (byte *)calloc(1, bufSize);
_soundBuffers[1] = (byte *)calloc(1, bufSize);
_soundThreadIsRunning = true;
// Finally start the thread
_soundThread = SDL_CreateThread(mixerProducerThreadEntry, this);
SdlMixerManager::startAudio();
}
void MacOSXMixerManager::mixerProducerThread() {
byte nextSoundBuffer;
SDL_LockMutex(_soundMutex);
while (true) {
// Wait till we are allowed to produce data
SDL_CondWait(_soundCond, _soundMutex);
if (_soundThreadShouldQuit)
break;
// Generate samples and put them into the next buffer
nextSoundBuffer = _activeSoundBuf ^ 1;
_mixer->mixCallback(_soundBuffers[nextSoundBuffer], _soundBufSize);
// Swap buffers
_activeSoundBuf = nextSoundBuffer;
}
SDL_UnlockMutex(_soundMutex);
}
int SDLCALL MacOSXMixerManager::mixerProducerThreadEntry(void *arg) {
MacOSXMixerManager *mixer = (MacOSXMixerManager *)arg;
assert(mixer);
mixer->mixerProducerThread();
return 0;
}
void MacOSXMixerManager::deinitThreadedMixer() {
// Kill thread?? _soundThread
if (_soundThreadIsRunning) {
// Signal the producer thread to end, and wait for it to actually finish.
_soundThreadShouldQuit = true;
SDL_CondBroadcast(_soundCond);
SDL_WaitThread(_soundThread, NULL);
// Kill the mutex & cond variables.
// Attention: AT this point, the mixer callback must not be running
// anymore, else we will crash!
SDL_DestroyMutex(_soundMutex);
SDL_DestroyCond(_soundCond);
_soundThreadIsRunning = false;
free(_soundBuffers[0]);
free(_soundBuffers[1]);
}
}
void MacOSXMixerManager::callbackHandler(byte *samples, int len) {
assert(_mixer);
assert((int)_soundBufSize == len);
// Lock mutex, to ensure our data is not overwritten by the producer thread
SDL_LockMutex(_soundMutex);
// Copy data from the current sound buffer
memcpy(samples, _soundBuffers[_activeSoundBuf], len);
// Unlock mutex and wake up the produced thread
SDL_UnlockMutex(_soundMutex);
SDL_CondSignal(_soundCond);
}
#endif

View file

@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef BACKENDS_MIXER_MACOSX_H
#define BACKENDS_MIXER_MACOSX_H
#include "backends/mixer/sdl/sdl-mixer.h"
class MacOSXMixerManager : public SdlMixerManager {
public:
MacOSXMixerManager();
~MacOSXMixerManager();
protected:
SDL_mutex *_soundMutex;
SDL_cond *_soundCond;
SDL_Thread *_soundThread;
bool _soundThreadIsRunning;
bool _soundThreadShouldQuit;
byte _activeSoundBuf;
uint _soundBufSize;
byte *_soundBuffers[2];
void mixerProducerThread();
void deinitThreadedMixer();
static int SDLCALL mixerProducerThreadEntry(void *arg);
void startAudio();
void callbackHandler(byte *samples, int len);
};
#endif

View file

@ -26,51 +26,55 @@
#if defined(WIN32) || defined(UNIX) || defined(MACOSX)
#include "backends/mixer/sdl/sdl-mixer.h"
#include "common/system.h"
#include "common/config-manager.h"
//#define SAMPLES_PER_SEC 11025
#define SAMPLES_PER_SEC 22050
//#define SAMPLES_PER_SEC 44100
SdlMixerImpl::SdlMixerImpl(OSystem *system)
SdlMixerManager::SdlMixerManager()
:
#if MIXER_DOUBLE_BUFFERING
_soundMutex(0), _soundCond(0), _soundThread(0),
_soundThreadIsRunning(false), _soundThreadShouldQuit(false),
#endif
MixerImpl(system, getSamplesPerSec()) {
if (_openAudio) {
setReady(true);
_mixer(0) {
#if MIXER_DOUBLE_BUFFERING
initThreadedMixer(_obtainedRate.samples * 4);
#endif
// start the sound system
SDL_PauseAudio(0);
}
else {
setReady(false);
}
}
SdlMixerImpl::~SdlMixerImpl() {
setReady(false);
SdlMixerManager::~SdlMixerManager() {
_mixer->setReady(false);
SDL_CloseAudio();
#if MIXER_DOUBLE_BUFFERING
deinitThreadedMixer();
#endif
delete _mixer;
}
uint SdlMixerImpl::getSamplesPerSec() {
void SdlMixerManager::init() {
// Start SDL Audio subsystem
if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1) {
error("Could not initialize SDL: %s", SDL_GetError());
}
// Get the desired audio specs
SDL_AudioSpec desired = getAudioSpec();
// Start SDL audio with the desired specs
if (SDL_OpenAudio(&desired, &_obtainedRate) != 0) {
warning("Could not open audio device: %s", SDL_GetError());
_mixer = new Audio::MixerImpl(g_system, desired.freq);
assert(_mixer);
_mixer->setReady(false);
} else {
debug(1, "Output sample rate: %d Hz", _obtainedRate.freq);
_mixer = new Audio::MixerImpl(g_system, _obtainedRate.freq);
assert(_mixer);
_mixer->setReady(true);
startAudio();
}
}
SDL_AudioSpec SdlMixerManager::getAudioSpec() {
SDL_AudioSpec desired;
// Determine the desired output sampling frequency.
@ -93,122 +97,27 @@ uint SdlMixerImpl::getSamplesPerSec() {
desired.format = AUDIO_S16SYS;
desired.channels = 2;
desired.samples = (uint16)samples;
desired.callback = mixSdlCallback;
desired.callback = sdlCallback;
desired.userdata = this;
if (SDL_OpenAudio(&desired, &_obtainedRate) != 0) {
warning("Could not open audio device: %s", SDL_GetError());
_openAudio = false;
} else {
// Note: This should be the obtained output rate, but it seems that at
// least on some platforms SDL will lie and claim it did get the rate
// even if it didn't. Probably only happens for "weird" rates, though.
samplesPerSec = _obtainedRate.freq;
debug(1, "Output sample rate: %d Hz", samplesPerSec);
_openAudio = true;
}
return samplesPerSec;
return desired;
}
#if MIXER_DOUBLE_BUFFERING
void SdlMixerImpl::mixerProducerThread() {
byte nextSoundBuffer;
SDL_LockMutex(_soundMutex);
while (true) {
// Wait till we are allowed to produce data
SDL_CondWait(_soundCond, _soundMutex);
if (_soundThreadShouldQuit)
break;
// Generate samples and put them into the next buffer
nextSoundBuffer = _activeSoundBuf ^ 1;
mixCallback(_soundBuffers[nextSoundBuffer], _soundBufSize);
// Swap buffers
_activeSoundBuf = nextSoundBuffer;
}
SDL_UnlockMutex(_soundMutex);
void SdlMixerManager::startAudio() {
// Start the sound system
SDL_PauseAudio(0);
}
int SDLCALL SdlMixerImpl::mixerProducerThreadEntry(void *arg) {
SdlMixerImpl *mixer = (SdlMixerImpl *)arg;
assert(mixer);
mixer->mixerProducerThread();
return 0;
void SdlMixerManager::callbackHandler(byte *samples, int len) {
assert(_mixer);
_mixer->mixCallback(samples, len);
}
void SdlMixerManager::sdlCallback(void *this_, byte *samples, int len) {
SdlMixerManager *manager = (SdlMixerManager *)this_;
assert(manager);
void SdlMixerImpl::initThreadedMixer(uint bufSize) {
_soundThreadIsRunning = false;
_soundThreadShouldQuit = false;
// Create mutex and condition variable
_soundMutex = SDL_CreateMutex();
_soundCond = SDL_CreateCond();
// Create two sound buffers
_activeSoundBuf = 0;
_soundBufSize = bufSize;
_soundBuffers[0] = (byte *)calloc(1, bufSize);
_soundBuffers[1] = (byte *)calloc(1, bufSize);
_soundThreadIsRunning = true;
// Finally start the thread
_soundThread = SDL_CreateThread(mixerProducerThreadEntry, this);
}
void SdlMixerImpl::deinitThreadedMixer() {
// Kill thread?? _soundThread
if (_soundThreadIsRunning) {
// Signal the producer thread to end, and wait for it to actually finish.
_soundThreadShouldQuit = true;
SDL_CondBroadcast(_soundCond);
SDL_WaitThread(_soundThread, NULL);
// Kill the mutex & cond variables.
// Attention: AT this point, the mixer callback must not be running
// anymore, else we will crash!
SDL_DestroyMutex(_soundMutex);
SDL_DestroyCond(_soundCond);
_soundThreadIsRunning = false;
free(_soundBuffers[0]);
free(_soundBuffers[1]);
}
}
void SdlMixerImpl::mixSdlCallback(void *arg, byte *samples, int len) {
SdlMixerImpl *mixer = (SdlMixerImpl *)arg;
assert(mixer);
assert((int)mixer->getSoundBufSize() == len);
// Lock mutex, to ensure our data is not overwritten by the producer thread
g_system->lockMutex((OSystem::MutexRef)mixer->getSoundMutex());
// Copy data from the current sound buffer
memcpy(samples, mixer->getActiveSoundBuf(), len);
// Unlock mutex and wake up the produced thread
g_system->unlockMutex((OSystem::MutexRef)mixer->getSoundMutex());
SDL_CondSignal(mixer->getSoundCond());
}
#else
void SdlMixerImpl::mixSdlCallback(void *sys, byte *samples, int len) {
Audio::MixerImpl *mixer = (Audio::MixerImpl *)sys;
assert(mixer);
mixer->mixCallback(samples, len);
manager->callbackHandler(samples, len);
}
#endif
#endif

View file

@ -34,51 +34,24 @@
#include "sound/mixer_intern.h"
#if defined(MACOSX)
// On Mac OS X, we need to double buffer the audio buffer, else anything
// which produces sampled data with high latency (like the MT-32 emulator)
// will sound terribly.
// This could be enabled for more / most ports in the future, but needs some
// testing.
#define MIXER_DOUBLE_BUFFERING 1
#endif
class SdlMixerImpl : public Audio::MixerImpl {
class SdlMixerManager {
public:
SdlMixerManager();
~SdlMixerManager();
SdlMixerImpl(OSystem *system);
~SdlMixerImpl();
virtual void init();
Audio::Mixer *getMixer() { return (Audio::Mixer *)_mixer; }
protected:
Audio::MixerImpl *_mixer;
SDL_AudioSpec _obtainedRate;
bool _openAudio;
uint getSamplesPerSec();
virtual SDL_AudioSpec getAudioSpec();
virtual void startAudio();
static void mixSdlCallback(void *s, byte *samples, int len);
#ifdef MIXER_DOUBLE_BUFFERING
SDL_mutex *_soundMutex;
SDL_cond *_soundCond;
SDL_Thread *_soundThread;
bool _soundThreadIsRunning;
bool _soundThreadShouldQuit;
byte _activeSoundBuf;
uint _soundBufSize;
byte *_soundBuffers[2];
void mixerProducerThread();
static int SDLCALL mixerProducerThreadEntry(void *arg);
void initThreadedMixer(uint bufSize);
void deinitThreadedMixer();
public:
SDL_mutex *getSoundMutex() { return _soundMutex; }
SDL_cond *getSoundCond() { return _soundCond; }
uint getSoundBufSize() { return _soundBufSize; }
byte *getActiveSoundBuf() { return _soundBuffers[_activeSoundBuf]; }
#endif
virtual void callbackHandler(byte *samples, int len);
static void sdlCallback(void *this_, byte *samples, int len);
};
#endif

View file

@ -34,7 +34,6 @@
#include "backends/audiocd/sdl/sdl-audiocd.h"
#include "backends/events/sdl/sdl-events.h"
#include "backends/mutex/sdl/sdl-mutex.h"
#include "backends/mixer/sdl/sdl-mixer.h"
#include "backends/timer/sdl/sdl-timer.h"
#include "icons/scummvm.xpm"
@ -46,7 +45,8 @@
OSystem_SDL::OSystem_SDL()
:
_inited(false),
_initedSDL(false) {
_initedSDL(false),
_mixerManager(0) {
}
@ -72,8 +72,12 @@ void OSystem_SDL::initBackend() {
if (_savefileManager == 0)
_savefileManager = new DefaultSaveFileManager();
if (_mixer == 0)
_mixer = new SdlMixerImpl(this);
if (_mixerManager == 0) {
_mixerManager = new SdlMixerManager();
// Setup and start mixer
_mixerManager->init();
}
if (_timerManager == 0)
_timerManager = new SdlTimerManager();
@ -166,8 +170,8 @@ void OSystem_SDL::deinit() {
_graphicsManager = 0;
delete _audiocdManager;
_audiocdManager = 0;
delete _mixer;
_mixer = 0;
delete _mixerManager;
_mixerManager = 0;
delete _timerManager;
_timerManager = 0;
delete _mutexManager;
@ -263,3 +267,8 @@ void OSystem_SDL::getTimeAndDate(TimeDate &td) const {
td.tm_mon = t.tm_mon;
td.tm_year = t.tm_year;
}
Audio::Mixer *OSystem_SDL::getMixer() {
assert(_mixerManager);
return _mixerManager->getMixer();
}

View file

@ -34,6 +34,7 @@
#include "backends/modular-backend.h"
#include "backends/graphics/sdl/sdl-graphics.h"
#include "backends/mixer/sdl/sdl-mixer.h"
class OSystem_SDL : public ModularBackend {
public:
@ -61,9 +62,12 @@ public:
virtual void delayMillis(uint msecs);
virtual void getTimeAndDate(TimeDate &td) const;
virtual Audio::Mixer *getMixer();
protected:
bool _inited;
bool _initedSDL;
SdlMixerManager *_mixerManager;
virtual void initSDL();