Added highpass filter
This commit is contained in:
parent
ca59aca4a6
commit
d04aaddcbd
57
Core/apu.c
57
Core/apu.c
@ -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;
|
||||||
|
}
|
||||||
|
17
Core/apu.h
17
Core/apu.h
@ -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);
|
||||||
|
12
Core/gb.c
12
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;
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user