diff --git a/Core/apu.c b/Core/apu.c index 9d78cef..9ef806b 100755 --- a/Core/apu.c +++ b/Core/apu.c @@ -62,10 +62,46 @@ static void render(GB_gameboy_t *gb) 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, + output.right - gb->apu_output.highpass_diff.right} : + output; + + switch (gb->apu_output.highpass_mode) { + case GB_HIGHPASS_OFF: + gb->apu_output.highpass_diff = (GB_double_sample_t) {0, 0}; + break; + case GB_HIGHPASS_ACCURATE: + gb->apu_output.highpass_diff = (GB_double_sample_t) + {output.left - filtered_output.left * gb->apu_output.highpass_rate, + output.right - filtered_output.right * gb->apu_output.highpass_rate}; + break; + case GB_HIGHPASS_REMOVE_DC_OFFSET: { + unsigned mask = gb->io_registers[GB_IO_NR51]; + unsigned left_volume = 0; + unsigned right_volume = 0; + for (unsigned i = GB_N_CHANNELS; i--;) { + if (mask & 1) { + left_volume += (gb->io_registers[GB_IO_NR50] & 7) * CH_STEP * 0xF; + } + if (mask & 0x10) { + right_volume += ((gb->io_registers[GB_IO_NR50] >> 4) & 7) * CH_STEP * 0xF; + } + mask >>= 1; + } + gb->apu_output.highpass_diff = (GB_double_sample_t) + {left_volume * (1 - gb->apu_output.highpass_rate) + gb->apu_output.highpass_diff.left * gb->apu_output.highpass_rate, + right_volume * (1 - gb->apu_output.highpass_rate) + gb->apu_output.highpass_diff.right * gb->apu_output.highpass_rate}; + + } + + } + while (gb->apu_output.copy_in_progress); while (!__sync_bool_compare_and_swap(&gb->apu_output.lock, false, true)); if (gb->apu_output.buffer_position < gb->apu_output.buffer_size) { - gb->apu_output.buffer[gb->apu_output.buffer_position++] = output; + gb->apu_output.buffer[gb->apu_output.buffer_position++] = filtered_output; } gb->apu_output.lock = false; } @@ -712,3 +748,22 @@ size_t GB_apu_get_current_buffer_length(GB_gameboy_t *gb) { return gb->apu_output.buffer_position; } + +void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate) +{ + if (gb->apu_output.buffer) { + free(gb->apu_output.buffer); + } + gb->apu_output.buffer_size = sample_rate / 25; // 40ms delay + gb->apu_output.buffer = malloc(gb->apu_output.buffer_size * sizeof(*gb->apu_output.buffer)); + gb->apu_output.sample_rate = sample_rate; + gb->apu_output.buffer_position = 0; + if (sample_rate) { + gb->apu_output.highpass_rate = pow(0.999958, CPU_FREQUENCY / (double)sample_rate); + } +} + +void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode) +{ + gb->apu_output.highpass_mode = mode; +} diff --git a/Core/apu.h b/Core/apu.h index 06a0a6e..ff70fb3 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -19,6 +19,12 @@ typedef struct int16_t right; } GB_sample_t; +typedef struct +{ + double left; + double right; +} GB_double_sample_t; + enum GB_CHANNELS { GB_SQUARE_1, GB_SQUARE_2, @@ -90,6 +96,12 @@ typedef struct } GB_apu_t; +typedef enum { + GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset + GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware + GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform +} GB_highpass_mode_t; + typedef struct { unsigned sample_rate; @@ -108,11 +120,16 @@ typedef struct { unsigned last_update[GB_N_CHANNELS]; GB_sample_t current_sample[GB_N_CHANNELS]; GB_sample_t summed_samples[GB_N_CHANNELS]; + + GB_highpass_mode_t highpass_mode; + double highpass_rate; + GB_double_sample_t highpass_diff; } GB_apu_output_t; void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate); void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, size_t count); size_t GB_apu_get_current_buffer_length(GB_gameboy_t *gb); +void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode); #ifdef GB_INTERNAL void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value); diff --git a/Core/gb.c b/Core/gb.c index 8a2e9e2..923b403 100755 --- a/Core/gb.c +++ b/Core/gb.c @@ -386,17 +386,6 @@ void GB_serial_set_data(GB_gameboy_t *gb, uint8_t data) gb->io_registers[GB_IO_IF] |= 8; } -void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate) -{ - if (gb->apu_output.buffer) { - free(gb->apu_output.buffer); - } - gb->apu_output.buffer_size = sample_rate / 25; // 40ms delay - gb->apu_output.buffer = malloc(gb->apu_output.buffer_size * sizeof(*gb->apu_output.buffer)); - gb->apu_output.sample_rate = sample_rate; - gb->apu_output.buffer_position = 0; -} - void GB_disconnect_serial(GB_gameboy_t *gb) { gb->serial_transfer_start_callback = NULL; @@ -495,7 +484,6 @@ void GB_switch_model_and_reset(GB_gameboy_t *gb, bool is_cgb) } gb->is_cgb = is_cgb; GB_reset(gb); - } void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank)