Complete rewrite of the APU. Channel 3 is complete and passes all the relevant tests from blargg’s suite, as well as PCM34-based tests. Actual sound output is basic and limited, though.

This commit is contained in:
Lior Halphon 2017-07-21 18:24:28 +03:00
parent c0a8a570e8
commit baccf336d7
6 changed files with 166 additions and 421 deletions

View File

@ -3,243 +3,72 @@
#include <string.h> #include <string.h>
#include "gb.h" #include "gb.h"
#undef max #define likely(x) __builtin_expect((x),1)
#define max(a,b) \ #define unlikely(x) __builtin_expect((x),0)
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
#undef min static void update_sample(GB_gameboy_t *gb, unsigned index, uint8_t value)
#define min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
#define APU_FREQUENCY 0x80000
static int16_t generate_square(uint64_t phase, uint32_t wave_length, int16_t amplitude, uint8_t duty)
{ {
if (!wave_length) return 0; gb->apu.samples[index] = value;
if (phase % wave_length > wave_length * duty / 8) {
return amplitude;
}
return 0;
} }
static int16_t generate_wave(uint64_t phase, uint32_t wave_length, int16_t amplitude, int8_t *wave, uint8_t shift) void GB_apu_div_event(GB_gameboy_t *gb)
{ {
if (!wave_length) wave_length = 1; if (gb->apu.is_active[GB_WAVE] && gb->apu.wave_channel.length_enabled) {
phase = phase % wave_length; if (gb->apu.wave_channel.pulse_length) {
return ((wave[(int)(phase * 32 / wave_length)]) >> shift) * (int)amplitude / 0xF; gb->apu.wave_channel.pulse_length--;
}
else {
gb->apu.is_active[GB_WAVE] = false;
gb->apu.wave_channel.current_sample = 0;
update_sample(gb, GB_WAVE, 0);
}
}
} }
static int16_t generate_noise(int16_t amplitude, uint16_t lfsr) static void render(GB_gameboy_t *gb)
{
if (lfsr & 1) {
return amplitude;
}
return 0;
}
static int16_t step_lfsr(uint16_t lfsr, bool uses_7_bit)
{
bool xor = (lfsr & 1) ^ ((lfsr & 2) >> 1);
lfsr >>= 1;
if (xor) {
lfsr |= 0x4000;
}
if (uses_7_bit) {
lfsr &= ~0x40;
if (xor) {
lfsr |= 0x40;
}
}
return lfsr;
}
/* General Todo: The APU emulation seems to fail many accuracy tests. It might require a rewrite with
these tests in mind. */
static void GB_apu_run_internal(GB_gameboy_t *gb)
{ {
while (gb->audio_copy_in_progress);
while (!__sync_bool_compare_and_swap(&gb->apu_lock, false, true)); while (!__sync_bool_compare_and_swap(&gb->apu_lock, false, true));
uint32_t steps = gb->apu.apu_cycles / (CPU_FREQUENCY/APU_FREQUENCY); if (gb->audio_position >= gb->buffer_size) {
if (!steps) goto exit; gb->apu_lock = false;
return;
gb->apu.apu_cycles %= (CPU_FREQUENCY/APU_FREQUENCY);
for (uint8_t i = 0; i < 4; i++) {
/* Phase */
gb->apu.wave_channels[i].phase += steps;
while (gb->apu.wave_channels[i].wave_length && gb->apu.wave_channels[i].phase >= gb->apu.wave_channels[i].wave_length) {
if (i == 3) {
gb->apu.lfsr = step_lfsr(gb->apu.lfsr, gb->apu.lfsr_7_bit);
} }
gb->audio_buffer[gb->audio_position++] = (GB_sample_t) {gb->apu.samples[GB_WAVE] * CH_STEP,
gb->apu.wave_channels[i].phase -= gb->apu.wave_channels[i].wave_length; gb->apu.samples[GB_WAVE] * CH_STEP};
}
/* Stop on Length */
if (gb->apu.wave_channels[i].stop_on_length) {
if (gb->apu.wave_channels[i].sound_length > 0) {
gb->apu.wave_channels[i].sound_length -= steps;
}
if (gb->apu.wave_channels[i].sound_length <= 0) {
gb->apu.wave_channels[i].amplitude = 0;
gb->apu.wave_channels[i].is_playing = false;
gb->apu.wave_channels[i].sound_length = i == 2? APU_FREQUENCY : APU_FREQUENCY / 4;
}
}
}
gb->apu.envelope_step_timer += steps;
while (gb->apu.envelope_step_timer >= APU_FREQUENCY / 64) {
gb->apu.envelope_step_timer -= APU_FREQUENCY / 64;
for (uint8_t i = 0; i < 4; i++) {
if (gb->apu.wave_channels[i].envelope_steps && !--gb->apu.wave_channels[i].cur_envelope_steps) {
gb->apu.wave_channels[i].amplitude = min(max(gb->apu.wave_channels[i].amplitude + gb->apu.wave_channels[i].envelope_direction * CH_STEP, 0), MAX_CH_AMP);
gb->apu.wave_channels[i].cur_envelope_steps = gb->apu.wave_channels[i].envelope_steps;
}
}
}
gb->apu.sweep_step_timer += steps;
while (gb->apu.sweep_step_timer >= APU_FREQUENCY / 128) {
gb->apu.sweep_step_timer -= APU_FREQUENCY / 128;
if (gb->apu.wave_channels[0].sweep_steps && !--gb->apu.wave_channels[0].cur_sweep_steps) {
// Convert back to GB format
uint16_t temp = 2048 - gb->apu.wave_channels[0].wave_length / (APU_FREQUENCY / 131072);
// Apply sweep
temp = temp + gb->apu.wave_channels[0].sweep_direction *
(temp / (1 << gb->apu.wave_channels[0].sweep_shift));
if (temp > 2047) {
temp = 0;
}
// Back to frequency
gb->apu.wave_channels[0].wave_length = (2048 - temp) * (APU_FREQUENCY / 131072);
gb->apu.wave_channels[0].cur_sweep_steps = gb->apu.wave_channels[0].sweep_steps;
}
}
exit:
gb->apu_lock = false; gb->apu_lock = false;
} }
void GB_apu_get_samples_and_update_pcm_regs(GB_gameboy_t *gb, GB_sample_t *samples) void GB_apu_run(GB_gameboy_t *gb, uint8_t cycles)
{ {
GB_apu_run_internal(gb); /* Convert 4MHZ to 2MHz. cycles is always even. */
cycles >>= 1;
samples->left = samples->right = 0; double cycles_per_sample = gb->sample_rate ? CPU_FREQUENCY / (double)gb->sample_rate : 0; // TODO: this should be cached!
if (!gb->apu.global_enable) {
return; if (gb->sample_rate && gb->apu_sample_cycles > cycles_per_sample) {
gb->apu_sample_cycles -= cycles_per_sample;
render(gb);
} }
gb->io_registers[GB_IO_PCM_12] = 0; gb->apu.wave_channel.wave_form_just_read = false;
gb->io_registers[GB_IO_PCM_34] = 0; if (gb->apu.is_active[GB_WAVE]) {
uint8_t cycles_left = cycles;
{ while (unlikely(cycles_left > gb->apu.wave_channel.sample_countdown)) {
int16_t sample = generate_square(gb->apu.wave_channels[0].phase, cycles_left -= gb->apu.wave_channel.sample_countdown + 1;
gb->apu.wave_channels[0].wave_length, gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length;
gb->apu.wave_channels[0].amplitude, gb->apu.wave_channel.current_sample_index++;
gb->apu.wave_channels[0].duty); gb->apu.wave_channel.current_sample_index &= 0x1F;
if (gb->apu.wave_channels[0].left_on ) samples->left += sample; gb->apu.wave_channel.current_sample =
if (gb->apu.wave_channels[0].right_on) samples->right += sample; gb->apu.wave_channel.wave_form[gb->apu.wave_channel.current_sample_index];
gb->io_registers[GB_IO_PCM_12] = ((int)sample) * 0xF / MAX_CH_AMP; update_sample(gb, GB_WAVE, gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift);
gb->apu.wave_channel.wave_form_just_read = true;
}
if (cycles_left) {
gb->apu.wave_channel.sample_countdown -= cycles_left;
gb->apu.wave_channel.wave_form_just_read = false;
}
} }
{
int16_t sample = generate_square(gb->apu.wave_channels[1].phase,
gb->apu.wave_channels[1].wave_length,
gb->apu.wave_channels[1].amplitude,
gb->apu.wave_channels[1].duty);
if (gb->apu.wave_channels[1].left_on ) samples->left += sample;
if (gb->apu.wave_channels[1].right_on) samples->right += sample;
gb->io_registers[GB_IO_PCM_12] |= (((int)sample) * 0xF / MAX_CH_AMP) << 4;
}
if (gb->apu.wave_channels[2].is_playing)
{
int16_t sample = generate_wave(gb->apu.wave_channels[2].phase,
gb->apu.wave_channels[2].wave_length,
MAX_CH_AMP,
gb->apu.wave_form,
gb->apu.wave_shift);
if (gb->apu.wave_channels[2].left_on ) samples->left += sample;
if (gb->apu.wave_channels[2].right_on) samples->right += sample;
gb->io_registers[GB_IO_PCM_34] = ((int)sample) * 0xF / MAX_CH_AMP;
}
{
int16_t sample = generate_noise(gb->apu.wave_channels[3].amplitude,
gb->apu.lfsr);
if (gb->apu.wave_channels[3].left_on ) samples->left += sample;
if (gb->apu.wave_channels[3].right_on) samples->right += sample;
gb->io_registers[GB_IO_PCM_34] |= (((int)sample) * 0xF / MAX_CH_AMP) << 4;
}
samples->left = (int) samples->left * gb->apu.left_volume / 7;
samples->right = (int) samples->right * gb->apu.right_volume / 7;
}
void GB_apu_run(GB_gameboy_t *gb)
{
if (gb->sample_rate == 0) {
if (gb->apu.apu_cycles > 0xFF00) {
GB_sample_t dummy;
GB_apu_get_samples_and_update_pcm_regs(gb, &dummy);
}
return;
}
while (gb->audio_copy_in_progress);
double ticks_per_sample = (double) CPU_FREQUENCY / gb->sample_rate;
if (gb->audio_quality == 0) {
GB_sample_t sample;
GB_apu_get_samples_and_update_pcm_regs(gb, &sample);
gb->current_supersample.left += sample.left;
gb->current_supersample.right += sample.right;
gb->n_subsamples++;
}
else if (gb->audio_quality != 1) {
double ticks_per_subsample = ticks_per_sample / gb->audio_quality;
if (ticks_per_subsample < 1) {
ticks_per_subsample = 1;
}
if (gb->apu_subsample_cycles > ticks_per_subsample) {
gb->apu_subsample_cycles -= ticks_per_subsample;
}
GB_sample_t sample;
GB_apu_get_samples_and_update_pcm_regs(gb, &sample);
gb->current_supersample.left += sample.left;
gb->current_supersample.right += sample.right;
gb->n_subsamples++;
}
if (gb->apu_sample_cycles > ticks_per_sample) {
gb->apu_sample_cycles -= ticks_per_sample;
if (gb->audio_position == gb->buffer_size) {
/*
if (!gb->turbo) {
GB_log(gb, "Audio overflow\n");
}
*/
}
else {
if (gb->audio_quality == 1) {
GB_apu_get_samples_and_update_pcm_regs(gb, &gb->audio_buffer[gb->audio_position++]);
}
else {
gb->audio_buffer[gb->audio_position].left = round(gb->current_supersample.left / gb->n_subsamples);
gb->audio_buffer[gb->audio_position].right = round(gb->current_supersample.right / gb->n_subsamples);
gb->n_subsamples = 0;
gb->current_supersample = (GB_double_sample_t){0, };
gb->audio_position++;
}
}
}
} }
void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count) void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count)
@ -274,24 +103,23 @@ void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count)
void GB_apu_init(GB_gameboy_t *gb) void GB_apu_init(GB_gameboy_t *gb)
{ {
memset(&gb->apu, 0, sizeof(gb->apu)); memset(&gb->apu, 0, sizeof(gb->apu));
gb->apu.wave_channels[0].duty = gb->apu.wave_channels[1].duty = 4; // gb->apu.wave_channels[0].duty = gb->apu.wave_channels[1].duty = 4;
gb->apu.lfsr = 0x7FFF; // gb->apu.lfsr = 0x7FFF;
gb->apu.left_volume = 7; gb->apu.left_volume = 7;
gb->apu.right_volume = 7; gb->apu.right_volume = 7;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
gb->apu.wave_channels[i].left_on = gb->apu.wave_channels[i].right_on = 1; gb->apu.left_enabled[i] = gb->apu.right_enabled[i] = true;
} }
gb->apu.wave_channel.sample_length = 0x7FF;
} }
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg) uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg)
{ {
GB_apu_run_internal(gb);
if (reg == GB_IO_NR52) { if (reg == GB_IO_NR52) {
uint8_t value = 0; uint8_t value = 0;
for (int i = 0; i < 4; i++) { for (int i = 0; i < GB_N_CHANNELS; i++) {
value >>= 1; value >>= 1;
if (gb->apu.wave_channels[i].is_playing) { if (gb->apu.is_active[i]) {
value |= 0x8; value |= 0x8;
} }
} }
@ -315,12 +143,11 @@ uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg)
0, /* ... */ 0, /* ... */
}; };
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.wave_channels[2].is_playing) { if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) {
if (gb->apu.wave_channels[2].wave_length == 0) { if (!gb->is_cgb && !gb->apu.wave_channel.wave_form_just_read) {
return gb->apu.wave_form[0]; return 0xFF;
} }
gb->apu.wave_channels[2].phase %= gb->apu.wave_channels[2].wave_length; reg = GB_IO_WAV_START + gb->apu.wave_channel.current_sample_index / 2;
return gb->apu.wave_form[(int)(gb->apu.wave_channels[2].phase * 32 / gb->apu.wave_channels[2].wave_length)];
} }
return gb->io_registers[reg] | read_mask[reg - GB_IO_NR10]; return gb->io_registers[reg] | read_mask[reg - GB_IO_NR10];
@ -328,145 +155,23 @@ uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg)
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)
{ {
GB_apu_run_internal(gb);
static const uint8_t duties[] = {1, 2, 4, 6}; /* Values are in 1/8 */
uint8_t channel = 0;
if (!gb->apu.global_enable && reg != GB_IO_NR52) { if (!gb->apu.global_enable && reg != GB_IO_NR52) {
return; return;
} }
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) {
if (!gb->is_cgb && !gb->apu.wave_channel.wave_form_just_read) {
return;
}
reg = GB_IO_WAV_START + gb->apu.wave_channel.current_sample_index / 2;
}
gb->io_registers[reg] = value; gb->io_registers[reg] = value;
switch (reg) {
case GB_IO_NR10:
case GB_IO_NR11:
case GB_IO_NR12:
case GB_IO_NR13:
case GB_IO_NR14:
channel = 0;
break;
case GB_IO_NR21:
case GB_IO_NR22:
case GB_IO_NR23:
case GB_IO_NR24:
channel = 1;
break;
case GB_IO_NR33:
case GB_IO_NR34:
channel = 2;
break;
case GB_IO_NR41:
case GB_IO_NR42:
channel = 3;
default:
break;
}
switch (reg) { switch (reg) {
case GB_IO_NR10: /* Globals */
gb->apu.wave_channels[channel].sweep_direction = value & 8? -1 : 1;
gb->apu.wave_channels[channel].cur_sweep_steps =
gb->apu.wave_channels[channel].sweep_steps = (value & 0x70) >> 4;
gb->apu.wave_channels[channel].sweep_shift = value & 7;
break;
case GB_IO_NR11:
case GB_IO_NR21:
case GB_IO_NR41:
gb->apu.wave_channels[channel].duty = duties[value >> 6];
gb->apu.wave_channels[channel].sound_length = (64 - (value & 0x3F)) * (APU_FREQUENCY / 256);
if (gb->apu.wave_channels[channel].sound_length == 0) {
gb->apu.wave_channels[channel].is_playing = false;
}
break;
case GB_IO_NR12:
case GB_IO_NR22:
case GB_IO_NR42:
gb->apu.wave_channels[channel].start_amplitude =
gb->apu.wave_channels[channel].amplitude = CH_STEP * (value >> 4);
if (value >> 4 == 0) {
gb->apu.wave_channels[channel].is_playing = false;
}
gb->apu.wave_channels[channel].envelope_direction = value & 8? 1 : -1;
gb->apu.wave_channels[channel].cur_envelope_steps =
gb->apu.wave_channels[channel].envelope_steps = value & 7;
break;
case GB_IO_NR13:
case GB_IO_NR23:
case GB_IO_NR33:
gb->apu.wave_channels[channel].NRX3_X4_temp = (gb->apu.wave_channels[channel].NRX3_X4_temp & 0xFF00) | value;
gb->apu.wave_channels[channel].wave_length = (2048 - gb->apu.wave_channels[channel].NRX3_X4_temp) * (APU_FREQUENCY / 131072);
if (channel == 2) {
gb->apu.wave_channels[channel].wave_length *= 2;
}
break;
case GB_IO_NR14:
case GB_IO_NR24:
case GB_IO_NR34:
gb->apu.wave_channels[channel].stop_on_length = value & 0x40;
if ((value & 0x80) && (channel != 2 || gb->apu.wave_enable)) {
gb->apu.wave_channels[channel].is_playing = true;
gb->apu.wave_channels[channel].phase = 0;
gb->apu.wave_channels[channel].amplitude = gb->apu.wave_channels[channel].start_amplitude;
gb->apu.wave_channels[channel].cur_envelope_steps = gb->apu.wave_channels[channel].envelope_steps;
}
gb->apu.wave_channels[channel].NRX3_X4_temp = (gb->apu.wave_channels[channel].NRX3_X4_temp & 0xFF) | ((value & 0x7) << 8);
gb->apu.wave_channels[channel].wave_length = (2048 - gb->apu.wave_channels[channel].NRX3_X4_temp) * (APU_FREQUENCY / 131072);
if (channel == 2) {
gb->apu.wave_channels[channel].wave_length *= 2;
}
break;
case GB_IO_NR30:
gb->apu.wave_enable = value & 0x80;
gb->apu.wave_channels[2].is_playing &= gb->apu.wave_enable;
break;
case GB_IO_NR31:
gb->apu.wave_channels[2].sound_length = (256 - value) * (APU_FREQUENCY / 256);
if (gb->apu.wave_channels[2].sound_length == 0) {
gb->apu.wave_channels[2].is_playing = false;
}
break;
case GB_IO_NR32:
gb->apu.wave_shift = ((value >> 5) + 3) & 3;
if (gb->apu.wave_shift == 3) {
gb->apu.wave_shift = 4;
}
break;
case GB_IO_NR43:
{
double r = value & 0x7;
if (r == 0) r = 0.5;
uint8_t s = value >> 4;
gb->apu.wave_channels[3].wave_length = r * (1 << s) * (APU_FREQUENCY / 262144) ;
gb->apu.lfsr_7_bit = value & 0x8;
break;
}
case GB_IO_NR44:
gb->apu.wave_channels[3].stop_on_length = value & 0x40;
if (value & 0x80) {
gb->apu.wave_channels[3].is_playing = true;
gb->apu.lfsr = 0x7FFF;
gb->apu.wave_channels[3].amplitude = gb->apu.wave_channels[3].start_amplitude;
gb->apu.wave_channels[3].cur_envelope_steps = gb->apu.wave_channels[3].envelope_steps;
}
break;
case GB_IO_NR50:
gb->apu.left_volume = (value & 7);
gb->apu.right_volume = ((value >> 4) & 7);
break;
case GB_IO_NR51:
for (int i = 0; i < 4; i++) {
gb->apu.wave_channels[i].left_on = value & 1;
gb->apu.wave_channels[i].right_on = value & 0x10;
value >>= 1;
}
break;
case GB_IO_NR52: case GB_IO_NR52:
if ((value & 0x80) && !gb->apu.global_enable) { if ((value & 0x80) && !gb->apu.global_enable) {
GB_apu_init(gb); GB_apu_init(gb);
gb->apu.global_enable = true; gb->apu.global_enable = true;
@ -474,21 +179,71 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
else if (!(value & 0x80) && gb->apu.global_enable) { else if (!(value & 0x80) && gb->apu.global_enable) {
memset(&gb->apu, 0, sizeof(gb->apu)); memset(&gb->apu, 0, sizeof(gb->apu));
memset(gb->io_registers + GB_IO_NR10, 0, GB_IO_WAV_START - GB_IO_NR10); memset(gb->io_registers + GB_IO_NR10, 0, GB_IO_WAV_START - GB_IO_NR10);
gb->apu.global_enable = false;
}
break;
/* Wave channel */
case GB_IO_NR30:
gb->apu.wave_channel.enable = value & 0x80;
if (!gb->apu.wave_channel.enable) {
gb->apu.is_active[GB_WAVE] = false;
gb->apu.wave_channel.current_sample = 0;
update_sample(gb, GB_WAVE, 0);
}
break;
case GB_IO_NR31:
break;
case GB_IO_NR32:
gb->apu.wave_channel.shift = (uint8_t[]){4, 0, 1, 2}[(value >> 5) & 3];
update_sample(gb, GB_WAVE, gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift);
break;
case GB_IO_NR33:
gb->apu.wave_channel.sample_length &= ~0xFF;
gb->apu.wave_channel.sample_length |= (~value) & 0xFF;
break;
case GB_IO_NR34:
gb->apu.wave_channel.length_enabled = value & 0x40;
gb->apu.wave_channel.sample_length &= 0xFF;
gb->apu.wave_channel.sample_length |= ((~value) & 7) << 8;
if ((value & 0x80) && gb->apu.wave_channel.enable) {
/* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU
reads from it. */
if (!gb->is_cgb && gb->apu.is_active[GB_WAVE] && gb->apu.wave_channel.sample_countdown == 0) {
unsigned offset = ((gb->apu.wave_channel.current_sample_index + 1) >> 1) & 0xF;
/* On SGB2 (and probably SGB1 and MGB as well) this behavior is not accurate,
however these systems are not currently emulated. */
if (offset < 4) {
gb->io_registers[GB_IO_WAV_START] = gb->io_registers[GB_IO_WAV_START + offset];
gb->apu.wave_channel.wave_form[0] = gb->apu.wave_channel.wave_form[offset / 2];
gb->apu.wave_channel.wave_form[1] = gb->apu.wave_channel.wave_form[offset / 2 + 1];
}
else {
memcpy(gb->io_registers + GB_IO_WAV_START,
gb->io_registers + GB_IO_WAV_START + (offset & ~3),
4);
memcpy(gb->apu.wave_channel.wave_form,
gb->apu.wave_channel.wave_form + (offset & ~3) * 2,
8);
}
}
gb->apu.is_active[GB_WAVE] = true;
gb->apu.wave_channel.pulse_length = ~gb->io_registers[GB_IO_NR31];
gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length + 3;
gb->apu.wave_channel.current_sample_index = 0;
/* Note that we don't change the sample just yet! This was verified on hardware. */
} }
break; break;
default: default:
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END) { if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END) {
gb->apu.wave_form[(reg - GB_IO_WAV_START) * 2] = value >> 4; gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = value >> 4;
gb->apu.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = value & 0xF; gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = value & 0xF;
} }
break;
} }
}
void GB_set_audio_quality(GB_gameboy_t *gb, unsigned quality)
{
gb->audio_quality = quality;
} }
unsigned GB_apu_get_current_buffer_length(GB_gameboy_t *gb) unsigned GB_apu_get_current_buffer_length(GB_gameboy_t *gb)

