diff --git a/projects/VisualStudio2013/mupen64plus-ui-console.vcxproj b/projects/VisualStudio2013/mupen64plus-ui-console.vcxproj index a76eca7..28e288f 100644 --- a/projects/VisualStudio2013/mupen64plus-ui-console.vcxproj +++ b/projects/VisualStudio2013/mupen64plus-ui-console.vcxproj @@ -122,22 +122,34 @@ copy ..\..\..\mupen64plus-win32-deps\zlib-1.2.3\bin\*.dll "$(OutDir)" + + + + + + + + + + + + - \ No newline at end of file + diff --git a/projects/unix/Makefile b/projects/unix/Makefile index a4a05e3..9611dad 100755 --- a/projects/unix/Makefile +++ b/projects/unix/Makefile @@ -229,7 +229,18 @@ SOURCE = \ $(SRCDIR)/compare_core.c \ $(SRCDIR)/core_interface.c \ $(SRCDIR)/main.c \ - $(SRCDIR)/plugin.c + $(SRCDIR)/object_factory.c \ + $(SRCDIR)/plugin.c \ + $(SRCDIR)/audio_backends/circular_buffer.c \ + $(SRCDIR)/audio_backends/dummy_audio_backend.c \ + $(SRCDIR)/resamplers/resampler.c \ + $(SRCDIR)/resamplers/trivial_resampler.c + +# optional audio backends +ifeq ($(HAVE_SDL_AUDIO_BACKEND),1) + CFLAGS += '-DHAVE_SDL_AUDIO_BACKEND' + SOURCE += $(SRCDIR)/audio_backends/sdl_audio_backend.c +endif ifeq ($(OS), MINGW) SOURCE += \ @@ -265,7 +276,9 @@ targets: @echo " OPTFLAGS=flags == compiler optimization (default: -O3 -flto)" @echo " WARNFLAGS=flag == compiler warning levels (default: -Wall)" @echo " PIE=(1|0) == Force enable/disable of position independent executables" - @echo " POSTFIX=name == String added to the name of the the build (default: '')" + @echo " POSTFIX=name == String added to the name of the the build (default: '')" + @echo " Optional modules:" + @echo " HAVE_SDL_AUDIO_BACKEND=(1|0) == enable|disable SDL audio backend compilation (default:0)" @echo " Install Options:" @echo " PREFIX=path == install/uninstall prefix (default: /usr/local/)" @echo " BINDIR=path == path to install mupen64plus binary (default: PREFIX/bin/)" diff --git a/src/audio_backends/circular_buffer.c b/src/audio_backends/circular_buffer.c new file mode 100644 index 0000000..f7f18ab --- /dev/null +++ b/src/audio_backends/circular_buffer.c @@ -0,0 +1,83 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - circular_buffer.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "circular_buffer.h" + +#include +#include +#include + + +int init_cbuff(struct circular_buffer* cbuff, size_t capacity) +{ + void* data = malloc(capacity); + + if (data == NULL) + { + return -1; + } + + cbuff->data = data; + cbuff->size = capacity; + cbuff->head = 0; + + return 0; +} + +void release_cbuff(struct circular_buffer* cbuff) +{ + free(cbuff->data); + memset(cbuff, 0, sizeof(*cbuff)); +} + + +void* cbuff_head(const struct circular_buffer* cbuff, size_t* available) +{ + assert(cbuff->head <= cbuff->size); + + *available = cbuff->size - cbuff->head; + return (void*)((char*)cbuff->data + cbuff->head); +} + + +void* cbuff_tail(const struct circular_buffer* cbuff, size_t* available) +{ + *available = cbuff->head; + return cbuff->data; +} + + +void produce_cbuff_data(struct circular_buffer* cbuff, size_t amount) +{ + assert(cbuff->head + amount <= cbuff->size); + + cbuff->head += amount; +} + + +void consume_cbuff_data(struct circular_buffer* cbuff, size_t amount) +{ + assert(cbuff->head >= amount); + + memmove(cbuff->data, cbuff->data + amount, cbuff->head - amount); + cbuff->head -= amount; +} + diff --git a/src/audio_backends/circular_buffer.h b/src/audio_backends/circular_buffer.h new file mode 100644 index 0000000..07b45c8 --- /dev/null +++ b/src/audio_backends/circular_buffer.h @@ -0,0 +1,46 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - circular_buffer.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef M64P_CIRCULAR_BUFFER_H +#define M64P_CIRCULAR_BUFFER_H + +#include + +struct circular_buffer +{ + void* data; + size_t size; + size_t head; +}; + +int init_cbuff(struct circular_buffer* cbuff, size_t capacity); + +void release_cbuff(struct circular_buffer* cbuff); + +void* cbuff_head(const struct circular_buffer* cbuff, size_t* available); + +void* cbuff_tail(const struct circular_buffer* cbuff, size_t* available); + +void produce_cbuff_data(struct circular_buffer* cbuff, size_t amount); + +void consume_cbuff_data(struct circular_buffer* cbuff, size_t amount); + +#endif diff --git a/src/audio_backends/dummy_audio_backend.c b/src/audio_backends/dummy_audio_backend.c new file mode 100644 index 0000000..c2e57c5 --- /dev/null +++ b/src/audio_backends/dummy_audio_backend.c @@ -0,0 +1,45 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - dummy_audio_backend.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "dummy_audio_backend.h" + +#include "m64p_types.h" + +#include + +/* dummy audio backend factory implementation */ +static m64p_error init(void* object) +{ + struct m64p_audio_backend* backend = (struct m64p_audio_backend*)object; + + /* set to NULL all members to trigger dummy audio backend behavior in the core */ + memset(backend, 0, sizeof(*backend)); + + return M64ERR_SUCCESS; +} + +static void release(void* object) +{ +} + + +const struct object_factory dummy_audio_backend_factory = { "dummy", init, release }; + diff --git a/src/audio_backends/dummy_audio_backend.h b/src/audio_backends/dummy_audio_backend.h new file mode 100644 index 0000000..a314c2b --- /dev/null +++ b/src/audio_backends/dummy_audio_backend.h @@ -0,0 +1,29 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - dummy_audio_backend.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef M64P_DUMMY_AUDIO_BACKEND_H +#define M64P_DUMMY_AUDIO_BACKEND_H + +#include "object_factory.h" + +const struct object_factory dummy_audio_backend_factory; + +#endif diff --git a/src/audio_backends/sdl_audio_backend.c b/src/audio_backends/sdl_audio_backend.c new file mode 100644 index 0000000..35a843c --- /dev/null +++ b/src/audio_backends/sdl_audio_backend.c @@ -0,0 +1,390 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - sdl_audio_backend.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "sdl_audio_backend.h" + +#include "circular_buffer.h" +#include "m64p_types.h" +#include "main.h" +#include "object_factory.h" +#include "resamplers/resampler.h" + +#include + +#include +#include + + +struct sdl_data +{ + struct circular_buffer primary_buffer; + size_t target; + size_t secondary_buffer_size; + + unsigned int last_cb_time; + unsigned int input_frequency; + unsigned int output_frequency; + unsigned int speed_factor; + + struct m64p_resampler resampler; + void (*release_resampler)(void* object); + + unsigned int error; + unsigned int swap_channels; +}; + + +static struct sdl_data* new_sdl_data(size_t primary_buffer_size, + size_t target, + size_t secondary_buffer_size, + const struct object_factory* resampler_factory, + unsigned int swap_channels) +{ + /* allocate sdl_data */ + struct sdl_data* sdl_data = malloc(sizeof(*sdl_data)); + if (sdl_data == NULL) + return NULL; + + /* init primary bugffer */ + if (init_cbuff(&sdl_data->primary_buffer, primary_buffer_size) != 0) + goto free_sdl_data; + + /* init resampler */ + if (resampler_factory->init(&sdl_data->resampler) != M64ERR_SUCCESS) + goto release_primary_buffer; + + /* init other fields */ + sdl_data->target = target; + sdl_data->secondary_buffer_size = secondary_buffer_size; + sdl_data->last_cb_time = 0; + sdl_data->input_frequency = 0; + sdl_data->output_frequency = 0; + sdl_data->speed_factor = 100; + sdl_data->release_resampler = resampler_factory->release; + sdl_data->error = 0; + sdl_data->swap_channels = swap_channels; + + return sdl_data; + +release_primary_buffer: + release_cbuff(&sdl_data->primary_buffer); +free_sdl_data: + free(sdl_data); + return NULL; +} + +static void delete_sdl_data(struct sdl_data* sdl_data) +{ + sdl_data->release_resampler(&sdl_data->resampler); + release_cbuff(&sdl_data->primary_buffer); + free(sdl_data); +} + +/* SDL audio callback. + * Pop and resample a certain amount of samples from the primary buffer + */ +static void audio_cb(void* userdata, unsigned char* stream, int len) +{ + unsigned int old_sample_rate; + unsigned int new_sample_rate; + size_t available; + size_t needed; + size_t consumed; + const void* src; + struct sdl_data* sdl_data = (struct sdl_data*)userdata; + + /* update last_cb_time */ + sdl_data->last_cb_time = SDL_GetTicks(); + + /* compute samples rates, and needed number of bytes */ + old_sample_rate = sdl_data->input_frequency; + new_sample_rate = sdl_data->output_frequency * 100 / sdl_data->speed_factor; + needed = len * old_sample_rate / new_sample_rate; + + src = cbuff_tail(&sdl_data->primary_buffer, &available); + if ((available > 0) && (available >= needed)) + { + /* consume samples */ + consumed = resample(&sdl_data->resampler, src, available, old_sample_rate, stream, len, new_sample_rate); + consume_cbuff_data(&sdl_data->primary_buffer, consumed); + } + else + { + /* buffer underflow */ + DebugMessage(M64MSG_VERBOSE, "buffer underflow"); + memset(stream, 0, len); + } +} + + +/* Select output frequency depending on input_frequency */ +static unsigned int select_output_frequency(unsigned int input_frequency) +{ + if (input_frequency <= 11025) { return 11025; } + else if (input_frequency <= 22050) { return 22050; } + else { return 44100; } +} + + +/* Initialize the audio device. */ +static void init_audio_device(struct sdl_data* sdl_data) +{ + SDL_AudioSpec desired, obtained; + + if (SDL_WasInit(SDL_INIT_AUDIO | SDL_INIT_TIMER) == (SDL_INIT_AUDIO | SDL_INIT_TIMER)) + { + SDL_PauseAudio(1); + SDL_CloseAudio(); + } + else + { + /* initilize sdl subsystems */ + if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0) + { + DebugMessage(M64MSG_ERROR, "Failed to initialize SDL audio subsystem"); + sdl_data->error = 1; + return; + } + } + + memset(&desired, 0, sizeof(desired)); + desired.freq = select_output_frequency(sdl_data->input_frequency); + desired.format = AUDIO_S16SYS; + desired.channels = 2; + desired.samples = sdl_data->secondary_buffer_size / (2 * 2); + desired.callback = audio_cb; + desired.userdata = sdl_data; + + if (SDL_OpenAudio(&desired, &obtained) < 0) + { + DebugMessage(M64MSG_ERROR, "Failed to open the audio device: %s", SDL_GetError()); + sdl_data->error = 1; + return; + } + + sdl_data->output_frequency = obtained.freq; + + if (sdl_data->last_cb_time == 0) + sdl_data->last_cb_time = SDL_GetTicks(); + + sdl_data->error = 0; + + DebugMessage(M64MSG_INFO, "Audio device initialized: freq=%dHz (input freq=%dHz)", + sdl_data->output_frequency, sdl_data->input_frequency); +} + + +static void memcpy_swap_LR16(void* dst, const void* src, size_t size) +{ + /* assume a size multiple of 4 */ + size_t i; + uint32_t v; + const uint32_t* src_ = src; + uint32_t* dst_ = dst; + + for(i = 0; i < size; i += 4) + { + v = *src_; + *dst_ = (v << 16) | (v >> 16); + + ++src_; + ++dst_; + } +} + +static void push_incoming_samples(struct sdl_data* sdl_data, const void* buffer, size_t size) +{ + size_t available; + void* dst = cbuff_head(&sdl_data->primary_buffer, &available); + + if (size > available) + { + DebugMessage(M64MSG_VERBOSE, "Audio buffer overflow. Requested %d Available %d", size, available); + } + else + { + SDL_LockAudio(); + + if (sdl_data->swap_channels == 0) + { + memcpy(dst, buffer, size); + } + else + { + memcpy_swap_LR16(dst, buffer, size); + } + + SDL_UnlockAudio(); + + produce_cbuff_data(&sdl_data->primary_buffer, size); + } +} + +static size_t estimate_consumable_size_at_next_audio_cb(const struct sdl_data* sdl_data) +{ + size_t available; + size_t current_size, expected_size; + unsigned int current_time, expected_cb_time; /* in ms */ + + + cbuff_tail(&sdl_data->primary_buffer, &available); + + + current_size = (available * sdl_data->output_frequency * 100) + / (sdl_data->input_frequency * sdl_data->speed_factor); + + current_time = SDL_GetTicks(); + + /* estimate time to next audio_cb */ + expected_cb_time = sdl_data->last_cb_time + + (1000 * sdl_data->secondary_buffer_size / 4) / sdl_data->output_frequency; + + /* estimate consumable size at next audio_cb */ + expected_size = current_size; + if (current_time < expected_cb_time) + { + expected_size += (expected_cb_time - current_time) * sdl_data->output_frequency * 4 / 1000; + } + + return expected_size; +} + +/* handle SDL Audio Thread / Core synchronization to avoid {over,under}flows */ +static void synchronize_audio(struct sdl_data* sdl_data) +{ + size_t expected_size = estimate_consumable_size_at_next_audio_cb(sdl_data); + + if (expected_size >= sdl_data->target + sdl_data->output_frequency / 100) + { + /* Core is ahead of SDL audio thread, + * delay emulation to allow the SDL audio thread to catch up */ + unsigned int wait_time = (expected_size - sdl_data->target) * 1000 / (4*sdl_data->output_frequency); + + SDL_PauseAudio(0); + SDL_Delay(wait_time); + } + else if (expected_size < sdl_data->secondary_buffer_size) + { + /* Core is behind SDL audio thread (predicting an underflow), + * pause the audio to let the Core catch up */ + SDL_PauseAudio(1); + } + else + { + /* expected size is within tolerance, everything is okay */ + SDL_PauseAudio(0); + } +} + + +static void set_audio_format(void* user_data, unsigned int frequency, unsigned int bits) +{ + struct sdl_data* sdl_data = (struct sdl_data*)user_data; + + //DebugMessage(M64MSG_WARNING, "SDL audio backend set format: %dHz %d bits", frequency, bits); + + /* XXX: assume 16bit samples whatever */ + if (bits != 16) + { + DebugMessage(M64MSG_WARNING, "Incoming samples are not 16 bits (%d)", bits); + } + + sdl_data->input_frequency = frequency; + init_audio_device(sdl_data); +} + +static void push_audio_samples(void* user_data, const void* buffer, size_t size) +{ + struct sdl_data* sdl_data = (struct sdl_data*)user_data; + + //DebugMessage(M64MSG_WARNING, "SDL audio backend push samples: @%p 0x%x bytes", buffer, size); + + if (sdl_data->error != 0 || sdl_data->input_frequency == 0 || sdl_data->output_frequency == 0) + return; + + push_incoming_samples(sdl_data, buffer, size); + synchronize_audio(sdl_data); +} + + +static m64p_error init(void* object) +{ + const struct object_factory* resampler_factory; + struct sdl_data* sdl_data; + + /* XXX: parse config file to retrieve the values */ + size_t primary_buffer_size = 16384 * 4; + size_t target = 10240 * 4; + size_t secondary_buffer_size = 2048 * 4; + unsigned int swap_channels = 1; + const char* resampler_name = "trivial"; + + struct m64p_audio_backend* backend = (struct m64p_audio_backend*)object; + + /* get resampler factory */ + resampler_factory = get_object_factory(resampler_factories, resampler_name); + if (resampler_factory == NULL) + { + DebugMessage(M64MSG_ERROR, "Couldn't find resampler factory: %s", resampler_name); + return M64ERR_INPUT_INVALID; + } + + /* allocate and initialize sdl_data */ + sdl_data = new_sdl_data(primary_buffer_size, + target, + secondary_buffer_size, + resampler_factory, + swap_channels); + if (sdl_data == NULL) + { + DebugMessage(M64MSG_ERROR, "Couldn't create new sdl_data !"); + return M64ERR_NO_MEMORY; + } + + /* fill backend structure */ + backend->user_data = sdl_data; + backend->set_audio_format = set_audio_format; + backend->push_audio_samples = push_audio_samples; + + return M64ERR_SUCCESS; +} + +static void release(void* object) +{ + struct m64p_audio_backend* backend = (struct m64p_audio_backend*)object; + struct sdl_data* sdl_data = (struct sdl_data*)backend->user_data; + + if (SDL_WasInit(SDL_INIT_AUDIO) != 0) + { + SDL_PauseAudio(1); + SDL_CloseAudio(); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + } + + if (SDL_WasInit(SDL_INIT_TIMER) != 0) + SDL_QuitSubSystem(SDL_INIT_TIMER); + + delete_sdl_data(sdl_data); + memset(backend, 0, sizeof(*backend)); +} + +const struct object_factory sdl_audio_backend_factory = { "sdl", init, release }; + diff --git a/src/audio_backends/sdl_audio_backend.h b/src/audio_backends/sdl_audio_backend.h new file mode 100644 index 0000000..52d0bbe --- /dev/null +++ b/src/audio_backends/sdl_audio_backend.h @@ -0,0 +1,29 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - sdl_audio_backend.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef M64P_SDL_AUDIO_BACKEND_H +#define M64P_SDL_AUDIO_BACKEND_H + +#include "object_factory.h" + +const struct object_factory sdl_audio_backend_factory; + +#endif diff --git a/src/main.c b/src/main.c index 6ddf47d..ff846c8 100644 --- a/src/main.c +++ b/src/main.c @@ -36,6 +36,7 @@ #include "core_interface.h" #include "m64p_types.h" #include "main.h" +#include "object_factory.h" #include "osal_preproc.h" #include "plugin.h" #include "version.h" @@ -633,6 +634,8 @@ __attribute__ ((visibility("default"))) int main(int argc, char *argv[]) { int i; + const struct object_factory* l_AudioBackendFactory; + struct m64p_audio_backend l_AudioBackend = { 0 }; printf(" __ __ __ _ _ ____ _ \n"); printf("| \\/ |_ _ _ __ ___ _ __ / /_ | || | | _ \\| |_ _ ___ \n"); @@ -677,6 +680,54 @@ int main(int argc, char *argv[]) return 5; } + /* list available audio backends if requested */ + if (g_AudioPlugin != NULL) + { + if (strcmp(g_AudioPlugin,"list") == 0) + { + DebugMessage(M64MSG_INFO, "List of available audio backends:"); + for(i = 0; audio_backend_factories[i] != NULL; ++i) + { + DebugMessage(M64MSG_INFO, "%d. %s", i, audio_backend_factories[i]->name); + } + + (*CoreShutdown)(); + DetachCoreLib(); + return 0; + } + } + + /* test if audio argument is an audio backend factory */ + l_AudioBackendFactory = get_object_factory(audio_backend_factories, g_AudioPlugin); + if (l_AudioBackendFactory != NULL) + { + /* call audio backend init procedure */ + if (l_AudioBackendFactory->init(&l_AudioBackend) != M64ERR_SUCCESS) + { + DebugMessage(M64MSG_ERROR, "failed initialization of %s audio backend", l_AudioBackendFactory->name); + memset(&l_AudioBackend, 0, sizeof(l_AudioBackend)); + l_AudioBackendFactory = NULL; + } + else + { + + /* let the core know about the audio backend */ + if ((*CoreDoCommand)(M64CMD_SET_AUDIO_INTERFACE_BACKEND, M64P_AUDIO_BACKEND_VERSION, &l_AudioBackend) != M64ERR_SUCCESS) + { + DebugMessage(M64MSG_ERROR, "core error while setting %s audio backend", l_AudioBackendFactory->name); + l_AudioBackendFactory->release(&l_AudioBackend); + memset(&l_AudioBackend, 0, sizeof(l_AudioBackend)); + l_AudioBackendFactory = NULL; + } + else + { + DebugMessage(M64MSG_INFO, "using audio backend %s", l_AudioBackendFactory->name); + /* disable audio plugin if audio backend was successfully set */ + g_AudioPlugin = "dummy"; + } + } + } + /* Handle the core comparison feature */ if (l_CoreCompareMode != 0 && !(g_CoreCapabilities & M64CAPS_CORE_COMPARE)) { @@ -786,6 +837,14 @@ int main(int argc, char *argv[]) (*CoreDetachPlugin)(g_PluginMap[i].type); PluginUnload(); + /* release audio backend */ + if (l_AudioBackendFactory != NULL) + { + l_AudioBackendFactory->release(&l_AudioBackend); + memset(&l_AudioBackend, 0, sizeof(l_AudioBackend)); + l_AudioBackendFactory = NULL; + } + /* close the ROM image */ (*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL); diff --git a/src/object_factory.c b/src/object_factory.c new file mode 100644 index 0000000..54e134b --- /dev/null +++ b/src/object_factory.c @@ -0,0 +1,67 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - object_factory.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "object_factory.h" + +#include "audio_backends/dummy_audio_backend.h" +#include "resamplers/trivial_resampler.h" + +#ifdef HAVE_SDL_AUDIO_BACKEND +#include "audio_backends/sdl_audio_backend.h" +#endif + +#include + + +const struct object_factory* const audio_backend_factories[] = +{ + &dummy_audio_backend_factory, +#ifdef HAVE_SDL_AUDIO_BACKEND + &sdl_audio_backend_factory, +#endif + NULL /* end of array sentinel */ +}; + +const struct object_factory* const resampler_factories[] = +{ + &trivial_resampler_factory, + NULL /* end of array sentinel */ +}; + + +const struct object_factory* get_object_factory(const struct object_factory* const* factories, const char* name) +{ + if (factories != NULL && name != NULL) + { + while((*factories) != NULL) + { + if (strcmp(name, (*factories)->name) == 0) + { + return *factories; + } + + ++factories; + } + } + + return NULL; +} + diff --git a/src/object_factory.h b/src/object_factory.h new file mode 100644 index 0000000..498a5a9 --- /dev/null +++ b/src/object_factory.h @@ -0,0 +1,40 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - object_factory.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef M64P_OBJECT_FACTORY_H +#define M64P_OBJECT_FACTORY_H + +#include "m64p_types.h" + +struct object_factory +{ + const char* name; + m64p_error (*init)(void* object); + void (*release)(void* object); +}; + +const struct object_factory* +get_object_factory(const struct object_factory* const* factories, const char* name); + +extern const struct object_factory* const audio_backend_factories[]; +extern const struct object_factory* const resampler_factories[]; + +#endif diff --git a/src/resamplers/resampler.c b/src/resamplers/resampler.c new file mode 100644 index 0000000..e211466 --- /dev/null +++ b/src/resamplers/resampler.c @@ -0,0 +1,32 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - resampler.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "resampler.h" + +size_t resample(struct m64p_resampler* resampler, + const void* src, size_t src_size, unsigned int input_frequency, + void* dst, size_t dst_size, unsigned int output_frequency) +{ + return resampler->resample(resampler->resampler_data, + src, src_size, input_frequency, + dst, dst_size, output_frequency); +} + diff --git a/src/resamplers/resampler.h b/src/resamplers/resampler.h new file mode 100644 index 0000000..a22d62d --- /dev/null +++ b/src/resamplers/resampler.h @@ -0,0 +1,39 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - resampler.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef M64P_RESAMPLER_H +#define M64P_RESAMPLER_H + +#include + +struct m64p_resampler +{ + void* resampler_data; + size_t (*resample)(void* resampler_data, + const void* src, size_t src_size, unsigned int input_frequency, + void* dst, size_t dst_size, unsigned int output_frequency); +}; + +size_t resample(struct m64p_resampler* resampler, + const void* src, size_t src_size, unsigned int input_frequency, + void* dst, size_t dst_size, unsigned int output_frequency); + +#endif diff --git a/src/resamplers/trivial_resampler.c b/src/resamplers/trivial_resampler.c new file mode 100644 index 0000000..bf0518f --- /dev/null +++ b/src/resamplers/trivial_resampler.c @@ -0,0 +1,87 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - trivial_resampler.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "trivial_resampler.h" +#include "resampler.h" + +#include "m64p_types.h" + +#include + +static size_t trivial_resample(void* resampler_data, + const void* src, size_t src_size, unsigned int input_frequency, + void* dst, size_t dst_size, unsigned int output_frequency) +{ + size_t i, j; + + if (output_frequency >= input_frequency) + { + int sldf = input_frequency; + int const2 = 2*sldf; + int dldf = output_frequency; + int const1 = const2 - 2*dldf; + int criteria = const2 - dldf; + + for (i = 0; i < dst_size/4; ++i) + { + ((uint32_t*)dst)[i] = ((uint32_t*)src)[j]; + + if (criteria >= 0) + { + ++j; + criteria += const1; + } + else + { + criteria += const2; + } + } + } + else + { + for (i = 0; i < dst_size/4; ++i) + { + j = i * input_frequency / output_frequency; + ((uint32_t*)dst)[i] = ((uint32_t*)src)[j]; + } + } + + return j * 4; +} + + +static m64p_error init(void* object) +{ + struct m64p_resampler* resampler = (struct m64p_resampler*)object; + + /* fill resampler structure */ + resampler->resampler_data = NULL; + resampler->resample = trivial_resample; + + return M64ERR_SUCCESS; +} + +static void release(void* object) +{ +} + +const struct object_factory trivial_resampler_factory = { "trivial", init, release }; + diff --git a/src/resamplers/trivial_resampler.h b/src/resamplers/trivial_resampler.h new file mode 100644 index 0000000..565e50b --- /dev/null +++ b/src/resamplers/trivial_resampler.h @@ -0,0 +1,29 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - trivial_resampler.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2015 Bobby Smiles * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef M64P_TRIVIAL_RESAMPLER_H +#define M64P_TRIVIAL_RESAMPLER_H + +#include "object_factory.h" + +const struct object_factory trivial_resampler_factory; + +#endif