Skip to content

Commit

Permalink
Improve audio anti-aliasing, allow 2MHz output
Browse files Browse the repository at this point in the history
  • Loading branch information
LIJI32 committed Jun 23, 2024
1 parent ad5ca0d commit d07b199
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 5 deletions.
53 changes: 49 additions & 4 deletions Core/apu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)))) {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions Core/apu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions Core/gb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
3 changes: 2 additions & 1 deletion Core/timing.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit d07b199

Please sign in to comment.