View File

@ -3,10 +3,16 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "gb_struct_def.h" #include "gb_struct_def.h"
#ifdef GB_INTERNAL
/* Divides nicely and never overflows with 4 channels */ /* Divides nicely and never overflows with 4 channels */
#define MAX_CH_AMP 0x1E00 #define MAX_CH_AMP 0x1E00
#define CH_STEP (0x1E00/0xF) #define CH_STEP (MAX_CH_AMP/0xF)
#endif
/* Lengths are in either DIV ticks (256Hz, triggered by the DIV register) or
APU ticks (2MHz, triggered by an internal APU clock)*/
typedef struct typedef struct
{ {
@ -20,59 +26,51 @@ typedef struct
double right; double right;
} GB_double_sample_t; } GB_double_sample_t;
/* Not all used on all channels */ enum GB_CHANNELS {
/* All lengths are in APU ticks */ GB_SQUARE_1,
typedef struct GB_SQUARE_2,
{ GB_WAVE,
uint32_t phase; GB_NOISE,
uint32_t wave_length; GB_N_CHANNELS
int32_t sound_length; };
bool stop_on_length;
uint8_t duty;
int16_t amplitude;
int16_t start_amplitude;
uint8_t envelope_steps;
uint8_t cur_envelope_steps;
int8_t envelope_direction;
uint8_t sweep_steps;
uint8_t cur_sweep_steps;
int8_t sweep_direction;
uint8_t sweep_shift;
bool is_playing;
uint16_t NRX3_X4_temp;
bool left_on;
bool right_on;
} GB_apu_channel_t;
typedef struct typedef struct
{ {
uint16_t apu_cycles;
bool global_enable; bool global_enable;
uint32_t envelope_step_timer;
uint32_t sweep_step_timer;
int8_t wave_form[32];
uint8_t wave_shift;
bool wave_enable;
uint16_t lfsr;
bool lfsr_7_bit;
uint8_t left_volume; uint8_t left_volume;
uint8_t right_volume; uint8_t right_volume;
GB_apu_channel_t wave_channels[4];
uint8_t samples[GB_N_CHANNELS];
bool left_enabled[GB_N_CHANNELS];
bool right_enabled[GB_N_CHANNELS];
bool is_active[GB_N_CHANNELS];
struct {
bool enable; // NR30
uint8_t pulse_length; // Reloaded from NR31 (xorred), in DIV ticks
uint8_t shift; // NR32
uint16_t sample_length; // NR33, NR34, in APU ticks
bool length_enabled; // NR34
uint16_t sample_countdown; // in APU ticks
uint8_t current_sample_index;
uint8_t current_sample; // Current sample before shifting.
int8_t wave_form[32];
bool wave_form_just_read;
} wave_channel;
} GB_apu_t; } GB_apu_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);
/* Quality is the number of subsamples per sampling, for the sake of resampling.
1 means on resampling at all, 0 is maximum quality. Default is 4. */
void GB_set_audio_quality(GB_gameboy_t *gb, unsigned quality);
void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count); void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count);
unsigned GB_apu_get_current_buffer_length(GB_gameboy_t *gb); unsigned GB_apu_get_current_buffer_length(GB_gameboy_t *gb);
#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);
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg); uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
void GB_apu_get_samples_and_update_pcm_regs(GB_gameboy_t *gb, GB_sample_t *samples); void GB_apu_div_event(GB_gameboy_t *gb);
void GB_apu_init(GB_gameboy_t *gb); void GB_apu_init(GB_gameboy_t *gb);
void GB_apu_run(GB_gameboy_t *gb); void GB_apu_run(GB_gameboy_t *gb, uint8_t cycles);
#endif #endif
#endif /* apu_h */ #endif /* apu_h */

