Removed doubles, reorganized code a bit
This commit is contained in:
parent
b95860c034
commit
ff7b8a6854
53
Core/apu.c
53
Core/apu.c
@ -18,10 +18,10 @@ _a < _b ? _a : _b; })
|
|||||||
|
|
||||||
#define APU_FREQUENCY 0x80000
|
#define APU_FREQUENCY 0x80000
|
||||||
|
|
||||||
static int16_t generate_square(uint64_t phase, uint32_t wave_length, int16_t amplitude, double duty)
|
static int16_t generate_square(uint64_t phase, uint32_t wave_length, int16_t amplitude, uint8_t duty)
|
||||||
{
|
{
|
||||||
if (!wave_length) return 0;
|
if (!wave_length) return 0;
|
||||||
if (phase % wave_length > wave_length * duty) {
|
if (phase % wave_length > wave_length * duty / 8) {
|
||||||
return amplitude;
|
return amplitude;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -58,16 +58,11 @@ static int16_t step_lfsr(uint16_t lfsr, bool uses_7_bit)
|
|||||||
return lfsr;
|
return lfsr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* General Todo: The APU emulation seems to fail many accuracy tests. It might require a rewrite with
|
void GB_apu_get_samples_and_update_pcm_regs(GB_gameboy_t *gb, GB_sample_t *samples)
|
||||||
these tests in mind. */
|
|
||||||
|
|
||||||
void GB_apu_run_internal(GB_gameboy_t *gb, unsigned int n_cycles, GB_sample_t *samples)
|
|
||||||
{
|
{
|
||||||
while (n_cycles--) {
|
|
||||||
if (n_cycles == 0) {
|
|
||||||
samples->left = samples->right = 0;
|
samples->left = samples->right = 0;
|
||||||
if (!gb->apu.global_enable) {
|
if (!gb->apu.global_enable) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->io_registers[GB_IO_PCM_12] = 0;
|
gb->io_registers[GB_IO_PCM_12] = 0;
|
||||||
@ -117,6 +112,13 @@ void GB_apu_run_internal(GB_gameboy_t *gb, unsigned int n_cycles, GB_sample_t *s
|
|||||||
samples->right *= gb->apu.right_volume;
|
samples->right *= gb->apu.right_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
for (;gb->apu.apu_cycles >= CPU_FREQUENCY/APU_FREQUENCY; gb->apu.apu_cycles -= CPU_FREQUENCY/APU_FREQUENCY) {
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
/* Phase */
|
/* Phase */
|
||||||
gb->apu.wave_channels[i].phase++;
|
gb->apu.wave_channels[i].phase++;
|
||||||
@ -130,19 +132,19 @@ void GB_apu_run_internal(GB_gameboy_t *gb, unsigned int n_cycles, GB_sample_t *s
|
|||||||
/* Stop on Length */
|
/* Stop on Length */
|
||||||
if (gb->apu.wave_channels[i].stop_on_length) {
|
if (gb->apu.wave_channels[i].stop_on_length) {
|
||||||
if (gb->apu.wave_channels[i].sound_length > 0) {
|
if (gb->apu.wave_channels[i].sound_length > 0) {
|
||||||
gb->apu.wave_channels[i].sound_length -= 1.0 / APU_FREQUENCY;
|
gb->apu.wave_channels[i].sound_length -= 1;
|
||||||
}
|
}
|
||||||
if (gb->apu.wave_channels[i].sound_length <= 0) {
|
if (gb->apu.wave_channels[i].sound_length <= 0) {
|
||||||
gb->apu.wave_channels[i].amplitude = 0;
|
gb->apu.wave_channels[i].amplitude = 0;
|
||||||
gb->apu.wave_channels[i].is_playing = false;
|
gb->apu.wave_channels[i].is_playing = false;
|
||||||
gb->apu.wave_channels[i].sound_length = i == 2? 1 : 0.25;
|
gb->apu.wave_channels[i].sound_length = i == 2? APU_FREQUENCY : APU_FREQUENCY / 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->apu.envelope_step_timer += 1.0 / APU_FREQUENCY;
|
gb->apu.envelope_step_timer += 1;
|
||||||
if (gb->apu.envelope_step_timer >= 1.0 / 64) {
|
if (gb->apu.envelope_step_timer >= APU_FREQUENCY / 64) {
|
||||||
gb->apu.envelope_step_timer -= 1.0 / 64;
|
gb->apu.envelope_step_timer -= APU_FREQUENCY / 64;
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
if (gb->apu.wave_channels[i].envelope_steps && !--gb->apu.wave_channels[i].cur_envelope_steps) {
|
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].amplitude = min(max(gb->apu.wave_channels[i].amplitude + gb->apu.wave_channels[i].envelope_direction * CH_STEP, 0), MAX_CH_AMP);
|
||||||
@ -151,9 +153,9 @@ void GB_apu_run_internal(GB_gameboy_t *gb, unsigned int n_cycles, GB_sample_t *s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->apu.sweep_step_timer += 1.0 / APU_FREQUENCY;
|
gb->apu.sweep_step_timer += 1;
|
||||||
if (gb->apu.sweep_step_timer >= 1.0 / 128) {
|
if (gb->apu.sweep_step_timer >= APU_FREQUENCY / 128) {
|
||||||
gb->apu.sweep_step_timer -= 1.0 / 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) {
|
if (gb->apu.wave_channels[0].sweep_steps && !--gb->apu.wave_channels[0].cur_sweep_steps) {
|
||||||
|
|
||||||
// Convert back to GB format
|
// Convert back to GB format
|
||||||
@ -181,13 +183,8 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
static bool should_log_overflow = true;
|
static bool should_log_overflow = true;
|
||||||
while (gb->audio_copy_in_progress);
|
while (gb->audio_copy_in_progress);
|
||||||
double ticks_per_sample = (double) CPU_FREQUENCY / gb->sample_rate;
|
double ticks_per_sample = (double) CPU_FREQUENCY / gb->sample_rate;
|
||||||
GB_sample_t sample = {0, };
|
|
||||||
|
|
||||||
if (gb->apu.apu_cycles >= CPU_FREQUENCY / APU_FREQUENCY) {
|
GB_apu_run_internal(gb);
|
||||||
GB_apu_run_internal(gb, gb->apu.apu_cycles / (CPU_FREQUENCY / APU_FREQUENCY), &sample);
|
|
||||||
gb->apu.apu_cycles %= (CPU_FREQUENCY / APU_FREQUENCY);
|
|
||||||
gb->audio_buffer[gb->audio_position] = sample;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gb->apu_sample_cycles > ticks_per_sample) {
|
if (gb->apu_sample_cycles > ticks_per_sample) {
|
||||||
gb->apu_sample_cycles -= ticks_per_sample;
|
gb->apu_sample_cycles -= ticks_per_sample;
|
||||||
@ -200,7 +197,7 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->audio_position++;
|
GB_apu_get_samples_and_update_pcm_regs(gb, &gb->audio_buffer[gb->audio_position++]);
|
||||||
should_log_overflow = true;
|
should_log_overflow = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,7 +228,7 @@ 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 = 0.5;
|
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 = 1.0;
|
gb->apu.left_volume = 1.0;
|
||||||
gb->apu.right_volume = 1.0;
|
gb->apu.right_volume = 1.0;
|
||||||
@ -281,7 +278,7 @@ 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)
|
||||||
{
|
{
|
||||||
static const double duties[] = {0.125, 0.25, 0.5, 0.75};
|
static const uint8_t duties[] = {1, 2, 4, 6}; /* Values are in 1/8 */
|
||||||
uint8_t channel = 0;
|
uint8_t channel = 0;
|
||||||
|
|
||||||
if (!gb->apu.global_enable && reg != GB_IO_NR52) {
|
if (!gb->apu.global_enable && reg != GB_IO_NR52) {
|
||||||
@ -326,7 +323,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||||||
case GB_IO_NR21:
|
case GB_IO_NR21:
|
||||||
case GB_IO_NR41:
|
case GB_IO_NR41:
|
||||||
gb->apu.wave_channels[channel].duty = duties[value >> 6];
|
gb->apu.wave_channels[channel].duty = duties[value >> 6];
|
||||||
gb->apu.wave_channels[channel].sound_length = (64 - (value & 0x3F)) / 256.0;
|
gb->apu.wave_channels[channel].sound_length = (64 - (value & 0x3F)) * (APU_FREQUENCY / 256);
|
||||||
if (gb->apu.wave_channels[channel].sound_length == 0) {
|
if (gb->apu.wave_channels[channel].sound_length == 0) {
|
||||||
gb->apu.wave_channels[channel].is_playing = false;
|
gb->apu.wave_channels[channel].is_playing = false;
|
||||||
}
|
}
|
||||||
@ -374,7 +371,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||||||
gb->apu.wave_channels[2].is_playing &= gb->apu.wave_enable;
|
gb->apu.wave_channels[2].is_playing &= gb->apu.wave_enable;
|
||||||
break;
|
break;
|
||||||
case GB_IO_NR31:
|
case GB_IO_NR31:
|
||||||
gb->apu.wave_channels[2].sound_length = (256 - value) / 256.0;
|
gb->apu.wave_channels[2].sound_length = (256 - value) * (APU_FREQUENCY / 256);
|
||||||
if (gb->apu.wave_channels[2].sound_length == 0) {
|
if (gb->apu.wave_channels[2].sound_length == 0) {
|
||||||
gb->apu.wave_channels[2].is_playing = false;
|
gb->apu.wave_channels[2].is_playing = false;
|
||||||
}
|
}
|
||||||
|
18
Core/apu.h
18
Core/apu.h
@ -19,21 +19,22 @@ typedef struct
|
|||||||
} GB_sample_t;
|
} GB_sample_t;
|
||||||
|
|
||||||
/* Not all used on all channels */
|
/* Not all used on all channels */
|
||||||
|
/* All lengths are in APU ticks */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint64_t phase;
|
uint32_t phase;
|
||||||
uint32_t wave_length;
|
uint32_t wave_length;
|
||||||
GB_aligned_double duty;
|
int32_t sound_length;
|
||||||
GB_aligned_double sound_length; /* In seconds */
|
bool stop_on_length;
|
||||||
|
uint8_t duty;
|
||||||
int16_t amplitude;
|
int16_t amplitude;
|
||||||
int16_t start_amplitude;
|
int16_t start_amplitude;
|
||||||
bool stop_on_length;
|
|
||||||
uint8_t envelope_steps;
|
uint8_t envelope_steps;
|
||||||
uint8_t cur_envelope_steps;
|
uint8_t cur_envelope_steps;
|
||||||
signed int envelope_direction;
|
int8_t envelope_direction;
|
||||||
uint8_t sweep_steps;
|
uint8_t sweep_steps;
|
||||||
uint8_t cur_sweep_steps;
|
uint8_t cur_sweep_steps;
|
||||||
signed int sweep_direction;
|
int8_t sweep_direction;
|
||||||
uint8_t sweep_shift;
|
uint8_t sweep_shift;
|
||||||
bool is_playing;
|
bool is_playing;
|
||||||
uint16_t NRX3_X4_temp;
|
uint16_t NRX3_X4_temp;
|
||||||
@ -45,8 +46,8 @@ typedef struct
|
|||||||
{
|
{
|
||||||
uint8_t apu_cycles;
|
uint8_t apu_cycles;
|
||||||
bool global_enable;
|
bool global_enable;
|
||||||
GB_aligned_double envelope_step_timer; /* In seconds */
|
uint32_t envelope_step_timer;
|
||||||
GB_aligned_double sweep_step_timer; /* In seconds */
|
uint32_t sweep_step_timer;
|
||||||
int8_t wave_form[32];
|
int8_t wave_form[32];
|
||||||
uint8_t wave_shift;
|
uint8_t wave_shift;
|
||||||
bool wave_enable;
|
bool wave_enable;
|
||||||
@ -60,6 +61,7 @@ typedef struct
|
|||||||
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);
|
||||||
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_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);
|
||||||
|
|
||||||
|
@ -139,12 +139,14 @@ 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_34:
|
||||||
|
GB_apu_get_samples_and_update_pcm_regs(gb, &gb->audio_buffer[gb->audio_position]);
|
||||||
case GB_IO_HDMA1:
|
case GB_IO_HDMA1:
|
||||||
case GB_IO_HDMA2:
|
case GB_IO_HDMA2:
|
||||||
case GB_IO_HDMA3:
|
case GB_IO_HDMA3:
|
||||||
case GB_IO_HDMA4:
|
case GB_IO_HDMA4:
|
||||||
case GB_IO_PCM_12:
|
|
||||||
case GB_IO_PCM_34:
|
|
||||||
if (!gb->is_cgb) {
|
if (!gb->is_cgb) {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user