Commit 1e3a85a3 authored by Rafael Kitover's avatar Rafael Kitover
Browse files

cleanup SoundSDL #139 #130 #97 #67 #65 #46 #47

Rewrite SoundSDL (the SDL sound driver).

Clean up the code and eliminate all deadlocks/hangs/crashes (hopefully.)

Many of the deadlocks were caused by initialize() not de-initializing
properly and causing the audio callback thread to deadlock, fix this.

Also use better logic for the semaphore controls, which will also
hopefully increase audio quality.

Use better logic for the throttle control, with throttle == 0 being the
same as throttle == 100 and implement setThrottle().

Also increase the buffer size to 300ms and the number of samples to
2048, for hopefully less choppiness in audio overall.
parent f88faef1
......@@ -15,6 +15,8 @@
// along with this program; if not, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <cmath>
#include <iostream>
#include "SoundSDL.h"
#include "ConfigManager.h"
#include "../gba/Globals.h"
......@@ -22,173 +24,161 @@
extern int emulating;
// Hold up to 32 ms of data in the ring buffer
const float SoundSDL::_delay = 0.032f;
// Hold up to 300 ms of data in the ring buffer
const double SoundSDL::buftime = 0.300;
SoundSDL::SoundSDL():
_rbuf(0),
_dev(-1),
current_rate(throttle),
_initialized(false)
{
samples_buf(0),
sound_device(-1),
current_rate(throttle ? throttle : 100),
initialized(false)
{}
void SoundSDL::soundCallback(void* data, uint8_t* stream, int len) {
reinterpret_cast<SoundSDL*>(data)->read(reinterpret_cast<uint16_t *>(stream), len);
}
void SoundSDL::soundCallback(void *data, uint8_t *stream, int len)
{
reinterpret_cast<SoundSDL*>(data)->read(reinterpret_cast<uint16_t *>(stream), len);
bool SoundSDL::should_wait() {
return emulating && !speedup && current_rate && !gba_joybus_active;
}
bool SoundSDL::should_wait()
{
return emulating && !speedup && throttle && !gba_joybus_active;
std::size_t SoundSDL::buffer_size() {
SDL_LockMutex(mutex);
std::size_t size = samples_buf.used();
SDL_UnlockMutex(mutex);
return size;
}
void SoundSDL::read(uint16_t * stream, int length)
{
if (!_initialized || length <= 0)
return;
void SoundSDL::read(uint16_t* stream, int length) {
if (!initialized || length <= 0)
return;
if (!emulating) {
SDL_memset(stream, _audio_spec.silence, length);
return;
}
SDL_memset(stream, audio_spec.silence, length);
if (should_wait())
SDL_SemWait (_semBufferFull);
if (!emulating)
return;
SDL_mutexP(_mutex);
if (!buffer_size())
if (should_wait())
SDL_SemWait(data_available);
else
return;
_rbuf.read(stream, std::min(static_cast<std::size_t>(length) / 2, _rbuf.used()));
SDL_LockMutex(mutex);
SDL_mutexV(_mutex);
samples_buf.read(stream, std::min((std::size_t)(length / 2), samples_buf.used()));
SDL_SemPost (_semBufferEmpty);
}
SDL_UnlockMutex(mutex);
void SoundSDL::write(uint16_t * finalWave, int length)
{
if (!_initialized)
return;
if (SDL_GetAudioDeviceStatus(_dev) != SDL_AUDIO_PLAYING)
SDL_PauseAudioDevice(_dev, 0);
SDL_mutexP(_mutex);
unsigned int samples = length / 4;
std::size_t avail;
while ((avail = _rbuf.avail() / 2) < samples)
{
_rbuf.write(finalWave, avail * 2);
finalWave += avail * 2;
samples -= avail;
SDL_mutexV(_mutex);
SDL_SemPost(_semBufferFull);
if (should_wait())
{
SDL_SemWait(_semBufferEmpty);
if (throttle > 0 && throttle != current_rate)
{
SDL_CloseAudioDevice(_dev);
//Reinit on throttle change:
init(soundGetSampleRate());
current_rate = throttle;
}
}
else
{
// Drop the remaining of the audio data
return;
}
SDL_mutexP(_mutex);
}
_rbuf.write(finalWave, samples * 2);
SDL_mutexV(_mutex);
SDL_SemPost(data_read);
}
void SoundSDL::write(uint16_t * finalWave, int length) {
if (!initialized)
return;
SDL_LockMutex(mutex);
if (SDL_GetAudioDeviceStatus(sound_device) != SDL_AUDIO_PLAYING)
SDL_PauseAudioDevice(sound_device, 0);
unsigned int samples = length / 4;
std::size_t avail;
while ((avail = samples_buf.avail() / 2) < samples) {
samples_buf.write(finalWave, avail * 2);
finalWave += avail * 2;
samples -= avail;
SDL_UnlockMutex(mutex);
SDL_SemPost(data_available);
bool SoundSDL::init(long sampleRate)
{
SDL_AudioSpec audio;
SDL_memset(&audio, 0, sizeof(audio));
audio.freq = throttle ? sampleRate * (throttle / 100.0) : sampleRate;
audio.format = AUDIO_S16SYS;
audio.channels = 2;
audio.samples = 1024;
audio.callback = soundCallback;
audio.userdata = this;
if (!SDL_WasInit(SDL_INIT_AUDIO)) SDL_Init(SDL_INIT_AUDIO);
_dev = SDL_OpenAudioDevice(NULL, 0, &audio, &_audio_spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
if(_dev < 0)
{
fprintf(stderr,"Failed to open audio: %s\n", SDL_GetError());
return false;
}
_rbuf.reset(_delay * sampleRate * 2);
if (!_initialized)
{
_mutex = SDL_CreateMutex();
_semBufferFull = SDL_CreateSemaphore (0);
_semBufferEmpty = SDL_CreateSemaphore (1);
_initialized = true;
}
return true;
if (should_wait())
SDL_SemWait(data_read);
else
// Drop the remaining of the audio data
return;
SDL_LockMutex(mutex);
}
samples_buf.write(finalWave, samples * 2);
SDL_UnlockMutex(mutex);
}
SoundSDL::~SoundSDL()
{
if (!_initialized)
return;
SDL_mutexP(_mutex);
int iSave = emulating;
emulating = 0;
SDL_SemPost(_semBufferFull);
SDL_SemPost(_semBufferEmpty);
SDL_mutexV(_mutex);
bool SoundSDL::init(long sampleRate) {
if (initialized) deinit();
SDL_DestroySemaphore(_semBufferFull);
SDL_DestroySemaphore(_semBufferEmpty);
_semBufferFull = NULL;
_semBufferEmpty = NULL;
SDL_AudioSpec audio;
SDL_memset(&audio, 0, sizeof(audio));
audio.freq = sampleRate * (current_rate / 100.0);
audio.format = AUDIO_S16SYS;
audio.channels = 2;
audio.samples = 2048;
audio.callback = soundCallback;
audio.userdata = this;
SDL_DestroyMutex(_mutex);
_mutex = NULL;
if (!SDL_WasInit(SDL_INIT_AUDIO)) SDL_Init(SDL_INIT_AUDIO);
SDL_CloseAudioDevice(_dev);
sound_device = SDL_OpenAudioDevice(NULL, 0, &audio, &audio_spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
emulating = iSave;
if(sound_device < 0) {
std::cerr << "Failed to open audio: " << SDL_GetError() << std::endl;
return false;
}
_initialized = false;
samples_buf.reset(std::ceil(buftime * sampleRate * 2));
mutex = SDL_CreateMutex();
data_available = SDL_CreateSemaphore(0);
data_read = SDL_CreateSemaphore(1);
return initialized = true;
}
void SoundSDL::pause()
{
if (!_initialized)
return;
void SoundSDL::deinit() {
if (!initialized)
return;
SDL_LockMutex(mutex);
int is_emulating = emulating;
emulating = 0;
SDL_SemPost(data_available);
SDL_SemPost(data_read);
SDL_UnlockMutex(mutex);
SDL_DestroySemaphore(data_available);
data_available = nullptr;
SDL_DestroySemaphore(data_read);
data_read = nullptr;
SDL_DestroyMutex(mutex);
mutex = nullptr;
SDL_CloseAudioDevice(sound_device);
emulating = is_emulating;
initialized = false;
}
//SDL_PauseAudioDevice(_dev, 1); // this causes thread deadlocks
SoundSDL::~SoundSDL() {
deinit();
}
void SoundSDL::resume()
{
if (!_initialized)
return;
void SoundSDL::pause() {}
void SoundSDL::resume() {}
//SDL_PauseAudioDevice(_dev, 0); // this causes thread deadlocks
void SoundSDL::reset() {
init(soundGetSampleRate());
}
void SoundSDL::reset()
{
void SoundSDL::setThrottle(unsigned short throttle_) {
current_rate = throttle_ ? throttle_ : 100;
reset();
}
......@@ -23,9 +23,8 @@
#include "SDL.h"
class SoundSDL : public SoundDriver
{
public:
class SoundSDL : public SoundDriver {
public:
SoundSDL();
virtual ~SoundSDL();
......@@ -34,26 +33,31 @@ class SoundSDL : public SoundDriver
virtual void reset();
virtual void resume();
virtual void write(uint16_t *finalWave, int length);
virtual void setThrottle(unsigned short throttle_);
protected:
static void soundCallback(void* data, uint8_t* stream, int length);
virtual void read(uint16_t* stream, int length);
virtual bool should_wait();
virtual std::size_t buffer_size();
virtual void deinit();
private:
RingBuffer<uint16_t> _rbuf;
private:
RingBuffer<uint16_t> samples_buf;
SDL_mutex *_mutex;
SDL_sem *_semBufferFull;
SDL_sem *_semBufferEmpty;
SDL_AudioDeviceID _dev;
SDL_AudioSpec _audio_spec;
SDL_AudioDeviceID sound_device = -1;
int current_rate;
SDL_mutex* mutex;
SDL_sem* data_available;
SDL_sem* data_read;
SDL_AudioSpec audio_spec;
bool _initialized;
unsigned short current_rate;
// Defines what delay in seconds we keep in the sound buffer
static const float _delay;
bool initialized = false;
static void soundCallback(void *data, uint8_t *stream, int length);
virtual void read(uint16_t *stream, int length);
// Defines what delay in seconds we keep in the sound buffer
static const double buftime;
};
#endif // __VBA_SOUND_SDL_H__
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment