Make the portaudio frontend sync to audio

This commit is contained in:
Lior Halphon 2020-04-28 19:43:36 +03:00
parent 1abb6e62f4
commit b4335e5934
4 changed files with 55 additions and 77 deletions

View File

@ -12,5 +12,6 @@ unsigned GB_audio_get_frequency(void);
size_t GB_audio_get_queue_length(void); size_t GB_audio_get_queue_length(void);
void GB_audio_queue_sample(GB_sample_t *sample); void GB_audio_queue_sample(GB_sample_t *sample);
void GB_audio_init(void); void GB_audio_init(void);
bool GB_audio_set_sync_mode(bool sync_mode);
#endif /* sdl_audio_h */ #endif /* sdl_audio_h */

View File

@ -1,20 +1,22 @@
#include "audio.h" #include "audio.h"
#include <portaudio.h> #include <portaudio.h>
#include <pthread.h>
#define AUDIO_FREQUENCY 96000 #define AUDIO_FREQUENCY 96000
static PaStream *stream; static PaStream *stream;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static size_t buffer_position = 0;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static GB_sample_t buffer[4096];
static size_t buffer_position = 0, buffer_size = 0, buffer_needed = 0; static bool blocking_mode = false;
static GB_sample_t *buffer = NULL;
static bool playing = false;
bool GB_audio_set_sync_mode(bool sync_mode)
{
blocking_mode = sync_mode;
return true;
}
bool GB_audio_is_playing(void) bool GB_audio_is_playing(void)
{ {
return playing; return stream && Pa_IsStreamActive(stream);
} }
void GB_audio_set_paused(bool paused) void GB_audio_set_paused(bool paused)
@ -22,17 +24,10 @@ void GB_audio_set_paused(bool paused)
if (!stream) return; if (!stream) return;
if (paused) { 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); Pa_StopStream(stream);
} }
else { else {
Pa_StartStream(stream); Pa_StartStream(stream);
playing = true;
} }
} }
@ -55,67 +50,32 @@ void GB_audio_queue_sample(GB_sample_t *sample)
{ {
if (!stream) return; if (!stream) return;
pthread_mutex_lock(&lock); if (buffer_position < sizeof(buffer) / sizeof(buffer[0])) {
if (!GB_audio_is_playing()) { buffer[buffer_position++] = *sample;
pthread_mutex_unlock(&lock); if (blocking_mode) {
return;
}
if (buffer_position == buffer_size) {
if (buffer_size >= 0x4000) {
buffer_position = 0;
pthread_mutex_unlock(&lock);
return; 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 { else {
memcpy(out, buffer + (buffer_position - frames), frames * sizeof(*buffer)); if (blocking_mode) {
buffer_position = 0; Pa_WriteStream(stream, buffer, sizeof(buffer) / sizeof(buffer[0]));
buffer_position = 0;
return;
}
}
size_t write_legnth = Pa_GetStreamWriteAvailable(stream);
if (write_legnth > buffer_position) {
write_legnth = buffer_position;
}
if (write_legnth) {
Pa_WriteStream(stream, buffer, write_legnth);
memmove(buffer, buffer + write_legnth, sizeof(buffer[0]) * (buffer_position - write_legnth));
buffer_position -= write_legnth;
} }
pthread_mutex_unlock(&lock);
return 0;
} }
void GB_audio_init(void) void GB_audio_init(void)
@ -126,8 +86,8 @@ void GB_audio_init(void)
2, 2,
paInt16, paInt16,
AUDIO_FREQUENCY, AUDIO_FREQUENCY,
paFramesPerBufferUnspecified, sizeof(buffer) / sizeof(buffer[0]),
callback, NULL,
NULL); NULL);
} }

View File

