diff --git a/Core/apu.c b/Core/apu.c index c9f710033..3fb3bd23c 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -211,7 +211,7 @@ static void render(GB_gameboy_t *gb) } } - if (likely(gb->apu_output.last_update[i] == 0)) { + if (likely(gb->apu_output.last_update[i] == 0 || gb->apu_output.cycles_since_render == 0)) { output.left += gb->apu_output.current_sample[i].left * multiplier; output.right += gb->apu_output.current_sample[i].right * multiplier; } @@ -633,11 +633,14 @@ static void step_lfsr(GB_gameboy_t *gb, unsigned cycles_offset) void GB_apu_run(GB_gameboy_t *gb, bool force) { - uint32_t clock_rate = GB_get_clock_rate(gb) * 2; + uint32_t clock_rate = GB_get_clock_rate(gb); + bool orig_force = force; + +restart:; uint16_t cycles = gb->apu.apu_cycles; if (force || - (gb->apu.apu_cycles > 0x400) || + (cycles + gb->apu_output.cycles_since_render >= gb->apu_output.max_cycles_per_sample) || (gb->apu_output.sample_cycles >= clock_rate) || (gb->apu.square_sweep_calculate_countdown || gb->apu.channel_1_restart_hold || gb->apu.square_sweep_calculate_countdown_reload_timer) || (gb->model <= GB_MODEL_CGB_E && (gb->apu.wave_channel.bugged_read_countdown || (gb->apu.wave_channel.enable && gb->apu.wave_channel.pulsed)))) { @@ -646,9 +649,46 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) if (!force) { return; } + + /* Force renders to never be more than max_cycles_per_sample apart by spliting runs. */ + while (cycles + gb->apu_output.cycles_since_render > gb->apu_output.max_cycles_per_sample) { + /* We're already past max_cycles_per_sample. This can happen when changing clock rates, etc. + Let this sample render normally. */ + if (unlikely(gb->apu_output.cycles_since_render > gb->apu_output.max_cycles_per_sample)) break; + + gb->apu.apu_cycles = gb->apu_output.max_cycles_per_sample - gb->apu_output.cycles_since_render; + + if (gb->apu.apu_cycles) { + // Run for just enough cycles to reach max_cycles_per_sample + cycles -= gb->apu.apu_cycles; + GB_apu_run(gb, true); + // Re-evaluate force if needed + if (!orig_force) { + force = false; + gb->apu.apu_cycles = cycles; + goto restart; + } + // Check if we need another batch + continue; + } + + // Render if needed + if (gb->apu_output.sample_cycles >= clock_rate) { + gb->apu_output.sample_cycles -= clock_rate; + render(gb); + } + break; + } gb->apu.apu_cycles = 0; - if (!cycles) return; + if (!cycles) { + /* This can happen in pre-CGB stop mode */ + while (unlikely(gb->apu_output.sample_cycles >= clock_rate)) { + gb->apu_output.sample_cycles -= clock_rate; + render(gb); + } + return; + } if (unlikely(gb->apu.wave_channel.bugged_read_countdown)) { uint16_t cycles_left = cycles; @@ -1633,6 +1673,10 @@ void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate) gb->apu_output.sample_rate = sample_rate; if (sample_rate) { gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate); + gb->apu_output.max_cycles_per_sample = ceil(GB_get_clock_rate(gb) / 2.0 / sample_rate); + } + else { + gb->apu_output.max_cycles_per_sample = 0x400; } } @@ -1645,6 +1689,7 @@ void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample) } gb->apu_output.sample_rate = GB_get_clock_rate(gb) / cycles_per_sample * 2; gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample); + gb->apu_output.max_cycles_per_sample = ceil(cycles_per_sample / 4); } unsigned GB_get_sample_rate(GB_gameboy_t *gb) diff --git a/Core/apu.h b/Core/apu.h index 6ebab49e0..312f88452 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -169,6 +169,7 @@ typedef struct { unsigned sample_rate; unsigned sample_cycles; // Counts by sample_rate until it reaches the clock frequency + unsigned max_cycles_per_sample; // Samples are NOT normalized to MAX_CH_AMP * 4 at this stage! unsigned cycles_since_render; diff --git a/Core/gb.c b/Core/gb.c index a7fefe3c4..398ff2404 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -171,6 +171,7 @@ GB_gameboy_t *GB_init(GB_gameboy_t *gb, GB_model_t model) #endif gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type gb->clock_multiplier = 1.0; + gb->apu_output.max_cycles_per_sample = 0x400; if (model & GB_MODEL_NO_SFC_BIT) { /* Disable time syncing. Timing should be done by the SFC emulator. */ diff --git a/Core/timing.c b/Core/timing.c index ce66ea17f..3b9ecf783 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -254,6 +254,7 @@ static void timers_run(GB_gameboy_t *gb, uint8_t cycles) if (GB_is_cgb(gb)) { gb->apu.apu_cycles += 1 << !gb->cgb_double_speed; } + gb->apu_output.sample_cycles += (gb->apu_output.sample_rate << !gb->cgb_double_speed) << 1; return; } @@ -267,6 +268,7 @@ static void timers_run(GB_gameboy_t *gb, uint8_t cycles) advance_tima_state_machine(gb); GB_set_internal_div_counter(gb, gb->div_counter + 4); gb->apu.apu_cycles += 1 << !gb->cgb_double_speed; + gb->apu_output.sample_cycles += (gb->apu_output.sample_rate << !gb->cgb_double_speed) << 1; GB_SLEEP(gb, div, 2, 4); } } @@ -476,7 +478,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) if (likely(gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE)) { gb->double_speed_alignment += cycles; } - gb->apu_output.sample_cycles += cycles * gb->apu_output.sample_rate; gb->cycles_since_last_sync += cycles; gb->cycles_since_run += cycles;