View File

@ -92,7 +92,6 @@ void GB_init(GB_gameboy_t *gb)
gb->input_callback = default_input_callback; gb->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback; gb->async_input_callback = default_async_input_callback;
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->audio_quality = 4;
GB_reset(gb); GB_reset(gb);
} }
@ -107,7 +106,6 @@ void GB_init_cgb(GB_gameboy_t *gb)
gb->input_callback = default_input_callback; gb->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback; gb->async_input_callback = default_async_input_callback;
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->audio_quality = 4;
GB_reset(gb); GB_reset(gb);
} }

View File

@ -419,11 +419,6 @@ struct GB_gameboy_internal_s {
volatile bool audio_copy_in_progress; volatile bool audio_copy_in_progress;
volatile bool apu_lock; volatile bool apu_lock;
double apu_sample_cycles; double apu_sample_cycles;
double apu_subsample_cycles;
GB_double_sample_t current_supersample;
unsigned n_subsamples;
unsigned audio_quality;
/* Callbacks */ /* Callbacks */
void *user_data; void *user_data;

View File

@ -148,13 +148,11 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
return gb->io_registers[GB_IO_DMG_EMULATION_INDICATION] | 0xFE; return gb->io_registers[GB_IO_DMG_EMULATION_INDICATION] | 0xFE;
case GB_IO_PCM_12: case GB_IO_PCM_12:
case GB_IO_PCM_34:
{
if (!gb->is_cgb) return 0xFF; if (!gb->is_cgb) return 0xFF;
GB_sample_t dummy; return (gb->apu.samples[GB_SQUARE_2] << 4) | gb->apu.samples[GB_SQUARE_1];
GB_apu_get_samples_and_update_pcm_regs(gb, &dummy); case GB_IO_PCM_34:
} if (!gb->is_cgb) return 0xFF;
/* Fall through */ return (gb->apu.samples[GB_NOISE] << 4) | gb->apu.samples[GB_WAVE];
case GB_IO_JOYP: case GB_IO_JOYP:
case GB_IO_TMA: case GB_IO_TMA:
case GB_IO_LCDC: case GB_IO_LCDC:

View File

@ -134,14 +134,12 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
// Not affected by speed boost // Not affected by speed boost
gb->hdma_cycles += cycles; gb->hdma_cycles += cycles;
gb->apu_sample_cycles += cycles; gb->apu_sample_cycles += cycles;
gb->apu_subsample_cycles += cycles;
gb->apu.apu_cycles += cycles;
gb->cycles_since_ir_change += cycles; gb->cycles_since_ir_change += cycles;
gb->cycles_since_input_ir_change += cycles; gb->cycles_since_input_ir_change += cycles;
gb->cycles_since_last_sync += cycles; gb->cycles_since_last_sync += cycles;
GB_dma_run(gb); GB_dma_run(gb);
GB_hdma_run(gb); GB_hdma_run(gb);
GB_apu_run(gb); GB_apu_run(gb, cycles);
GB_display_run(gb, cycles); GB_display_run(gb, cycles);
GB_ir_run(gb); GB_ir_run(gb);
} }
@ -172,6 +170,9 @@ void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value)
counter_overflow_check(gb->div_cycles, value, GB_TAC_RATIOS[gb->io_registers[GB_IO_TAC] & 3])) { counter_overflow_check(gb->div_cycles, value, GB_TAC_RATIOS[gb->io_registers[GB_IO_TAC] & 3])) {
increase_tima(gb); increase_tima(gb);
} }
if (counter_overflow_check(gb->div_cycles, value, gb->cgb_double_speed? 0x4000 : 0x2000)) {
GB_apu_div_event(gb);
}
gb->div_cycles = value; gb->div_cycles = value;
} }