@ -25,6 +25,11 @@
static SDL_AudioDeviceID device_id; static SDL_AudioDeviceID device_id;
static SDL_AudioSpec want_aspec, have_aspec; static SDL_AudioSpec want_aspec, have_aspec;
bool GB_audio_set_sync_mode(bool sync_mode)
{
return false;
}
bool GB_audio_is_playing(void) bool GB_audio_is_playing(void)
{ {
return SDL_GetAudioDeviceStatus(device_id) == SDL_AUDIO_PLAYING; return SDL_GetAudioDeviceStatus(device_id) == SDL_AUDIO_PLAYING;
@ -58,6 +63,7 @@ void GB_audio_queue_sample(GB_sample_t *sample)
void GB_audio_init(void) void GB_audio_init(void)
{ {
SDL_Init(SDL_INIT_AUDIO);
/* Configure Audio */ /* Configure Audio */
memset(&want_aspec, 0, sizeof(want_aspec)); memset(&want_aspec, 0, sizeof(want_aspec));
want_aspec.freq = AUDIO_FREQUENCY; want_aspec.freq = AUDIO_FREQUENCY;

View File

@ -27,6 +27,16 @@ static char *filename = NULL;
static typeof(free) *free_function = NULL; static typeof(free) *free_function = NULL;
static char *battery_save_path_ptr; static char *battery_save_path_ptr;
static void update_turbo_mode(void)
{
if (GB_audio_set_sync_mode(!turbo_down) && !turbo_down) {
GB_set_turbo_mode(&gb, true, true);
}
else {
// Sync mode is not supported
GB_set_turbo_mode(&gb, turbo_down, turbo_down && rewind_down);
}
}
void set_filename(const char *new_filename, typeof(free) *new_free_function) void set_filename(const char *new_filename, typeof(free) *new_free_function)
{ {
@ -159,7 +169,7 @@ static void handle_events(GB_gameboy_t *gb)
else if (button == JOYPAD_BUTTON_TURBO) { else if (button == JOYPAD_BUTTON_TURBO) {
GB_audio_clear_queue(); GB_audio_clear_queue();
turbo_down = event.type == SDL_JOYBUTTONDOWN; turbo_down = event.type == SDL_JOYBUTTONDOWN;
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down); update_turbo_mode();
} }
else if (button == JOYPAD_BUTTON_SLOW_MOTION) { else if (button == JOYPAD_BUTTON_SLOW_MOTION) {
underclock_down = event.type == SDL_JOYBUTTONDOWN; underclock_down = event.type == SDL_JOYBUTTONDOWN;
@ -169,7 +179,7 @@ static void handle_events(GB_gameboy_t *gb)
if (event.type == SDL_JOYBUTTONUP) { if (event.type == SDL_JOYBUTTONUP) {
rewind_paused = false; rewind_paused = false;
} }
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down); update_turbo_mode();
} }
else if (button == JOYPAD_BUTTON_MENU && event.type == SDL_JOYBUTTONDOWN) { else if (button == JOYPAD_BUTTON_MENU && event.type == SDL_JOYBUTTONDOWN) {
open_menu(); open_menu();
@ -311,14 +321,14 @@ static void handle_events(GB_gameboy_t *gb)
if (event.key.keysym.scancode == configuration.keys[8]) { if (event.key.keysym.scancode == configuration.keys[8]) {
turbo_down = event.type == SDL_KEYDOWN; turbo_down = event.type == SDL_KEYDOWN;
GB_audio_clear_queue(); GB_audio_clear_queue();
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down); update_turbo_mode();
} }
else if (event.key.keysym.scancode == configuration.keys_2[0]) { else if (event.key.keysym.scancode == configuration.keys_2[0]) {
rewind_down = event.type == SDL_KEYDOWN; rewind_down = event.type == SDL_KEYDOWN;
if (event.type == SDL_KEYUP) { if (event.type == SDL_KEYUP) {
rewind_paused = false; rewind_paused = false;
} }
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down); update_turbo_mode();
} }
else if (event.key.keysym.scancode == configuration.keys_2[1]) { else if (event.key.keysym.scancode == configuration.keys_2[1]) {
underclock_down = event.type == SDL_KEYDOWN; underclock_down = event.type == SDL_KEYDOWN;
@ -615,7 +625,7 @@ int main(int argc, char **argv)
signal(SIGINT, debugger_interrupt); signal(SIGINT, debugger_interrupt);
SDL_Init(SDL_INIT_EVERYTHING); SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
@ -683,6 +693,7 @@ int main(int argc, char **argv)
connect_joypad(); connect_joypad();
} }
GB_audio_set_paused(false); GB_audio_set_paused(false);
update_turbo_mode();
run(); // Never returns run(); // Never returns
return 0; return 0;
} }