From f79af39ea202008a860f6ac07dd0ec19b9613f57 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 16 Feb 2018 18:01:50 +0200 Subject: [PATCH] =?UTF-8?q?More=20accurate=20emulation=20of=20the=20APU?= =?UTF-8?q?=E2=80=99s=20analog=20characteristics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/apu.c | 50 ++++++++++++++++++++++++++++++++++---------------- Core/apu.h | 6 ++++++ Core/memory.c | 6 ++++-- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/Core/apu.c b/Core/apu.c index 0164f31..59970ec 100755 --- a/Core/apu.c +++ b/Core/apu.c @@ -23,8 +23,15 @@ static void refresh_channel(GB_gameboy_t *gb, unsigned index, unsigned cycles_of static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset) { - gb->apu.samples[index] = value; - if (gb->apu.is_active[index] && gb->apu_output.sample_rate) { + if (!gb->apu.is_active[index]) { + value = gb->apu.samples[index]; + } + else { + gb->apu.samples[index] = value; + gb->apu_output.dac_discharge[index] = 1.0; + } + + if (gb->apu_output.sample_rate) { unsigned left_volume = 0; if (gb->io_registers[GB_IO_NR51] & (1 << index)) { left_volume = (gb->io_registers[GB_IO_NR50] & 7) + 1; @@ -41,27 +48,36 @@ static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsign } } -static void render(GB_gameboy_t *gb) +static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest) { GB_sample_t output = {0,0}; for (unsigned i = GB_N_CHANNELS; i--;) { - if (likely(gb->apu_output.last_update[i] == 0)) { - output.left += gb->apu_output.current_sample[i].left * CH_STEP; - output.right += gb->apu_output.current_sample[i].right * CH_STEP; + double multiplier = CH_STEP; + if (!gb->apu.is_active[i]) { + gb->apu_output.dac_discharge[i] -= ((double) DAC_DECAY_SPEED) / gb->apu_output.sample_rate; + if (gb->apu_output.dac_discharge[i] < 0) { + multiplier = 0; + } + else { + multiplier *= pow(0.05, 1 - gb->apu_output.dac_discharge[i]) * (gb->apu_output.dac_discharge[i]); + } + } + + if (likely(gb->apu_output.last_update[i] == 0 || no_downsampling)) { + output.left += gb->apu_output.current_sample[i].left * multiplier; + output.right += gb->apu_output.current_sample[i].right * multiplier; } else { refresh_channel(gb, i, 0); - output.left += (signed long) gb->apu_output.summed_samples[i].left * CH_STEP + output.left += (signed long) gb->apu_output.summed_samples[i].left * multiplier / gb->apu_output.cycles_since_render; - output.right += (signed long) gb->apu_output.summed_samples[i].right * CH_STEP + output.right += (signed long) gb->apu_output.summed_samples[i].right * multiplier / gb->apu_output.cycles_since_render; gb->apu_output.summed_samples[i] = (GB_sample_t){0, 0}; } gb->apu_output.last_update[i] = 0; } gb->apu_output.cycles_since_render = 0; - - GB_sample_t filtered_output = gb->apu_output.highpass_mode? (GB_sample_t) {output.left - gb->apu_output.highpass_diff.left, @@ -104,6 +120,10 @@ static void render(GB_gameboy_t *gb) } } + 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)); @@ -369,7 +389,7 @@ void GB_apu_run(GB_gameboy_t *gb) if (gb->apu_output.sample_cycles > cycles_per_sample) { gb->apu_output.sample_cycles -= cycles_per_sample; - render(gb); + render(gb, false, NULL); } } } @@ -386,15 +406,13 @@ void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, size_t count) if (count > gb->apu_output.buffer_position) { // GB_log(gb, "Audio underflow: %d\n", count - gb->apu_output.buffer_position); - GB_sample_t output = {-gb->apu_output.highpass_diff.left, -gb->apu_output.highpass_diff.right}; - for (unsigned i = GB_N_CHANNELS; i--;) { - output.left += gb->apu_output.current_sample[i].left * CH_STEP; - output.right += gb->apu_output.current_sample[i].right * CH_STEP; - } + GB_sample_t output; + render(gb, true, &output); for (unsigned i = 0; i < count - gb->apu_output.buffer_position; i++) { dest[gb->apu_output.buffer_position + i] = output; } + count = gb->apu_output.buffer_position; } memcpy(dest, gb->apu_output.buffer, count * sizeof(*gb->apu_output.buffer)); diff --git a/Core/apu.h b/Core/apu.h index 495bea8..bd69ac4 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -6,6 +6,9 @@ #ifdef GB_INTERNAL +/* Speed = 1 / Length (in seconds) */ +#define DAC_DECAY_SPEED 500.0 + /* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */ #ifdef WIIU /* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/ @@ -16,6 +19,8 @@ #define CH_STEP (MAX_CH_AMP/0xF/8) #endif + + /* APU ticks are 2MHz, triggered by an internal APU clock. */ typedef struct @@ -129,6 +134,7 @@ typedef struct { unsigned last_update[GB_N_CHANNELS]; GB_sample_t current_sample[GB_N_CHANNELS]; GB_sample_t summed_samples[GB_N_CHANNELS]; + double dac_discharge[GB_N_CHANNELS]; GB_highpass_mode_t highpass_mode; double highpass_rate; diff --git a/Core/memory.c b/Core/memory.c index b41057f..ef2f007 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -149,10 +149,12 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) case GB_IO_PCM_12: if (!gb->is_cgb) return 0xFF; - return (gb->apu.samples[GB_SQUARE_2] << 4) | gb->apu.samples[GB_SQUARE_1]; + return (gb->apu.is_active[GB_SQUARE_2] ? (gb->apu.samples[GB_SQUARE_2] << 4) : 0) | + (gb->apu.is_active[GB_SQUARE_1] ? (gb->apu.samples[GB_SQUARE_1]) : 0); case GB_IO_PCM_34: if (!gb->is_cgb) return 0xFF; - return (gb->apu.samples[GB_NOISE] << 4) | gb->apu.samples[GB_WAVE]; + return (gb->apu.is_active[GB_NOISE] ? (gb->apu.samples[GB_NOISE] << 4) : 0) | + (gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0); case GB_IO_JOYP: case GB_IO_TMA: case GB_IO_LCDC: