From e268efefeff5720a6a3ae29a7b88283849c05076 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 15 Jun 2019 23:22:27 +0300 Subject: [PATCH] Redesign and reimplement the audio API, let the frontends handle more stuff. Probably affects #161 --- Cocoa/Document.m | 69 ++++++++++++++++++++++++++++++++- Core/apu.c | 84 +++++++--------------------------------- Core/apu.h | 18 +++------ Core/debugger.c | 42 ++++++++++---------- Core/gb.c | 3 -- Core/gb.h | 2 +- Core/memory.c | 4 +- Core/sgb.c | 25 ++++++++---- Core/sgb.h | 1 - Core/timing.c | 6 +-- SDL/main.c | 19 ++++----- libretro/Makefile.common | 1 + libretro/libretro.c | 21 +++------- 13 files changed, 146 insertions(+), 149 deletions(-) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index 731f7c8..b99e4c8 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -55,6 +55,12 @@ enum model { bool rewind; bool modelsChanging; + + NSCondition *audioLock; + GB_sample_t *audioBuffer; + size_t audioBufferSize; + size_t audioBufferPosition; + size_t audioBufferNeeded; } @property GBAudioClient *audioClient; @@ -67,6 +73,7 @@ enum model { - (void) printImage:(uint32_t *)image height:(unsigned) height topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin exposure:(unsigned) exposure; +- (void) gotNewSample:(GB_sample_t *)sample; @end static void vblank(GB_gameboy_t *gb) @@ -118,6 +125,12 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, [self printImage:image height:height topMargin:top_margin bottomMargin:bottom_margin exposure:exposure]; } +static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample) +{ + Document *self = (__bridge Document *)GB_get_user_data(gb); + [self gotNewSample:sample]; +} + @implementation Document { GB_gameboy_t gb; @@ -133,6 +146,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, has_debugger_input = [[NSConditionLock alloc] initWithCondition:0]; debugger_input_queue = [[NSMutableArray alloc] init]; console_output_lock = [[NSRecursiveLock alloc] init]; + audioLock = [[NSCondition alloc] init]; } return self; } @@ -184,6 +198,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, GB_set_camera_update_request_callback(&gb, cameraRequestUpdate); GB_set_highpass_filter_mode(&gb, (GB_highpass_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBHighpassFilter"]); GB_set_rewind_length(&gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindLength"]); + GB_apu_set_sample_callback(&gb, audioCallback); } - (void) vblank @@ -201,13 +216,57 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, } } +- (void)gotNewSample:(GB_sample_t *)sample +{ + [audioLock lock]; + if (self.audioClient.isPlaying) { + if (audioBufferPosition == audioBufferSize) { + if (audioBufferSize >= 0x4000) { + audioBufferPosition = 0; + [audioLock unlock]; + return; + } + + if (audioBufferSize == 0) { + audioBufferSize = 512; + } + else { + audioBufferSize += audioBufferSize >> 2; + } + audioBuffer = realloc(audioBuffer, sizeof(*sample) * audioBufferSize); + } + audioBuffer[audioBufferPosition++] = *sample; + } + if (audioBufferPosition == audioBufferNeeded) { + [audioLock signal]; + audioBufferNeeded = 0; + } + [audioLock unlock]; +} + - (void) run { running = true; GB_set_pixels_output(&gb, self.view.pixels); GB_set_sample_rate(&gb, 96000); self.audioClient = [[GBAudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer) { - GB_apu_copy_buffer(&gb, buffer, nFrames); + [audioLock lock]; + + if (audioBufferPosition < nFrames) { + audioBufferNeeded = nFrames; + [audioLock wait]; + } + + if (audioBufferPosition >= nFrames && audioBufferPosition < nFrames + 4800) { + memcpy(buffer, audioBuffer, nFrames * sizeof(*buffer)); + memmove(audioBuffer, audioBuffer + nFrames, (audioBufferPosition - nFrames) * sizeof(*buffer)); + audioBufferPosition = audioBufferPosition - nFrames; + } + else { + memcpy(buffer, audioBuffer + (audioBufferPosition - nFrames), nFrames * sizeof(*buffer)); + audioBufferPosition = 0; + } + [audioLock unlock]; } andSampleRate:96000]; if (![[NSUserDefaults standardUserDefaults] boolForKey:@"Mute"]) { [self.audioClient start]; @@ -227,6 +286,11 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, } } [hex_timer invalidate]; + [audioLock lock]; + memset(audioBuffer, 0, (audioBufferSize - audioBufferPosition) * sizeof(*audioBuffer)); + audioBufferPosition = audioBufferNeeded; + [audioLock signal]; + [audioLock unlock]; [self.audioClient stop]; self.audioClient = nil; self.view.mouseHidingEnabled = NO; @@ -329,6 +393,9 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, if (cameraImage) { CVBufferRelease(cameraImage); } + if (audioBuffer) { + free(audioBuffer); + } } - (void)windowControllerDidLoadNib:(NSWindowController *)aController { diff --git a/Core/apu.c b/Core/apu.c index 664530d..5e975cb 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "gb.h" #define likely(x) __builtin_expect((x), 1) @@ -117,7 +118,7 @@ static double smooth(double x) return 3*x*x - 2*x*x*x; } -static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest) +static void render(GB_gameboy_t *gb) { GB_sample_t output = {0,0}; @@ -147,7 +148,7 @@ static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest) } } - if (likely(gb->apu_output.last_update[i] == 0 || no_downsampling)) { + if (likely(gb->apu_output.last_update[i] == 0)) { output.left += gb->apu_output.current_sample[i].left * multiplier; output.right += gb->apu_output.current_sample[i].right * multiplier; } @@ -205,17 +206,9 @@ static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest) } } - if (dest) { - *dest = filtered_output; - return; - } - - while (gb->apu_output.copy_in_progress); - while (!__sync_bool_compare_and_swap(&gb->apu_output.lock, false, true)); - if (gb->apu_output.buffer_position < gb->apu_output.buffer_size) { - gb->apu_output.buffer[gb->apu_output.buffer_position++] = filtered_output; - } - gb->apu_output.lock = false; + + assert(gb->apu_output.sample_callback); + gb->apu_output.sample_callback(gb, &filtered_output); } static uint16_t new_sweep_sample_legnth(GB_gameboy_t *gb) @@ -504,56 +497,12 @@ void GB_apu_run(GB_gameboy_t *gb) if (gb->apu_output.sample_rate) { gb->apu_output.cycles_since_render += cycles; - if (gb->apu_output.sample_cycles > gb->apu_output.cycles_per_sample) { + if (gb->apu_output.sample_cycles >= gb->apu_output.cycles_per_sample) { gb->apu_output.sample_cycles -= gb->apu_output.cycles_per_sample; - render(gb, false, NULL); + render(gb); } } } - -void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, size_t count) -{ - if (gb->sgb) { - if (GB_sgb_render_jingle(gb, dest, count)) return; - } - gb->apu_output.copy_in_progress = true; - - /* TODO: Rewrite this as a proper cyclic buffer. This is a workaround to avoid a very rare crashing race condition */ - size_t buffer_position = gb->apu_output.buffer_position; - - if (!gb->apu_output.stream_started) { - // Intentionally fail the first copy to sync the stream with the Gameboy. - gb->apu_output.stream_started = true; - gb->apu_output.buffer_position = 0; - buffer_position = 0; - } - - if (count > buffer_position) { - // GB_log(gb, "Audio underflow: %d\n", count - gb->apu_output.buffer_position); - GB_sample_t output; - render(gb, true, &output); - - for (unsigned i = 0; i < count - buffer_position; i++) { - dest[buffer_position + i] = output; - } - - if (buffer_position) { - if (gb->apu_output.buffer_size + (count - buffer_position) < count * 3) { - gb->apu_output.buffer_size += count - buffer_position; - gb->apu_output.buffer = realloc(gb->apu_output.buffer, - gb->apu_output.buffer_size * sizeof(*gb->apu_output.buffer)); - gb->apu_output.stream_started = false; - } - } - count = buffer_position; - } - memcpy(dest, gb->apu_output.buffer, count * sizeof(*gb->apu_output.buffer)); - memmove(gb->apu_output.buffer, gb->apu_output.buffer + count, (buffer_position - count) * sizeof(*gb->apu_output.buffer)); - gb->apu_output.buffer_position -= count; - - gb->apu_output.copy_in_progress = false; -} - void GB_apu_init(GB_gameboy_t *gb) { memset(&gb->apu, 0, sizeof(gb->apu)); @@ -1009,26 +958,21 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) gb->io_registers[reg] = value; } -size_t GB_apu_get_current_buffer_length(GB_gameboy_t *gb) +void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate) { - return gb->apu_output.buffer_position; -} -void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate) -{ - if (gb->apu_output.buffer) { - free(gb->apu_output.buffer); - } - gb->apu_output.buffer_size = sample_rate / 25; // 40ms delay - gb->apu_output.buffer = malloc(gb->apu_output.buffer_size * sizeof(*gb->apu_output.buffer)); gb->apu_output.sample_rate = sample_rate; - gb->apu_output.buffer_position = 0; if (sample_rate) { gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate); } GB_apu_update_cycles_per_sample(gb); } +void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback) +{ + gb->apu_output.sample_callback = callback; +} + void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode) { gb->apu_output.highpass_mode = mode; diff --git a/Core/apu.h b/Core/apu.h index f8f56e9..7f8acfc 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -46,6 +46,8 @@ enum GB_CHANNELS { GB_N_CHANNELS }; +typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample); + typedef struct { bool global_enable; @@ -126,14 +128,6 @@ typedef enum { typedef struct { unsigned sample_rate; - GB_sample_t *buffer; - size_t buffer_size; - size_t buffer_position; - - bool stream_started; /* detects first copy request to minimize lag */ - volatile bool copy_in_progress; - volatile bool lock; - double sample_cycles; // In 8 MHz units double cycles_per_sample; @@ -147,13 +141,13 @@ typedef struct { GB_highpass_mode_t highpass_mode; double highpass_rate; GB_double_sample_t highpass_diff; + + GB_sample_callback_t sample_callback; } GB_apu_output_t; -void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate); -void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, size_t count); -size_t GB_apu_get_current_buffer_length(GB_gameboy_t *gb); +void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate); void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode); - +void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback); #ifdef GB_INTERNAL bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index); void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value); diff --git a/Core/debugger.c b/Core/debugger.c index 79b5991..6874433 100644 --- a/Core/debugger.c +++ b/Core/debugger.c @@ -455,12 +455,12 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string, case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc}; } } - GB_log(gb, "Unknown register: %.*s\n", (unsigned int) length, string); + GB_log(gb, "Unknown register: %.*s\n", (unsigned) length, string); *error = true; return (lvalue_t){0,}; } - GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned int) length, string); + GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned) length, string); *error = true; return (lvalue_t){0,}; } @@ -564,8 +564,8 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string, } // Search for lowest priority operator signed int depth = 0; - unsigned int operator_index = -1; - unsigned int operator_pos = 0; + unsigned operator_index = -1; + unsigned operator_pos = 0; for (int i = 0; i < length; i++) { if (string[i] == '(') depth++; else if (string[i] == ')') depth--; @@ -593,7 +593,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string, } } if (operator_index != -1) { - unsigned int right_start = (unsigned int)(operator_pos + strlen(operators[operator_index].string)); + unsigned right_start = (unsigned)(operator_pos + strlen(operators[operator_index].string)); value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, watchpoint_address, watchpoint_new_value); if (*error) goto exit; if (operators[operator_index].lvalue_operator) { @@ -661,7 +661,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string, goto exit; } - GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned int) length, string); + GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned) length, string); *error = true; goto exit; } @@ -675,7 +675,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string, } uint16_t literal = (uint16_t) (strtol(string, &end, base)); if (end != string + length) { - GB_log(gb, "Failed to parse: %.*s\n", (unsigned int) length, string); + GB_log(gb, "Failed to parse: %.*s\n", (unsigned) length, string); *error = true; goto exit; } @@ -880,13 +880,13 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const condition += strlen(" if "); /* Verify condition is sane (Todo: This might have side effects!) */ bool error; - debugger_evaluate(gb, condition, (unsigned int)strlen(condition), &error, NULL, NULL); + debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, NULL, NULL); if (error) return true; } bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); + value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); uint32_t key = BP_KEY(result); if (error) return true; @@ -949,7 +949,7 @@ static bool delete(GB_gameboy_t *gb, char *arguments, char *modifiers, const deb } bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); + value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); uint32_t key = BP_KEY(result); if (error) return true; @@ -1065,13 +1065,13 @@ print_usage: /* To make $new and $old legal */ uint16_t dummy = 0; uint8_t dummy2 = 0; - debugger_evaluate(gb, condition, (unsigned int)strlen(condition), &error, &dummy, &dummy2); + debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, &dummy, &dummy2); if (error) return true; } bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); + value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); uint32_t key = WP_KEY(result); if (error) return true; @@ -1132,7 +1132,7 @@ static bool unwatch(GB_gameboy_t *gb, char *arguments, char *modifiers, const de } bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); + value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); uint32_t key = WP_KEY(result); if (error) return true; @@ -1234,7 +1234,7 @@ static bool _should_break(GB_gameboy_t *gb, value_t addr, bool jump_to) } bool error; bool condition = debugger_evaluate(gb, gb->breakpoints[index].condition, - (unsigned int)strlen(gb->breakpoints[index].condition), &error, NULL, NULL).value; + (unsigned)strlen(gb->breakpoints[index].condition), &error, NULL, NULL).value; if (error) { /* Should never happen */ GB_log(gb, "An internal error has occured\n"); @@ -1273,7 +1273,7 @@ static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu } bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); + value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); if (!error) { switch (modifiers[0]) { case 'a': @@ -1319,7 +1319,7 @@ static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const de } bool error; - value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); + value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); uint16_t count = 32; if (modifiers) { @@ -1371,7 +1371,7 @@ static bool disassemble(GB_gameboy_t *gb, char *arguments, char *modifiers, cons } bool error; - value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); + value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); uint16_t count = 5; if (modifiers) { @@ -1468,7 +1468,7 @@ static bool backtrace(GB_gameboy_t *gb, char *arguments, char *modifiers, const } GB_log(gb, " 1. %s\n", debugger_value_to_string(gb, (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}, true)); - for (unsigned int i = gb->backtrace_size; i--;) { + for (unsigned i = gb->backtrace_size; i--;) { GB_log(gb, "%3d. %s\n", gb->backtrace_size - i + 1, debugger_value_to_string(gb, (value_t){true, gb->backtrace_returns[i].bank, gb->backtrace_returns[i].addr}, true)); } @@ -1937,7 +1937,7 @@ static bool _GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, value_t addr, u } bool error; bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition, - (unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr.value, &value).value; + (unsigned)strlen(gb->watchpoints[index].condition), &error, &addr.value, &value).value; if (error) { /* Should never happen */ GB_log(gb, "An internal error has occured\n"); @@ -1982,7 +1982,7 @@ static bool _GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, value_t addr) } bool error; bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition, - (unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr.value, NULL).value; + (unsigned)strlen(gb->watchpoints[index].condition), &error, &addr.value, NULL).value; if (error) { /* Should never happen */ GB_log(gb, "An internal error has occured\n"); @@ -2169,7 +2169,7 @@ void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path) } if (length == 0) continue; - unsigned int bank, address; + unsigned bank, address; char symbol[length]; if (sscanf(line, "%02x:%04x %s", &bank, &address, symbol) == 3) { diff --git a/Core/gb.c b/Core/gb.c index 2f395a9..cbd295c 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -135,9 +135,6 @@ void GB_free(GB_gameboy_t *gb) if (gb->rom) { free(gb->rom); } - if (gb->apu_output.buffer) { - free(gb->apu_output.buffer); - } if (gb->breakpoints) { free(gb->breakpoints); } diff --git a/Core/gb.h b/Core/gb.h index 5385710..2d7d599 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -555,7 +555,7 @@ struct GB_gameboy_internal_s { uint16_t addr_for_call_depth[0x200]; /* Backtrace */ - unsigned int backtrace_size; + unsigned backtrace_size; uint16_t backtrace_sps[0x200]; struct { uint16_t bank; diff --git a/Core/memory.c b/Core/memory.c index 9104cbd..99ae190 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -126,13 +126,13 @@ static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr) if (!gb->rom_size) { return 0xFF; } - unsigned int effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000; + unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000; return gb->rom[effective_address & (gb->rom_size - 1)]; } static uint8_t read_mbc_rom(GB_gameboy_t *gb, uint16_t addr) { - unsigned int effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000; + unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000; return gb->rom[effective_address & (gb->rom_size - 1)]; } diff --git a/Core/sgb.c b/Core/sgb.c index 0944985..323e10f 100644 --- a/Core/sgb.c +++ b/Core/sgb.c @@ -1,6 +1,7 @@ #include "gb.h" #include "random.h" #include +#include #define INTRO_ANIMATION_LENGTH 200 @@ -461,8 +462,13 @@ static void render_boot_animation (GB_gameboy_t *gb) } } +static void render_jingle(GB_gameboy_t *gb, size_t count); void GB_sgb_render(GB_gameboy_t *gb) { + if (gb->apu_output.sample_rate) { + render_jingle(gb, gb->apu_output.sample_rate / (GB_get_clock_rate(gb) / LCDC_PERIOD)); + } + if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++; if (!gb->screen || !gb->rgb_encode_callback) return; @@ -689,7 +695,7 @@ static double random_double(void) return ((signed)(GB_random32() % 0x10001) - 0x8000) / (double) 0x8000; } -bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count) +static void render_jingle(GB_gameboy_t *gb, size_t count) { const double frequencies[7] = { 466.16, // Bb4 @@ -701,15 +707,17 @@ bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count) 1567.98, // G6 }; + assert(gb->apu_output.sample_callback); + if (gb->sgb->intro_animation < 0) { + GB_sample_t sample = {0, 0}; for (unsigned i = 0; i < count; i++) { - dest->left = dest->right = 0; - dest++; + gb->apu_output.sample_callback(gb, &sample); } - return true; + return; } - if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return false; + if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return; signed jingle_stage = (gb->sgb->intro_animation - 64) / 3; double sweep_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate; @@ -718,6 +726,7 @@ bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count) sweep_cutoff_ratio = 1; } + GB_sample_t stereo; for (unsigned i = 0; i < count; i++) { double sample = 0; for (signed f = 0; f < 7 && f < jingle_stage; f++) { @@ -738,10 +747,10 @@ bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count) sample += gb->sgb_intro_sweep_previous_sample * pow((120 - gb->sgb->intro_animation) / 120.0, 2) * 0.8; } - dest->left = dest->right = sample * 0x7000; - dest++; + stereo.left = stereo.right = sample * 0x7000; + gb->apu_output.sample_callback(gb, &stereo); } - return true; + return; } diff --git a/Core/sgb.h b/Core/sgb.h index 7e36198..2c6e8ee 100644 --- a/Core/sgb.h +++ b/Core/sgb.h @@ -54,7 +54,6 @@ struct GB_sgb_s { void GB_sgb_write(GB_gameboy_t *gb, uint8_t value); void GB_sgb_render(GB_gameboy_t *gb); void GB_sgb_load_default_data(GB_gameboy_t *gb); -bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count); #endif diff --git a/Core/timing.c b/Core/timing.c index b14a00b..5bc369b 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -8,7 +8,7 @@ #include #endif -static const unsigned int GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128}; +static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128}; #ifndef DISABLE_TIMEKEEPING static int64_t get_nanoseconds(void) @@ -251,8 +251,8 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac) /* Glitch only happens when old_tac is enabled. */ if (!(old_tac & 4)) return; - unsigned int old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3]; - unsigned int new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3]; + unsigned old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3]; + unsigned new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3]; /* The bit used for overflow testing must have been 1 */ if (gb->div_counter & old_clocks) { diff --git a/SDL/main.c b/SDL/main.c index be52afd..c0a0585 100755 --- a/SDL/main.c +++ b/SDL/main.c @@ -33,6 +33,7 @@ static double clock_mutliplier = 1.0; static char *filename = NULL; static typeof(free) *free_function = NULL; static char *battery_save_path_ptr; +static bool skip_audio; SDL_AudioDeviceID device_id; @@ -336,6 +337,8 @@ static void vblank(GB_gameboy_t *gb) } do_rewind = rewind_down; handle_events(gb); + + skip_audio = (SDL_GetQueuedAudioSize(device_id) / sizeof(GB_sample_t)) > have_aspec.freq / 20; } @@ -355,16 +358,12 @@ static void debugger_interrupt(int ignore) GB_debugger_break(&gb); } - -static void audio_callback(void *gb, Uint8 *stream, int len) +static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) { - if (GB_is_inited(gb)) { - GB_apu_copy_buffer(gb, (GB_sample_t *) stream, len / sizeof(GB_sample_t)); - } - else { - memset(stream, 0, len); - } + if (skip_audio) return; + SDL_QueueAudio(device_id, sample, sizeof(*sample)); } + static bool handle_pending_command(void) { @@ -436,6 +435,7 @@ restart: GB_set_highpass_filter_mode(&gb, configuration.highpass_mode); GB_set_rewind_length(&gb, configuration.rewind_length); GB_set_update_input_hint_callback(&gb, handle_events); + GB_apu_set_sample_callback(&gb, gb_audio_callback); } SDL_DestroyTexture(texture); @@ -612,9 +612,6 @@ int main(int argc, char **argv) } #endif - - want_aspec.callback = audio_callback; - want_aspec.userdata = &gb; device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); /* Start Audio */ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index fb3a02f..3bf1783 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -12,6 +12,7 @@ SOURCES_C := $(CORE_DIR)/Core/gb.c \ $(CORE_DIR)/Core/sm83_cpu.c \ $(CORE_DIR)/Core/joypad.c \ $(CORE_DIR)/Core/save_state.c \ + $(CORE_DIR)/Core/random.c \ $(CORE_DIR)/libretro/agb_boot.c \ $(CORE_DIR)/libretro/cgb_boot.c \ $(CORE_DIR)/libretro/dmg_boot.c \ diff --git a/libretro/libretro.c b/libretro/libretro.c index f5e4e2e..cec93f8 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -150,34 +150,22 @@ static void GB_update_keys_status(GB_gameboy_t *gb, unsigned port) } -static void audio_callback(void *gb) +static void audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) { - size_t length = GB_apu_get_current_buffer_length(gb); - - while (length > sizeof(soundbuf) / 4) - { - GB_apu_copy_buffer(gb, (GB_sample_t *) soundbuf, 1024); - audio_batch_cb(soundbuf, 1024); - length -= 1024; - } - if (length) { - GB_apu_copy_buffer(gb, (GB_sample_t *) soundbuf, length); - audio_batch_cb(soundbuf, length); + if ((audio_out == GB_1 && gb == &gameboy[0]) || + (audio_out == GB_2 && gb == &gameboy[1])) { + audio_batch_cb((void*)sample, 1); } } static void vblank1(GB_gameboy_t *gb) { vblank1_occurred = true; - if (audio_out == GB_1) - audio_callback(gb); } static void vblank2(GB_gameboy_t *gb) { vblank2_occurred = true; - if (audio_out == GB_2) - audio_callback(gb); } static bool bit_to_send1 = true, bit_to_send2 = true; @@ -383,6 +371,7 @@ static void init_for_current_model(unsigned id) GB_set_pixels_output(&gameboy[i],(unsigned int*)(frame_buf + i * ((model[i] == MODEL_SGB || model[i] == MODEL_SGB2) ? SGB_VIDEO_PIXELS : VIDEO_PIXELS))); GB_set_rgb_encode_callback(&gameboy[i], rgb_encode); GB_set_sample_rate(&gameboy[i], AUDIO_FREQUENCY); + GB_apu_set_sample_callback(&gameboy[i], audio_callback); /* todo: attempt to make these more generic */ GB_set_vblank_callback(&gameboy[0], (GB_vblank_callback_t) vblank1);