From 5c7b1f01a31284dcd22ad18f88925a7b0468c7f2 Mon Sep 17 00:00:00 2001 From: Noah Sweilem Date: Sat, 10 Jun 2023 14:23:15 -0700 Subject: [PATCH 1/8] Add som APU query methods for libsameboy - GB_get_channel_volume() - GB_get_channel_amplitude() - GB_get_channel_period() - GB_get_apu_wave_table() --- Core/apu.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ Core/apu.h | 4 ++++ 2 files changed, 50 insertions(+) diff --git a/Core/apu.c b/Core/apu.c index 2196997..1efbd96 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -1750,3 +1750,49 @@ bool GB_is_channel_muted(GB_gameboy_t *gb, GB_channel_t channel) { return gb->apu_output.channel_muted[channel]; } + +uint8_t GB_get_channel_volume(GB_gameboy_t *gb, GB_channel_t channel) { + switch (channel) { + case GB_SQUARE_1: + case GB_SQUARE_2: + return gb->apu.square_channels[channel].current_volume; + + case GB_WAVE: + return (const uint8_t[]){0, 4, 8, 0, 0xF}[gb->apu.wave_channel.shift]; + + case GB_NOISE: + return gb->apu.noise_channel.current_volume; + + default: + return 0; + } +} + +uint8_t GB_get_channel_amplitude(GB_gameboy_t *gb, GB_channel_t channel) { + return gb->apu.is_active[channel] ? gb->apu.samples[channel] : 0; +} + +uint16_t GB_get_channel_period(GB_gameboy_t *gb, GB_channel_t channel) { + switch (channel) { + case GB_SQUARE_1: + case GB_SQUARE_2: + return gb->apu.square_channels[channel].sample_length; + + case GB_WAVE: + return gb->apu.wave_channel.sample_length; + + case GB_NOISE: + return (gb->io_registers[GB_IO_NR43] & 7) << (gb->io_registers[GB_IO_NR43] >> 4); + + default: + return 0; + } +} + +// wave_table is a user allocated uint8_t[32] array +void GB_get_apu_wave_table(GB_gameboy_t *gb, uint8_t *wave_table) { + for (unsigned i = GB_IO_WAV_START; i <= GB_IO_WAV_END; i++) { + wave_table[2 * (i - GB_IO_WAV_START)] = gb->io_registers[i] >> 4; + wave_table[2 * (i - GB_IO_WAV_START) + 1] = gb->io_registers[i] & 0xF; + } +} diff --git a/Core/apu.h b/Core/apu.h index 042299e..aec69ac 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -188,6 +188,10 @@ void GB_set_interference_volume(GB_gameboy_t *gb, double volume); void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback); int GB_start_audio_recording(GB_gameboy_t *gb, const char *path, GB_audio_format_t format); int GB_stop_audio_recording(GB_gameboy_t *gb); +uint8_t GB_get_channel_volume(GB_gameboy_t *gb, GB_channel_t channel); +uint8_t GB_get_channel_amplitude(GB_gameboy_t *gb, GB_channel_t channel); +uint16_t GB_get_channel_period(GB_gameboy_t *gb, GB_channel_t channel); +void GB_get_apu_wave_table(GB_gameboy_t *gb, uint8_t *wave_table); #ifdef GB_INTERNAL internal bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, GB_channel_t index); internal void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value); From 7bdf8dbe4791dd65092c661f13edbec64f9f4619 Mon Sep 17 00:00:00 2001 From: Noah Sweilem Date: Sun, 11 Jun 2023 15:27:31 -0700 Subject: [PATCH 2/8] Add initial edge trigger detection method - GB_get_channel_edge_triggered() --- Core/apu.c | 28 ++++++++++++++++++++++++++++ Core/apu.h | 7 +++++++ 2 files changed, 35 insertions(+) diff --git a/Core/apu.c b/Core/apu.c index 1efbd96..a2242c9 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -726,6 +726,7 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) gb->apu.pcm_mask[0] &= i == GB_SQUARE_1? 0xF0 : 0x0F; } gb->apu.square_channels[i].did_tick = true; + gb->apu.square_channels[i].edge_triggered = true; update_square_sample(gb, i); } if (cycles_left) { @@ -746,6 +747,7 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) gb->io_registers[GB_IO_WAV_START + (gb->apu.wave_channel.current_sample_index >> 1)]; update_wave_sample(gb, cycles - cycles_left); gb->apu.wave_channel.wave_form_just_read = true; + gb->apu.wave_channel.edge_triggered = true; } if (cycles_left) { gb->apu.wave_channel.sample_countdown -= cycles_left; @@ -805,6 +807,7 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) } else { gb->apu.noise_channel.countdown_reloaded = true; + gb->apu.noise_channel.edge_triggered = true; } } } @@ -1796,3 +1799,28 @@ void GB_get_apu_wave_table(GB_gameboy_t *gb, uint8_t *wave_table) { wave_table[2 * (i - GB_IO_WAV_START) + 1] = gb->io_registers[i] & 0xF; } } + +bool GB_get_channel_edge_triggered(GB_gameboy_t *gb, GB_channel_t channel) { + bool edge_triggered; + switch (channel) { + case GB_SQUARE_1: + case GB_SQUARE_2: + edge_triggered = gb->apu.square_channels[channel].edge_triggered; + gb->apu.square_channels[channel].edge_triggered = false; + break; + + case GB_WAVE: + edge_triggered = gb->apu.wave_channel.edge_triggered; + gb->apu.wave_channel.edge_triggered = false; + break; + + case GB_NOISE: + edge_triggered = gb->apu.noise_channel.edge_triggered; + gb->apu.noise_channel.edge_triggered = false; + break; + + default: + return false; + } + return edge_triggered; +} diff --git a/Core/apu.h b/Core/apu.h index aec69ac..6cd3b39 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -92,6 +92,8 @@ typedef struct GB_envelope_clock_t envelope_clock; uint8_t delay; // Hack for CGB D/E phantom step due to how sample_countdown is implemented in SameBoy bool did_tick; + + bool edge_triggered; } square_channels[2]; struct { @@ -107,6 +109,8 @@ typedef struct bool wave_form_just_read; bool pulsed; uint8_t bugged_read_countdown; + + bool edge_triggered; } wave_channel; struct { @@ -127,6 +131,8 @@ typedef struct bool countdown_reloaded; uint8_t dmg_delayed_start; GB_envelope_clock_t envelope_clock; + + bool edge_triggered; } noise_channel; GB_ENUM(uint8_t, { @@ -192,6 +198,7 @@ uint8_t GB_get_channel_volume(GB_gameboy_t *gb, GB_channel_t channel); uint8_t GB_get_channel_amplitude(GB_gameboy_t *gb, GB_channel_t channel); uint16_t GB_get_channel_period(GB_gameboy_t *gb, GB_channel_t channel); void GB_get_apu_wave_table(GB_gameboy_t *gb, uint8_t *wave_table); +bool GB_get_channel_edge_triggered(GB_gameboy_t *gb, GB_channel_t channel); #ifdef GB_INTERNAL internal bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, GB_channel_t index); internal void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value); From ec9536eede79d1704e88989051ac77f454c44615 Mon Sep 17 00:00:00 2001 From: Noah Sweilem Date: Sun, 11 Jun 2023 15:44:28 -0700 Subject: [PATCH 3/8] Try a modified edge trigger detection for pulse and wave channels --- Core/apu.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/apu.c b/Core/apu.c index a2242c9..71a06c2 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -726,8 +726,10 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) gb->apu.pcm_mask[0] &= i == GB_SQUARE_1? 0xF0 : 0x0F; } gb->apu.square_channels[i].did_tick = true; - gb->apu.square_channels[i].edge_triggered = true; update_square_sample(gb, i); + if (gb->apu.square_channels[i].current_sample_index == 0) { + gb->apu.square_channels[i].edge_triggered = true; + } } if (cycles_left) { gb->apu.square_channels[i].sample_countdown -= cycles_left; @@ -747,7 +749,9 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) gb->io_registers[GB_IO_WAV_START + (gb->apu.wave_channel.current_sample_index >> 1)]; update_wave_sample(gb, cycles - cycles_left); gb->apu.wave_channel.wave_form_just_read = true; - gb->apu.wave_channel.edge_triggered = true; + if (gb->apu.wave_channel.current_sample_index == 0) { + gb->apu.wave_channel.edge_triggered = true; + } } if (cycles_left) { gb->apu.wave_channel.sample_countdown -= cycles_left; From ae855d470d8785ac9a0a89e728e68b843cfb4c04 Mon Sep 17 00:00:00 2001 From: Noah Sweilem Date: Sun, 11 Jun 2023 16:23:26 -0700 Subject: [PATCH 4/8] Use different sample index for square channel edge trigger depending on duty cycle --- Core/apu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Core/apu.c b/Core/apu.c index 71a06c2..db56130 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -727,7 +727,10 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) } gb->apu.square_channels[i].did_tick = true; update_square_sample(gb, i); - if (gb->apu.square_channels[i].current_sample_index == 0) { + + uint8_t duty = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6; + uint8_t edge_sample_index = (const uint8_t[]){7, 7, 5, 1}[duty]; + if (gb->apu.square_channels[i].current_sample_index == edge_sample_index) { gb->apu.square_channels[i].edge_triggered = true; } } From 42135242866e47657ff58faf76c7f2a66a3710e2 Mon Sep 17 00:00:00 2001 From: Noah Sweilem Date: Fri, 16 Jun 2023 19:04:12 -0700 Subject: [PATCH 5/8] Fix swapped wave channel volume --- Core/apu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/apu.c b/Core/apu.c index db56130..ae223a1 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -1768,7 +1768,7 @@ uint8_t GB_get_channel_volume(GB_gameboy_t *gb, GB_channel_t channel) { return gb->apu.square_channels[channel].current_volume; case GB_WAVE: - return (const uint8_t[]){0, 4, 8, 0, 0xF}[gb->apu.wave_channel.shift]; + return (const uint8_t[]){0xF, 8, 4, 0, 0}[gb->apu.wave_channel.shift]; case GB_NOISE: return gb->apu.noise_channel.current_volume; From 9148b26bd847c572dfad2693fe8de1e5bc85c5e2 Mon Sep 17 00:00:00 2001 From: Noah Sweilem Date: Tue, 20 Jun 2023 10:25:25 -0700 Subject: [PATCH 6/8] Move `edge_triggered` variables into `gb->apu_output` --- Core/apu.c | 29 +++++------------------------ Core/apu.h | 7 +------ 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/Core/apu.c b/Core/apu.c index ae223a1..5f645e3 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -731,7 +731,7 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) uint8_t duty = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6; uint8_t edge_sample_index = (const uint8_t[]){7, 7, 5, 1}[duty]; if (gb->apu.square_channels[i].current_sample_index == edge_sample_index) { - gb->apu.square_channels[i].edge_triggered = true; + gb->apu_output.edge_triggered[i] = true; } } if (cycles_left) { @@ -753,7 +753,7 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) update_wave_sample(gb, cycles - cycles_left); gb->apu.wave_channel.wave_form_just_read = true; if (gb->apu.wave_channel.current_sample_index == 0) { - gb->apu.wave_channel.edge_triggered = true; + gb->apu_output.edge_triggered[GB_WAVE] = true; } } if (cycles_left) { @@ -814,7 +814,7 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) } else { gb->apu.noise_channel.countdown_reloaded = true; - gb->apu.noise_channel.edge_triggered = true; + gb->apu_output.edge_triggered[GB_NOISE] = true; } } } @@ -1808,26 +1808,7 @@ void GB_get_apu_wave_table(GB_gameboy_t *gb, uint8_t *wave_table) { } bool GB_get_channel_edge_triggered(GB_gameboy_t *gb, GB_channel_t channel) { - bool edge_triggered; - switch (channel) { - case GB_SQUARE_1: - case GB_SQUARE_2: - edge_triggered = gb->apu.square_channels[channel].edge_triggered; - gb->apu.square_channels[channel].edge_triggered = false; - break; - - case GB_WAVE: - edge_triggered = gb->apu.wave_channel.edge_triggered; - gb->apu.wave_channel.edge_triggered = false; - break; - - case GB_NOISE: - edge_triggered = gb->apu.noise_channel.edge_triggered; - gb->apu.noise_channel.edge_triggered = false; - break; - - default: - return false; - } + bool edge_triggered = gb->apu_output.edge_triggered[channel]; + gb->apu_output.edge_triggered[channel] = false; return edge_triggered; } diff --git a/Core/apu.h b/Core/apu.h index 6cd3b39..eb75a7e 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -92,8 +92,6 @@ typedef struct GB_envelope_clock_t envelope_clock; uint8_t delay; // Hack for CGB D/E phantom step due to how sample_countdown is implemented in SameBoy bool did_tick; - - bool edge_triggered; } square_channels[2]; struct { @@ -109,8 +107,6 @@ typedef struct bool wave_form_just_read; bool pulsed; uint8_t bugged_read_countdown; - - bool edge_triggered; } wave_channel; struct { @@ -131,8 +127,6 @@ typedef struct bool countdown_reloaded; uint8_t dmg_delayed_start; GB_envelope_clock_t envelope_clock; - - bool edge_triggered; } noise_channel; GB_ENUM(uint8_t, { @@ -168,6 +162,7 @@ typedef struct { GB_sample_t summed_samples[GB_N_CHANNELS]; double dac_discharge[GB_N_CHANNELS]; bool channel_muted[GB_N_CHANNELS]; + bool edge_triggered[GB_N_CHANNELS]; GB_highpass_mode_t highpass_mode; double highpass_rate; From dad9e4b704f3d1a7adae783b521b7e80e871582a Mon Sep 17 00:00:00 2001 From: Noah Sweilem Date: Tue, 20 Jun 2023 10:28:01 -0700 Subject: [PATCH 7/8] Formatting fixes --- Core/apu.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Core/apu.c b/Core/apu.c index 5f645e3..d3c7cea 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -1761,6 +1761,7 @@ bool GB_is_channel_muted(GB_gameboy_t *gb, GB_channel_t channel) return gb->apu_output.channel_muted[channel]; } +// Note: this intentionally does not check to see if the channel is muted. uint8_t GB_get_channel_volume(GB_gameboy_t *gb, GB_channel_t channel) { switch (channel) { case GB_SQUARE_1: @@ -1778,11 +1779,13 @@ uint8_t GB_get_channel_volume(GB_gameboy_t *gb, GB_channel_t channel) { } } -uint8_t GB_get_channel_amplitude(GB_gameboy_t *gb, GB_channel_t channel) { +uint8_t GB_get_channel_amplitude(GB_gameboy_t *gb, GB_channel_t channel) +{ return gb->apu.is_active[channel] ? gb->apu.samples[channel] : 0; } -uint16_t GB_get_channel_period(GB_gameboy_t *gb, GB_channel_t channel) { +uint16_t GB_get_channel_period(GB_gameboy_t *gb, GB_channel_t channel) +{ switch (channel) { case GB_SQUARE_1: case GB_SQUARE_2: @@ -1800,14 +1803,16 @@ uint16_t GB_get_channel_period(GB_gameboy_t *gb, GB_channel_t channel) { } // wave_table is a user allocated uint8_t[32] array -void GB_get_apu_wave_table(GB_gameboy_t *gb, uint8_t *wave_table) { +void GB_get_apu_wave_table(GB_gameboy_t *gb, uint8_t *wave_table) +{ for (unsigned i = GB_IO_WAV_START; i <= GB_IO_WAV_END; i++) { wave_table[2 * (i - GB_IO_WAV_START)] = gb->io_registers[i] >> 4; wave_table[2 * (i - GB_IO_WAV_START) + 1] = gb->io_registers[i] & 0xF; } } -bool GB_get_channel_edge_triggered(GB_gameboy_t *gb, GB_channel_t channel) { +bool GB_get_channel_edge_triggered(GB_gameboy_t *gb, GB_channel_t channel) +{ bool edge_triggered = gb->apu_output.edge_triggered[channel]; gb->apu_output.edge_triggered[channel] = false; return edge_triggered; From 074d152a4e52da4ff65b01c05b22c3db2028745c Mon Sep 17 00:00:00 2001 From: Noah Sweilem Date: Tue, 20 Jun 2023 10:28:18 -0700 Subject: [PATCH 8/8] Formatting fixes --- Core/apu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/apu.c b/Core/apu.c index d3c7cea..c9755ec 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -1762,7 +1762,8 @@ bool GB_is_channel_muted(GB_gameboy_t *gb, GB_channel_t channel) } // Note: this intentionally does not check to see if the channel is muted. -uint8_t GB_get_channel_volume(GB_gameboy_t *gb, GB_channel_t channel) { +uint8_t GB_get_channel_volume(GB_gameboy_t *gb, GB_channel_t channel) +{ switch (channel) { case GB_SQUARE_1: case GB_SQUARE_2: