Added highpass filter

This commit is contained in:
Lior Halphon 2017-08-15 21:14:55 +03:00
parent ca59aca4a6
commit d04aaddcbd
3 changed files with 73 additions and 13 deletions

View File

@ -62,10 +62,46 @@ 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) {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 (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));
if (gb->apu_output.buffer_position < gb->apu_output.buffer_size) { 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; 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; 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;
}

View File

@ -19,6 +19,12 @@ typedef struct
int16_t right; int16_t right;
} GB_sample_t; } GB_sample_t;
typedef struct
{
double left;
double right;
} GB_double_sample_t;
enum GB_CHANNELS { enum GB_CHANNELS {
GB_SQUARE_1, GB_SQUARE_1,
GB_SQUARE_2, GB_SQUARE_2,
@ -90,6 +96,12 @@ typedef struct
} GB_apu_t; } 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 { typedef struct {
unsigned sample_rate; unsigned sample_rate;
@ -108,11 +120,16 @@ 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];
GB_highpass_mode_t highpass_mode;
double highpass_rate;
GB_double_sample_t highpass_diff;
} GB_apu_output_t; } GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate); 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); 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); 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 #ifdef GB_INTERNAL
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value); void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);

View File

@ -386,17 +386,6 @@ void GB_serial_set_data(GB_gameboy_t *gb, uint8_t data)
gb->io_registers[GB_IO_IF] |= 8; 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) void GB_disconnect_serial(GB_gameboy_t *gb)
{ {
gb->serial_transfer_start_callback = NULL; 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->is_cgb = is_cgb;
GB_reset(gb); GB_reset(gb);
} }
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank) void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank)