More accurate emulation of the APU’s analog characteristics

This commit is contained in:
Lior Halphon 2018-02-16 18:01:50 +02:00
parent fc35111ae7
commit f79af39ea2
3 changed files with 44 additions and 18 deletions

View File

@ -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) 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]) {
if (gb->apu.is_active[index] && gb->apu_output.sample_rate) { 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; unsigned left_volume = 0;
if (gb->io_registers[GB_IO_NR51] & (1 << index)) { if (gb->io_registers[GB_IO_NR51] & (1 << index)) {
left_volume = (gb->io_registers[GB_IO_NR50] & 7) + 1; 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}; GB_sample_t output = {0,0};
for (unsigned i = GB_N_CHANNELS; i--;) { for (unsigned i = GB_N_CHANNELS; i--;) {
if (likely(gb->apu_output.last_update[i] == 0)) { double multiplier = CH_STEP;
output.left += gb->apu_output.current_sample[i].left * CH_STEP; if (!gb->apu.is_active[i]) {
output.right += gb->apu_output.current_sample[i].right * CH_STEP; 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 { else {
refresh_channel(gb, i, 0); 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; / 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.cycles_since_render;
gb->apu_output.summed_samples[i] = (GB_sample_t){0, 0}; gb->apu_output.summed_samples[i] = (GB_sample_t){0, 0};
} }
gb->apu_output.last_update[i] = 0; gb->apu_output.last_update[i] = 0;
} }
gb->apu_output.cycles_since_render = 0; gb->apu_output.cycles_since_render = 0;
GB_sample_t filtered_output = gb->apu_output.highpass_mode? GB_sample_t filtered_output = gb->apu_output.highpass_mode?
(GB_sample_t) {output.left - gb->apu_output.highpass_diff.left, (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 (gb->apu_output.copy_in_progress);
while (!__sync_bool_compare_and_swap(&gb->apu_output.lock, false, true)); 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) { if (gb->apu_output.sample_cycles > cycles_per_sample) {
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) { if (count > gb->apu_output.buffer_position) {
// GB_log(gb, "Audio underflow: %d\n", 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}; GB_sample_t output;
for (unsigned i = GB_N_CHANNELS; i--;) { render(gb, true, &output);
output.left += gb->apu_output.current_sample[i].left * CH_STEP;
output.right += gb->apu_output.current_sample[i].right * CH_STEP;
}
for (unsigned i = 0; i < count - gb->apu_output.buffer_position; i++) { for (unsigned i = 0; i < count - gb->apu_output.buffer_position; i++) {
dest[gb->apu_output.buffer_position + i] = output; dest[gb->apu_output.buffer_position + i] = output;
} }
count = gb->apu_output.buffer_position; count = gb->apu_output.buffer_position;
} }
memcpy(dest, gb->apu_output.buffer, count * sizeof(*gb->apu_output.buffer)); memcpy(dest, gb->apu_output.buffer, count * sizeof(*gb->apu_output.buffer));

View File

@ -6,6 +6,9 @@
#ifdef GB_INTERNAL #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 */ /* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */
#ifdef WIIU #ifdef WIIU
/* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/ /* 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) #define CH_STEP (MAX_CH_AMP/0xF/8)
#endif #endif
/* APU ticks are 2MHz, triggered by an internal APU clock. */ /* APU ticks are 2MHz, triggered by an internal APU clock. */
typedef struct typedef struct
@ -129,6 +134,7 @@ typedef struct {
unsigned last_update[GB_N_CHANNELS]; unsigned last_update[GB_N_CHANNELS];
GB_sample_t current_sample[GB_N_CHANNELS]; GB_sample_t current_sample[GB_N_CHANNELS];
GB_sample_t summed_samples[GB_N_CHANNELS]; GB_sample_t summed_samples[GB_N_CHANNELS];
double dac_discharge[GB_N_CHANNELS];
GB_highpass_mode_t highpass_mode; GB_highpass_mode_t highpass_mode;
double highpass_rate; double highpass_rate;

View File

@ -149,10 +149,12 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
case GB_IO_PCM_12: case GB_IO_PCM_12:
if (!gb->is_cgb) return 0xFF; 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: case GB_IO_PCM_34:
if (!gb->is_cgb) return 0xFF; 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_JOYP:
case GB_IO_TMA: case GB_IO_TMA:
case GB_IO_LCDC: case GB_IO_LCDC: