GBHawk: Audio work

This commit is contained in:
alyosha-tas 2017-11-15 12:17:58 -05:00
parent 67d4f0bb7d
commit 27601653e5
1 changed files with 376 additions and 335 deletions

View File

@ -52,61 +52,54 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public byte[] Wave_RAM = new byte [16];
struct AUD_Object
{
// channel controls
public byte swp_period;
public bool negate;
public byte shift;
public byte duty;
public byte length;
public byte st_vol;
public bool env_add;
public byte per;
public int frq;
public bool trigger;
public bool len_en;
public bool DAC_pow;
public byte vol_code;
public byte clk_shft;
public bool wdth_md;
public byte div_code;
// channel states
public byte length_counter;
public byte volume_state;
public int frq_shadow;
public int internal_cntr;
public byte duty_counter;
public bool enable;
public int noise_LFSR;
public int wave_counter;
public int vol_per;
// Audio Variables
// derived
public bool WAVE_DAC_pow;
public bool NOISE_wdth_md;
public bool SQ1_negate;
public bool SQ1_trigger, SQ2_trigger, WAVE_trigger, NOISE_trigger;
public bool SQ1_len_en, SQ2_len_en, WAVE_len_en, NOISE_len_en;
public bool SQ1_env_add, SQ2_env_add, NOISE_env_add;
public byte WAVE_vol_code;
public byte NOISE_clk_shft;
public byte NOISE_div_code;
public byte SQ1_shift;
public byte SQ1_duty, SQ2_duty;
public byte SQ1_st_vol, SQ2_st_vol, NOISE_st_vol;
public byte SQ1_per, SQ2_per, NOISE_per;
public byte SQ1_swp_prd;
public int SQ1_frq, SQ2_frq, WAVE_frq;
public ushort SQ1_length, SQ2_length, WAVE_length, NOISE_length;
// state
public bool SQ1_enable, SQ2_enable, WAVE_enable, NOISE_enable;
public byte SQ1_vol_state, SQ2_vol_state, NOISE_vol_state;
public byte SQ1_duty_cntr, SQ2_duty_cntr;
public byte WAVE_wave_cntr;
public int SQ1_frq_shadow;
public int SQ1_intl_cntr, SQ2_intl_cntr, WAVE_intl_cntr, NOISE_intl_cntr;
public int SQ1_vol_per, SQ2_vol_per, NOISE_vol_per;
public int SQ1_intl_swp_cnt;
public int NOISE_LFSR;
public ushort SQ1_len_cntr, SQ2_len_cntr, WAVE_len_cntr, NOISE_len_cntr;
// computed
public int SQ1_output, SQ2_output, WAVE_output, NOISE_output;
// channel non-states
public int output;
}
// Contol Variables
public bool AUD_CTRL_vin_L_en;
public bool AUD_CTRL_vin_R_en;
public bool AUD_CTRL_sq1_L_en;
public bool AUD_CTRL_sq2_L_en;
public bool AUD_CTRL_wave_L_en;
public bool AUD_CTRL_noise_L_en;
public bool AUD_CTRL_sq1_R_en;
public bool AUD_CTRL_sq2_R_en;
public bool AUD_CTRL_wave_R_en;
public bool AUD_CTRL_noise_R_en;
public bool AUD_CTRL_power;
public byte AUD_CTRL_vol_L;
public byte AUD_CTRL_vol_R;
struct CTRL_Object
{
public bool vin_L_en;
public bool vin_R_en;
public byte vol_L;
public byte vol_R;
public bool sq1_L_en;
public bool sq2_L_en;
public bool wave_L_en;
public bool noise_L_en;
public bool sq1_R_en;
public bool sq2_R_en;
public bool wave_R_en;
public bool noise_R_en;
public bool power;
}
AUD_Object SQ1, SQ2, WAVE, NOISE;
CTRL_Object AUD_CTRL;
public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick;
@ -138,7 +131,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (trigger)
case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (ctrl)
case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (ctrl)
case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); break; // NR52 (ctrl)
case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); Console.WriteLine(Audio_Regs[NR52] & 0xF); break; // NR52 (ctrl)
// wave ram table
case 0xFF30:
@ -168,170 +161,184 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public void WriteReg(int addr, byte value)
{
// while power is on, everything is writable
if (AUD_CTRL.power)
if (AUD_CTRL_power)
{
switch (addr)
{
case 0xFF10: // NR10 (sweep)
Audio_Regs[NR10] = value;
SQ1.swp_period = (byte)((value & 0x70) >> 4);
SQ1.negate = (value & 8) > 0;
SQ1.shift = (byte)(value & 7);
SQ1_swp_prd = (byte)((value & 0x70) >> 4);
SQ1_negate = (value & 8) > 0;
SQ1_shift = (byte)(value & 7);
if (SQ1_swp_prd == 0) { SQ1_swp_prd = 8; }
break;
case 0xFF11: // NR11 (sound length / wave pattern duty %)
Audio_Regs[NR11] = value;
SQ1.duty = (byte)((value & 0xC0) >> 6);
SQ1.length = (byte)(64 - value & 0x3F);
SQ1_duty = (byte)((value & 0xC0) >> 6);
SQ1_length = (ushort)(64 - value & 0x3F);
SQ1_len_cntr = SQ1_length;
break;
case 0xFF12: // NR12 (envelope)
Audio_Regs[NR12] = value;
SQ1.st_vol = (byte)((value & 0xF0) >> 4);
SQ1.env_add = (value & 8) > 0;
SQ1.per = (byte)(value & 7);
SQ1_st_vol = (byte)((value & 0xF0) >> 4);
SQ1_env_add = (value & 8) > 0;
SQ1_per = (byte)(value & 7);
if (SQ1_per == 0) { SQ1_per = 8; }
break;
case 0xFF13: // NR13 (freq low)
Audio_Regs[NR13] = value;
SQ1.frq &= 0x700;
SQ1.frq |= value;
SQ1_frq &= 0x700;
SQ1_frq |= value;
break;
case 0xFF14: // NR14 (freq hi)
Audio_Regs[NR14] = value;
SQ1.trigger = (value & 0x80) > 0;
SQ1.len_en = (value & 0x40) > 0;
SQ1.frq &= 0xFF;
SQ1.frq |= (ushort)((value & 7) << 8);
SQ1_trigger = (value & 0x80) > 0;
SQ1_len_en = (value & 0x40) > 0;
SQ1_frq &= 0xFF;
SQ1_frq |= (ushort)((value & 7) << 8);
if (SQ1.trigger)
if (SQ1_trigger)
{
SQ1.enable = true;
if (SQ1.length_counter == 0) { SQ1.length_counter = 64; }
SQ1.internal_cntr = 0;
SQ1.volume_state = SQ1.st_vol;
SQ1.vol_per = 0;
SQ1_enable = true;
Audio_Regs[NR52] |= 1;
if (SQ1_len_cntr == 0) { SQ1_len_cntr = 64; }
SQ1_intl_cntr = 0;
SQ1_vol_state = SQ1_st_vol;
SQ1_vol_per = 0;
SQ1_frq_shadow = SQ1_frq;
if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; }
}
break;
case 0xFF16: // NR21 (sound length / wave pattern duty %)
Audio_Regs[NR21] = value;
SQ2.duty = (byte)((value & 0xC0) >> 6);
SQ2.length = (byte)(64 - value & 0x3F);
SQ2_duty = (byte)((value & 0xC0) >> 6);
SQ2_length = (ushort)(64 - value & 0x3F);
SQ2_len_cntr = SQ2_length;
break;
case 0xFF17: // NR22 (envelope)
Audio_Regs[NR22] = value;
SQ2.st_vol = (byte)((value & 0xF0) >> 4);
SQ2.env_add = (value & 8) > 0;
SQ2.per = (byte)(value & 7);
SQ2_st_vol = (byte)((value & 0xF0) >> 4);
SQ2_env_add = (value & 8) > 0;
SQ2_per = (byte)(value & 7);
//if (SQ2_per == 0) { SQ2_per = 8; }
break;
case 0xFF18: // NR23 (freq low)
Audio_Regs[NR23] = value;
SQ2.frq &= 0x700;
SQ2.frq |= value;
SQ2_frq &= 0x700;
SQ2_frq |= value;
break;
case 0xFF19: // NR24 (freq hi)
Audio_Regs[NR24] = value;
SQ2.trigger = (value & 0x80) > 0;
SQ2.len_en = (value & 0x40) > 0;
SQ2.frq &= 0xFF;
SQ2.frq |= (ushort)((value & 7) << 8);
SQ2_trigger = (value & 0x80) > 0;
SQ2_len_en = (value & 0x40) > 0;
SQ2_frq &= 0xFF;
SQ2_frq |= (ushort)((value & 7) << 8);
if (SQ2.trigger)
if (SQ2_trigger)
{
SQ2.enable = true;
if (SQ2.length_counter == 0) { SQ2.length_counter = 64; }
SQ2.internal_cntr = 0;
SQ2.volume_state = SQ2.st_vol;
SQ2.vol_per = 0;
SQ2_enable = true;
Audio_Regs[NR52] |= 2;
if (SQ2_len_cntr == 0) { SQ2_len_cntr = 64; }
SQ2_intl_cntr = 0;
SQ2_vol_state = SQ2_st_vol;
SQ2_vol_per = 0;
if ((SQ2_vol_state == 0) && !SQ2_env_add) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; }
}
break;
case 0xFF1A: // NR30 (on/off)
Audio_Regs[NR30] = value;
WAVE.DAC_pow = (value & 0x80) > 0;
WAVE_DAC_pow = (value & 0x80) > 0;
break;
case 0xFF1B: // NR31 (length)
Audio_Regs[NR31] = value;
WAVE.length = (byte)(256 - value);
WAVE_length = (ushort)(256 - value);
WAVE_len_cntr = WAVE_length;
break;
case 0xFF1C: // NR32 (level output)
Audio_Regs[NR32] = value;
WAVE.vol_code = (byte)((value & 0x60) >> 5);
WAVE_vol_code = (byte)((value & 0x60) >> 5);
break;
case 0xFF1D: // NR33 (freq low)
Audio_Regs[NR33] = value;
WAVE.frq &= 0x700;
WAVE.frq |= value;
WAVE_frq &= 0x700;
WAVE_frq |= value;
break;
case 0xFF1E: // NR34 (freq hi)
Audio_Regs[NR34] = value;
WAVE.trigger = (value & 0x80) > 0;
WAVE.len_en = (value & 0x40) > 0;
WAVE.frq &= 0xFF;
WAVE.frq |= (ushort)((value & 7) << 8);
WAVE_trigger = (value & 0x80) > 0;
WAVE_len_en = (value & 0x40) > 0;
WAVE_frq &= 0xFF;
WAVE_frq |= (ushort)((value & 7) << 8);
if (WAVE.trigger)
if (WAVE_trigger)
{
WAVE.enable = true;
if (WAVE.length_counter == 0) { WAVE.length_counter = 64; }
WAVE.internal_cntr = 0;
WAVE.volume_state = WAVE.st_vol;
WAVE.vol_per = 0;
WAVE.wave_counter = 0;
WAVE_enable = true;
Audio_Regs[NR52] |= 4;
if (WAVE_len_cntr == 0) { WAVE_len_cntr = 256; }
WAVE_intl_cntr = 0;
WAVE_wave_cntr = 0;
if (!WAVE_DAC_pow) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; }
}
break;
case 0xFF20: // NR41 (length)
Audio_Regs[NR41] = value;
NOISE.length = (byte)(64 - value & 0x3F);
NOISE_length = (ushort)(64 - value & 0x3F);
NOISE_len_cntr = NOISE_length;
break;
case 0xFF21: // NR42 (envelope)
Audio_Regs[NR42] = value;
NOISE.st_vol = (byte)((value & 0xF0) >> 4);
NOISE.env_add = (value & 8) > 0;
NOISE.per = (byte)(value & 7);
NOISE_st_vol = (byte)((value & 0xF0) >> 4);
NOISE_env_add = (value & 8) > 0;
NOISE_per = (byte)(value & 7);
//if (NOISE_per == 0) { NOISE_per = 8; }
break;
case 0xFF22: // NR43 (shift)
Audio_Regs[NR43] = value;
NOISE.clk_shft = (byte)((value & 0xF0) >> 4);
NOISE.wdth_md = (value & 8) > 0;
NOISE.div_code = (byte)(value & 7);
NOISE_clk_shft = (byte)((value & 0xF0) >> 4);
NOISE_wdth_md = (value & 8) > 0;
NOISE_div_code = (byte)(value & 7);
break;
case 0xFF23: // NR44 (trigger)
Audio_Regs[NR44] = value;
NOISE.trigger = (value & 0x80) > 0;
NOISE.len_en = (value & 0x40) > 0;
NOISE_trigger = (value & 0x80) > 0;
NOISE_len_en = (value & 0x40) > 0;
if (NOISE.trigger)
if (NOISE_trigger)
{
NOISE.enable = true;
if (NOISE.length_counter == 0) { NOISE.length_counter = 64; }
NOISE.internal_cntr = 0;
NOISE.volume_state = NOISE.st_vol;
NOISE.vol_per = 0;
NOISE.wave_counter = 0;
NOISE.noise_LFSR = 0x7FFF;
NOISE_enable = true;
Audio_Regs[NR52] |= 8;
if (NOISE_len_cntr == 0) { NOISE_len_cntr = 64; }
NOISE_intl_cntr = 0;
NOISE_vol_state = NOISE_st_vol;
NOISE_vol_per = 0;
NOISE_LFSR = 0x7FFF;
if ((NOISE_vol_state == 0) && !NOISE_env_add) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; }
}
break;
case 0xFF24: // NR50 (ctrl)
Audio_Regs[NR50] = value;
AUD_CTRL.vin_L_en = (value & 0x80) > 0;
AUD_CTRL.vol_L = (byte)((value & 0x70) >> 4);
AUD_CTRL.vin_R_en = (value & 8) > 0;
AUD_CTRL.vol_R = (byte)(value & 7);
AUD_CTRL_vin_L_en = (value & 0x80) > 0;
AUD_CTRL_vol_L = (byte)((value & 0x70) >> 4);
AUD_CTRL_vin_R_en = (value & 8) > 0;
AUD_CTRL_vol_R = (byte)(value & 7);
break;
case 0xFF25: // NR51 (ctrl)
Audio_Regs[NR51] = value;
AUD_CTRL.noise_L_en = (value & 0x80) > 0;
AUD_CTRL.wave_L_en = (value & 0x40) > 0;
AUD_CTRL.sq2_L_en = (value & 0x20) > 0;
AUD_CTRL.sq1_L_en = (value & 0x10) > 0;
AUD_CTRL.noise_R_en = (value & 8) > 0;
AUD_CTRL.wave_R_en = (value & 4) > 0;
AUD_CTRL.sq2_R_en = (value & 2) > 0;
AUD_CTRL.sq1_R_en = (value & 1) > 0;
AUD_CTRL_noise_L_en = (value & 0x80) > 0;
AUD_CTRL_wave_L_en = (value & 0x40) > 0;
AUD_CTRL_sq2_L_en = (value & 0x20) > 0;
AUD_CTRL_sq1_L_en = (value & 0x10) > 0;
AUD_CTRL_noise_R_en = (value & 8) > 0;
AUD_CTRL_wave_R_en = (value & 4) > 0;
AUD_CTRL_sq2_R_en = (value & 2) > 0;
AUD_CTRL_sq1_R_en = (value & 1) > 0;
break;
case 0xFF26: // NR52 (ctrl)
Audio_Regs[NR52] &= 0x7F;
Audio_Regs[NR52] |= (byte)(value & 0x80);
AUD_CTRL.power = (value & 0x80) > 0;
AUD_CTRL_power = (value & 0x80) > 0;
if (!AUD_CTRL.power)
if (!AUD_CTRL_power)
{
power_off();
}
@ -364,21 +371,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
switch (addr)
{
case 0xFF11: // NR11 (sound length / wave pattern duty %)
SQ1.length = (byte)(64 - value & 0x3F);
SQ1_length = (ushort)(64 - value & 0x3F);
break;
case 0xFF16: // NR21 (sound length / wave pattern duty %)
SQ2.length = (byte)(64 - value & 0x3F);
SQ2_length = (ushort)(64 - value & 0x3F);
break;
case 0xFF1B: // NR31 (length)
WAVE.length = (byte)(256 - value);
WAVE_length = (ushort)(256 - value);
break;
case 0xFF20: // NR41 (length)
NOISE.length = (byte)(64 - value & 0x3F);
NOISE_length = (ushort)(64 - value & 0x3F);
break;
case 0xFF26: // NR52 (ctrl)
Audio_Regs[NR52] &= 0x7F;
Audio_Regs[NR52] |= (byte)(value & 0x80);
AUD_CTRL.power = (value & 0x80) > 0;
AUD_CTRL_power = (value & 0x80) > 0;
break;
// wave ram table
@ -407,60 +414,61 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public void tick()
{
// calculate square1's output
if (SQ1.enable)
if (SQ1_enable)
{
SQ1.internal_cntr++;
if (SQ1.internal_cntr == (2048 - SQ1.frq_shadow) * 4)
SQ1_intl_cntr++;
if (SQ1_intl_cntr >= (2048 - SQ1_frq_shadow) * 4)
{
SQ1.internal_cntr = 0;
SQ1.duty_counter++;
SQ1.duty_counter &= 7;
SQ1_intl_cntr = 0;
SQ1_duty_cntr++;
SQ1_duty_cntr &= 7;
SQ1.output = DUTY_CYCLES[SQ1.duty * 8 + SQ1.duty_counter];
SQ1.output *= SQ1.volume_state;
SQ1_output = DUTY_CYCLES[SQ1_duty * 8 + SQ1_duty_cntr];
SQ1_output *= SQ1_vol_state;
}
}
// calculate square2's output
if (SQ2.enable)
if (SQ2_enable)
{
SQ2.internal_cntr++;
if (SQ2.internal_cntr == (2048 - SQ2.frq) * 4)
SQ2_intl_cntr++;
if (SQ2_intl_cntr >= (2048 - SQ2_frq) * 4)
{
SQ2.internal_cntr = 0;
SQ2.duty_counter++;
SQ2.duty_counter &= 7;
SQ2_intl_cntr = 0;
SQ2_duty_cntr++;
SQ2_duty_cntr &= 7;
SQ2.output = DUTY_CYCLES[SQ2.duty * 8 + SQ2.duty_counter];
SQ2.output *= SQ2.volume_state;
SQ2_output = DUTY_CYCLES[SQ2_duty * 8 + SQ2_duty_cntr];
SQ2_output *= SQ2_vol_state;
}
}
// calculate wave output
if (WAVE.enable)
if (WAVE_enable)
{
WAVE.internal_cntr++;
if (WAVE.internal_cntr == (2048 - WAVE.frq) * 2)
WAVE_intl_cntr++;
if (WAVE_intl_cntr >= (2048 - WAVE_frq) * 2)
{
WAVE.wave_counter++;
WAVE.wave_counter &= 0x1F;
WAVE_intl_cntr = 0;
WAVE_wave_cntr++;
WAVE_wave_cntr &= 0x1F;
byte sample = Wave_RAM[WAVE.wave_counter >> 1];
byte sample = Wave_RAM[WAVE_wave_cntr >> 1];
if ((WAVE.wave_counter & 1) == 0)
if ((WAVE_wave_cntr & 1) == 0)
{
sample = (byte)(sample >> 4);
}
if (WAVE.vol_code == 0)
if (WAVE_vol_code == 0)
{
sample = (byte)((sample & 0xF) >> 4);
}
else if (WAVE.vol_code == 1)
else if (WAVE_vol_code == 1)
{
sample = (byte)(sample & 0xF);
}
else if (WAVE.vol_code == 2)
else if (WAVE_vol_code == 2)
{
sample = (byte)((sample & 0xF) >> 1);
}
@ -469,36 +477,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
sample = (byte)((sample & 0xF) >> 2);
}
WAVE.output = sample;
WAVE_output = sample;
if (!WAVE.DAC_pow)
if (!WAVE_DAC_pow)
{
WAVE.output = 0;
WAVE_output = 0;
}
}
}
// calculate noise output
if (NOISE.enable)
if (NOISE_enable)
{
NOISE.internal_cntr++;
if (NOISE.internal_cntr == DIVISOR[NOISE.div_code] << NOISE.shift)
NOISE_intl_cntr++;
if (NOISE_intl_cntr >= (DIVISOR[NOISE_div_code] << NOISE_clk_shft))
{
NOISE.internal_cntr = 0;
int bit_lfsr = (NOISE.noise_LFSR & 1) ^ ((NOISE.noise_LFSR & 2) >> 1);
NOISE_intl_cntr = 0;
int bit_lfsr = (NOISE_LFSR & 1) ^ ((NOISE_LFSR & 2) >> 1);
NOISE.noise_LFSR = (NOISE.noise_LFSR >> 1) & 0x3FFF;
NOISE.noise_LFSR |= (bit_lfsr << 14);
NOISE_LFSR = (NOISE_LFSR >> 1) & 0x3FFF;
NOISE_LFSR |= (bit_lfsr << 14);
if (NOISE.wdth_md)
if (NOISE_wdth_md)
{
NOISE.noise_LFSR = NOISE.noise_LFSR & 0x7FBF;
NOISE.noise_LFSR |= (bit_lfsr << 6);
NOISE_LFSR = NOISE_LFSR & 0x7FBF;
NOISE_LFSR |= (bit_lfsr << 6);
}
NOISE.output = NOISE.noise_LFSR & 1;
NOISE.output *= NOISE.volume_state;
NOISE_output = NOISE_LFSR & 1;
NOISE_output *= NOISE_vol_state;
}
}
@ -506,18 +514,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
int L_final = 0;
int R_final = 0;
if (AUD_CTRL.sq1_L_en) { L_final += SQ1.output; }
if (AUD_CTRL.sq2_L_en) { L_final += SQ2.output; }
if (AUD_CTRL.wave_L_en) { L_final += WAVE.output; }
if (AUD_CTRL.noise_L_en) { L_final += NOISE.output; }
if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; }
if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; }
if (AUD_CTRL_wave_L_en) { L_final += WAVE_output; }
if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; }
if (AUD_CTRL.sq1_R_en) { R_final += SQ1.output; }
if (AUD_CTRL.sq2_R_en) { R_final += SQ2.output; }
if (AUD_CTRL.wave_R_en) { R_final += WAVE.output; }
if (AUD_CTRL.noise_R_en) { R_final += NOISE.output; }
if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; }
if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; }
if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; }
if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; }
L_final *= (AUD_CTRL.vol_L + 1);
R_final *= (AUD_CTRL.vol_R + 1);
L_final *= (AUD_CTRL_vol_L + 1);
R_final *= (AUD_CTRL_vol_R + 1);
// send out an actual sample every 94 cycles
master_audio_clock++;
@ -526,18 +534,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
master_audio_clock = 0;
if (AudioClocks < 1500)
{
AudioSamples[AudioClocks] = (short)L_final;
AudioSamples[AudioClocks] = (short)(L_final * 4);
/*
Console.Write(SQ1.output);
Console.Write(" ");
Console.Write(SQ2.output);
Console.Write(" ");
Console.Write(WAVE.output);
Console.Write(" ");
Console.WriteLine(NOISE.output);
Console_Write(SQ1_output);
Console_Write(" ");
Console_Write(SQ2_output);
Console_Write(" ");
Console_Write(WAVE_output);
Console_Write(" ");
Console_WriteLine(NOISE_output);
*/
AudioClocks++;
AudioSamples[AudioClocks] = (short)R_final;
AudioSamples[AudioClocks] = (short)(R_final * 4);
AudioClocks++;
}
}
@ -556,47 +564,71 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// clock the lengths
if ((sequencer_len == 1) || (sequencer_len == 3) || (sequencer_len == 5) || (sequencer_len == 7))
{
if (SQ1.len_en && SQ1.length_counter > 0) { SQ1.length_counter--; if (SQ1.length_counter == 0) { SQ1.enable = false; } }
if (SQ2.len_en && SQ2.length_counter > 0) { SQ2.length_counter--; if (SQ2.length_counter == 0) { SQ2.enable = false; } }
if (WAVE.len_en && WAVE.length_counter > 0) { WAVE.length_counter--; if (WAVE.length_counter == 0) { WAVE.enable = false; } }
if (NOISE.len_en && NOISE.length_counter > 0) { NOISE.length_counter--; if (NOISE.length_counter == 0) { NOISE.enable = false; } }
if (SQ1_len_en && SQ1_len_cntr > 0)
{
SQ1_len_cntr--;
if (SQ1_len_cntr == 0) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; }
}
if (SQ2_len_en && SQ2_len_cntr > 0)
{
SQ2_len_cntr--;
if (SQ2_len_cntr == 0) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; }
}
if (WAVE_len_en && WAVE_len_cntr > 0)
{
WAVE_len_cntr--;
if (WAVE_len_cntr == 0) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; }
}
if (NOISE_len_en && NOISE_len_cntr > 0)
{
NOISE_len_cntr--;
if (NOISE_len_cntr == 0) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; }
}
}
// clock the sweep
if ((sequencer_swp == 3) || (sequencer_swp == 7))
{
if (((SQ1.swp_period > 0) || (SQ1.shift > 0)) && (SQ1.swp_period > 0))
SQ1_intl_swp_cnt++;
if (SQ1_intl_swp_cnt >= SQ1_swp_prd)
{
int shadow_frq = SQ1.frq_shadow;
shadow_frq = shadow_frq >> SQ1.shift;
if (SQ1.negate) { shadow_frq = -shadow_frq; }
shadow_frq += SQ1.frq_shadow;
SQ1_intl_swp_cnt = 0;
if ((SQ1_swp_prd > 0) && (SQ1_shift > 0))
{
int shadow_frq = SQ1_frq_shadow;
shadow_frq = shadow_frq >> SQ1_shift;
if (SQ1_negate) { shadow_frq = -shadow_frq; }
shadow_frq += SQ1_frq_shadow;
// disable channel if overflow
if ((uint)shadow_frq > 2047)
{
SQ1.enable = false;
SQ1_enable = false;
Audio_Regs[NR52] &= 0xFE;
}
else
{
shadow_frq &= 0x7FF;
SQ1.frq = shadow_frq;
SQ1.frq_shadow = shadow_frq;
SQ1_frq = shadow_frq;
SQ1_frq_shadow = shadow_frq;
// note that we also write back the frequency to the actual register
Audio_Regs[NR13] = (byte)(SQ1.frq & 0xFF);
Audio_Regs[NR13] = (byte)(SQ1_frq & 0xFF);
Audio_Regs[NR14] &= 0xF8;
Audio_Regs[NR14] |= (byte)((SQ1.frq >> 8) & 7);
Audio_Regs[NR14] |= (byte)((SQ1_frq >> 8) & 7);
// after writing, we repeat the process and do another overflow check
shadow_frq = SQ1.frq_shadow;
shadow_frq = shadow_frq >> SQ1.shift;
if (SQ1.negate) { shadow_frq = -shadow_frq; }
shadow_frq += SQ1.frq_shadow;
shadow_frq = SQ1_frq_shadow;
shadow_frq = shadow_frq >> SQ1_shift;
if (SQ1_negate) { shadow_frq = -shadow_frq; }
shadow_frq += SQ1_frq_shadow;
if ((uint)shadow_frq > 2047)
{
SQ1.enable = false;
SQ1_enable = false;
Audio_Regs[NR52] &= 0xFE;
}
}
}
}
@ -605,44 +637,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// clock the volume envelope
if (sequencer_vol == 0)
{
if (SQ1.per > 0)
if (SQ1_per > 0)
{
SQ1.vol_per++;
if (SQ1.vol_per == SQ1.per)
SQ1_vol_per++;
if (SQ1_vol_per >= SQ1_per)
{
SQ1.vol_per = 0;
if (SQ1.env_add) { SQ1.volume_state++; }
else { SQ1.volume_state--; }
SQ1_vol_per = 0;
if (SQ1_env_add && (SQ1_vol_state < 15)) { SQ1_vol_state++; }
else if (SQ1_vol_state > 0) { SQ1_vol_state--; }
}
}
if (SQ2.per > 0)
if (SQ2_per > 0)
{
SQ2.vol_per++;
if (SQ2.vol_per == SQ2.per)
SQ2_vol_per++;
if (SQ2_vol_per >= SQ2_per)
{
SQ2.vol_per = 0;
if (SQ2.env_add) { SQ2.volume_state++; }
else { SQ2.volume_state--; }
SQ2_vol_per = 0;
if (SQ2_env_add && (SQ2_vol_state < 15)) { SQ2_vol_state++; }
else if (SQ2_vol_state > 0) { SQ2_vol_state--; }
}
}
if (WAVE.per > 0)
if (NOISE_per > 0)
{
WAVE.vol_per++;
if (WAVE.vol_per == WAVE.per)
NOISE_vol_per++;
if (NOISE_vol_per >= NOISE_per)
{
WAVE.vol_per = 0;
if (WAVE.env_add) { WAVE.volume_state++; }
else { WAVE.volume_state--; }
}
}
if (NOISE.per > 0)
{
NOISE.vol_per++;
if (NOISE.vol_per == NOISE.per)
{
NOISE.vol_per = 0;
if (NOISE.env_add) { NOISE.volume_state++; }
else { NOISE.volume_state--; }
NOISE_vol_per = 0;
if (NOISE_env_add && (NOISE_vol_state < 15)) { NOISE_vol_state++; }
else if (NOISE_vol_state > 0) { NOISE_vol_state--; }
}
}
}
@ -651,10 +673,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public void power_off()
{
for (int i = 0; i < 20; i++)
for (int i = 0; i < 21; i++)
{
Audio_Regs[i] = 0;
}
SQ1_enable = false;
SQ2_enable = false;
WAVE_enable = false;
NOISE_enable = false;
sequencer_len = 0;
sequencer_vol = 0;
@ -668,12 +694,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
Audio_Regs = new byte[21];
SQ1 = new AUD_Object();
SQ2 = new AUD_Object();
WAVE = new AUD_Object();
NOISE = new AUD_Object();
AUD_CTRL = new CTRL_Object();
AudioClocks = 0;
master_audio_clock = 0;
@ -688,11 +708,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
ser.Sync("Audio_Regs", ref Audio_Regs, false);
ser.Sync("Wave_Ram", ref Wave_RAM, false);
// some aspecta of the channel states are not derived from the Regs
ser.Sync("SQ1.length_counter", ref SQ1.length_counter);
ser.Sync("SQ2.length_counter", ref SQ2.length_counter);
ser.Sync("WAVE.length_counter", ref WAVE.length_counter);
ser.Sync("NOISE.length_counter", ref NOISE.length_counter);
// save state variables
ser.Sync("SQ1_length_counter", ref SQ1_len_cntr);
ser.Sync("SQ2_length_counter", ref SQ2_len_cntr);
ser.Sync("WAVE_length_counter", ref WAVE_len_cntr);
ser.Sync("NOISE_length_counter", ref NOISE_len_cntr);
ser.Sync("SQ1_enable", ref SQ1_enable);
ser.Sync("SQ2_enable", ref SQ2_enable);
ser.Sync("WAVE_enable", ref WAVE_enable);
ser.Sync("NOISE_enable", ref NOISE_enable);
ser.Sync("SQ1_vol_state", ref SQ1_vol_state);
ser.Sync("SQ2_vol_state", ref SQ2_vol_state);
ser.Sync("NOISE_vol_state", ref NOISE_vol_state);
ser.Sync("SQ1_duty_cntr", ref SQ1_duty_cntr);
ser.Sync("SQ2_duty_cntr", ref SQ2_duty_cntr);
ser.Sync("WAVE_wave_cntr", ref WAVE_wave_cntr);
ser.Sync("SQ1_frq_shadow", ref SQ1_frq_shadow);
ser.Sync("SQ1_intl_cntr", ref SQ1_intl_cntr);
ser.Sync("SQ2_intl_cntr", ref SQ2_intl_cntr);
ser.Sync("WAVE_intl_cntr", ref WAVE_intl_cntr);
ser.Sync("NOISE_intl_cntr", ref NOISE_intl_cntr);
ser.Sync("SQ1_vol_per", ref SQ1_vol_per);
ser.Sync("SQ2_vol_per", ref SQ2_vol_per);
ser.Sync("NOISE_vol_per", ref NOISE_vol_per);
ser.Sync("SQ1_intl_swp_cnt", ref SQ1_intl_swp_cnt);
ser.Sync("NOISE_LFSR", ref NOISE_LFSR);
ser.Sync("SQ1_len_cntr", ref SQ1_len_cntr);
ser.Sync("SQ2_len_cntr", ref SQ2_len_cntr);
ser.Sync("WAVE_len_cntr", ref WAVE_len_cntr);
ser.Sync("NOISE_len_cntr", ref NOISE_len_cntr);
ser.Sync("sequencer_len", ref sequencer_len);
ser.Sync("sequencer_vol", ref sequencer_vol);
@ -706,97 +751,93 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
sync_channels();
}
}
public void sync_channels()
{
SQ1.swp_period = (byte)((Audio_Regs[NR10] & 0x70) >> 4);
SQ1.negate = (Audio_Regs[NR10] & 8) > 0;
SQ1.shift = (byte)(Audio_Regs[NR10] & 7);
SQ1_swp_prd = (byte)((Audio_Regs[NR10] & 0x70) >> 4);
SQ1_negate = (Audio_Regs[NR10] & 8) > 0;
SQ1_shift = (byte)(Audio_Regs[NR10] & 7);
SQ1.duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6);
SQ1.length = (byte)(64 - Audio_Regs[NR11] & 0x3F);
SQ1_duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6);
SQ1_length = (ushort)(64 - Audio_Regs[NR11] & 0x3F);
SQ1.st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4);
SQ1.env_add = (Audio_Regs[NR12] & 8) > 0;
SQ1.per = (byte)(Audio_Regs[NR12] & 7);
SQ1_st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4);
SQ1_env_add = (Audio_Regs[NR12] & 8) > 0;
SQ1_per = (byte)(Audio_Regs[NR12] & 7);
SQ1.frq &= 0x700;
SQ1.frq |= Audio_Regs[NR13];
SQ1_frq &= 0x700;
SQ1_frq |= Audio_Regs[NR13];
SQ1.trigger = (Audio_Regs[NR14] & 0x80) > 0;
SQ1.len_en = (Audio_Regs[NR14] & 0x40) > 0;
SQ1.frq &= 0xFF;
SQ1.frq |= (ushort)((Audio_Regs[NR14] & 7) << 8);
SQ1_trigger = (Audio_Regs[NR14] & 0x80) > 0;
SQ1_len_en = (Audio_Regs[NR14] & 0x40) > 0;
SQ1_frq &= 0xFF;
SQ1_frq |= (ushort)((Audio_Regs[NR14] & 7) << 8);
SQ2.duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6);
SQ2.length = (byte)(64 - Audio_Regs[NR21] & 0x3F);
SQ2_duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6);
SQ2_length = (ushort)(64 - Audio_Regs[NR21] & 0x3F);
SQ2.st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4);
SQ2.env_add = (Audio_Regs[NR22] & 8) > 0;
SQ2.per = (byte)(Audio_Regs[NR22] & 7);
SQ2_st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4);
SQ2_env_add = (Audio_Regs[NR22] & 8) > 0;
SQ2_per = (byte)(Audio_Regs[NR22] & 7);
SQ2.frq &= 0x700;
SQ2.frq |= Audio_Regs[NR23];
SQ2_frq &= 0x700;
SQ2_frq |= Audio_Regs[NR23];
SQ2.trigger = (Audio_Regs[NR24] & 0x80) > 0;
SQ2.len_en = (Audio_Regs[NR24] & 0x40) > 0;
SQ2.frq &= 0xFF;
SQ2.frq |= (ushort)((Audio_Regs[NR24] & 7) << 8);
SQ2_trigger = (Audio_Regs[NR24] & 0x80) > 0;
SQ2_len_en = (Audio_Regs[NR24] & 0x40) > 0;
SQ2_frq &= 0xFF;
SQ2_frq |= (ushort)((Audio_Regs[NR24] & 7) << 8);
WAVE.DAC_pow = (Audio_Regs[NR30] & 0x80) > 0;
WAVE_DAC_pow = (Audio_Regs[NR30] & 0x80) > 0;
WAVE.length = (byte)(256 - Audio_Regs[NR31]);
WAVE_length = (ushort)(256 - Audio_Regs[NR31]);
WAVE.vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5);
WAVE_vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5);
WAVE.frq &= 0x700;
WAVE.frq |= Audio_Regs[NR33];
WAVE_frq &= 0x700;
WAVE_frq |= Audio_Regs[NR33];
WAVE.trigger = (Audio_Regs[NR34] & 0x80) > 0;
WAVE.len_en = (Audio_Regs[NR34] & 0x40) > 0;
WAVE.frq &= 0xFF;
WAVE.frq |= (ushort)((Audio_Regs[NR34] & 7) << 8);
WAVE_trigger = (Audio_Regs[NR34] & 0x80) > 0;
WAVE_len_en = (Audio_Regs[NR34] & 0x40) > 0;
WAVE_frq &= 0xFF;
WAVE_frq |= (ushort)((Audio_Regs[NR34] & 7) << 8);
NOISE.length = (byte)(64 - Audio_Regs[NR41] & 0x3F);
NOISE_length = (ushort)(64 - Audio_Regs[NR41] & 0x3F);
NOISE.st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4);
NOISE.env_add = (Audio_Regs[NR42] & 8) > 0;
NOISE.per = (byte)(Audio_Regs[NR42] & 7);
NOISE_st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4);
NOISE_env_add = (Audio_Regs[NR42] & 8) > 0;
NOISE_per = (byte)(Audio_Regs[NR42] & 7);
NOISE.clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4);
NOISE.wdth_md = (Audio_Regs[NR43] & 8) > 0;
NOISE.div_code = (byte)(Audio_Regs[NR43] & 7);
NOISE_clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4);
NOISE_wdth_md = (Audio_Regs[NR43] & 8) > 0;
NOISE_div_code = (byte)(Audio_Regs[NR43] & 7);
WAVE.trigger = (Audio_Regs[NR44] & 0x80) > 0;
WAVE.len_en = (Audio_Regs[NR44] & 0x40) > 0;
WAVE_trigger = (Audio_Regs[NR44] & 0x80) > 0;
WAVE_len_en = (Audio_Regs[NR44] & 0x40) > 0;
AUD_CTRL.vin_L_en = (Audio_Regs[NR50] & 0x80) > 0;
AUD_CTRL.vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4);
AUD_CTRL.vin_R_en = (Audio_Regs[NR50] & 8) > 0;
AUD_CTRL.vol_R = (byte)(Audio_Regs[NR50] & 7);
AUD_CTRL_vin_L_en = (Audio_Regs[NR50] & 0x80) > 0;
AUD_CTRL_vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4);
AUD_CTRL_vin_R_en = (Audio_Regs[NR50] & 8) > 0;
AUD_CTRL_vol_R = (byte)(Audio_Regs[NR50] & 7);
AUD_CTRL.noise_L_en = (Audio_Regs[NR51] & 0x80) > 0;
AUD_CTRL.wave_L_en = (Audio_Regs[NR51] & 0x40) > 0;
AUD_CTRL.sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0;
AUD_CTRL.sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0;
AUD_CTRL.noise_R_en = (Audio_Regs[NR51] & 8) > 0;
AUD_CTRL.wave_R_en = (Audio_Regs[NR51] & 4) > 0;
AUD_CTRL.sq2_R_en = (Audio_Regs[NR51] & 2) > 0;
AUD_CTRL.sq1_R_en = (Audio_Regs[NR51] & 1) > 0;
AUD_CTRL_noise_L_en = (Audio_Regs[NR51] & 0x80) > 0;
AUD_CTRL_wave_L_en = (Audio_Regs[NR51] & 0x40) > 0;
AUD_CTRL_sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0;
AUD_CTRL_sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0;
AUD_CTRL_noise_R_en = (Audio_Regs[NR51] & 8) > 0;
AUD_CTRL_wave_R_en = (Audio_Regs[NR51] & 4) > 0;
AUD_CTRL_sq2_R_en = (Audio_Regs[NR51] & 2) > 0;
AUD_CTRL_sq1_R_en = (Audio_Regs[NR51] & 1) > 0;
AUD_CTRL.power = (Audio_Regs[NR51] & 0x80) > 0;
AUD_CTRL_power = (Audio_Regs[NR51] & 0x80) > 0;
}
#region audio
public bool CanProvideAsync => false;
public int _spf;
public int AudioClocks;
public short[] AudioSamples = new short[1500];
@ -804,7 +845,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
if (mode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only Sync mode is supported.");
throw new InvalidOperationException("Only Sync mode is supported_");
}
}
@ -819,7 +860,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
temp_samp[i] = AudioSamples[i];
}
Console.WriteLine(AudioClocks);
samples = temp_samp;
AudioClocks = 0;