More accurate emulation of the APU’s analog characteristics
This commit is contained in:
parent
fc35111ae7
commit
f79af39ea2
48
Core/apu.c
48
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)
|
static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset)
|
||||||
{
|
{
|
||||||
|
if (!gb->apu.is_active[index]) {
|
||||||
|
value = gb->apu.samples[index];
|
||||||
|
}
|
||||||
|
else {
|
||||||
gb->apu.samples[index] = value;
|
gb->apu.samples[index] = value;
|
||||||
if (gb->apu.is_active[index] && gb->apu_output.sample_rate) {
|
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,19 +48,30 @@ 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};
|
||||||
}
|
}
|
||||||
@ -61,8 +79,6 @@ static void render(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
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,
|
||||||
output.right - gb->apu_output.highpass_diff.right} :
|
output.right - gb->apu_output.highpass_diff.right} :
|
||||||
@ -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));
|
||||||
|
@ -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;
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user