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)
{
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));

View File

@ -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;

View File

@ -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: