diff --git a/Makefile b/Makefile index 9df7eb8..48d12c4 100644 --- a/Makefile +++ b/Makefile @@ -121,6 +121,16 @@ else LDFLAGS += -lc -lm -ldl endif +ifeq ($(SDL_AUDIO_DRIVER),portaudio) +ifeq (,$(PKG_CONFIG)) +SDL_CFLAGS += -I/usr/local/include +SDL_LDFLAGS += -lportaudio +else +SDL_CFLAGS += $(shell $(PKG_CONFIG) --cflags portaudio) +SDL_LDFLAGS += $(shell $(PKG_CONFIG) --libs portaudio) +endif +endif + ifeq ($(PLATFORM),Darwin) SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> $(NULL)) CFLAGS += -F/Library/Frameworks -mmacosx-version-min=10.9 diff --git a/SDL/audio/portaudio.c b/SDL/audio/portaudio.c new file mode 100644 index 0000000..5ce0e24 --- /dev/null +++ b/SDL/audio/portaudio.c @@ -0,0 +1,133 @@ +#include "audio.h" +#include +#include + + +#define AUDIO_FREQUENCY 96000 +static PaStream *stream; +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static size_t buffer_position = 0, buffer_size = 0, buffer_needed = 0; +static GB_sample_t *buffer = NULL; +static bool playing = false; + + +bool GB_audio_is_playing(void) +{ + return playing; +} + +void GB_audio_set_paused(bool paused) +{ + if (!stream) return; + + if (paused) { + Pa_Sleep(1000); + pthread_mutex_lock(&lock); + playing = false; + GB_audio_clear_queue(); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + Pa_StopStream(stream); + } + else { + Pa_StartStream(stream); + playing = true; + } +} + +void GB_audio_clear_queue(void) +{ + buffer_position = 0; +} + +unsigned GB_audio_get_frequency(void) +{ + return stream? Pa_GetStreamInfo(stream)->sampleRate : 0; +} + +size_t GB_audio_get_queue_length(void) +{ + return buffer_position; +} + +void GB_audio_queue_sample(GB_sample_t *sample) +{ + if (!stream) return; + + pthread_mutex_lock(&lock); + if (!GB_audio_is_playing()) { + pthread_mutex_unlock(&lock); + return; + } + + if (buffer_position == buffer_size) { + if (buffer_size >= 0x4000) { + buffer_position = 0; + pthread_mutex_unlock(&lock); + return; + } + + if (buffer_size == 0) { + buffer_size = 512; + } + else { + buffer_size += buffer_size >> 2; + } + buffer = realloc(buffer, sizeof(*sample) * buffer_size); + } + buffer[buffer_position++] = *sample; + if (buffer_position == buffer_needed) { + pthread_cond_signal(&cond); + buffer_needed = 0; + } + pthread_mutex_unlock(&lock); +} + +static int callback(const void *in, void *_out, + unsigned long frames, + const PaStreamCallbackTimeInfo *time_info, + PaStreamCallbackFlags status_flags, + void *unused) +{ + GB_sample_t *out = (GB_sample_t *)_out; + pthread_mutex_lock(&lock); + + if (buffer_position < frames) { + buffer_needed = frames; + pthread_cond_wait(&cond, &lock); + } + + if (!playing) { + memset(out, 0, frames * sizeof(*out)); + pthread_mutex_unlock(&lock); + return 0; + } + + if (buffer_position >= frames && buffer_position < frames + 4800) { + memcpy(out, buffer, frames * sizeof(*buffer)); + memmove(buffer, buffer + frames, (buffer_position - frames) * sizeof(*buffer)); + buffer_position -= frames; + } + else { + memcpy(out, buffer + (buffer_position - frames), frames * sizeof(*buffer)); + buffer_position = 0; + } + pthread_mutex_unlock(&lock); + + return 0; +} + +void GB_audio_init(void) +{ + Pa_Initialize(); + Pa_OpenDefaultStream(&stream, + 0, + 2, + paInt16, + AUDIO_FREQUENCY, + paFramesPerBufferUnspecified, + callback, + NULL); + +}