GBHawk: Starting to get sound

This commit is contained in:
alyosha-tas 2017-11-14 17:52:35 -05:00
parent 574a78ee86
commit 67d4f0bb7d
2 changed files with 217 additions and 14 deletions

View File

@ -17,6 +17,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
1, 0, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0};
public static int[] DIVISOR = new int[] {8, 16, 32, 48, 64, 80, 96, 112};
public const int NR10 = 0;
public const int NR11 = 1;
public const int NR12 = 2;
@ -76,6 +79,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public int internal_cntr;
public byte duty_counter;
public bool enable;
public int noise_LFSR;
public int wave_counter;
public int vol_per;
// channel non-states
public int output;
@ -104,6 +110,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick;
public int master_audio_clock;
public byte ReadReg(int addr)
{
byte ret = 0;
@ -192,6 +200,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
SQ1.len_en = (value & 0x40) > 0;
SQ1.frq &= 0xFF;
SQ1.frq |= (ushort)((value & 7) << 8);
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;
}
break;
case 0xFF16: // NR21 (sound length / wave pattern duty %)
Audio_Regs[NR21] = value;
@ -215,6 +232,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
SQ2.len_en = (value & 0x40) > 0;
SQ2.frq &= 0xFF;
SQ2.frq |= (ushort)((value & 7) << 8);
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;
}
break;
case 0xFF1A: // NR30 (on/off)
Audio_Regs[NR30] = value;
@ -239,6 +265,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
WAVE.len_en = (value & 0x40) > 0;
WAVE.frq &= 0xFF;
WAVE.frq |= (ushort)((value & 7) << 8);
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;
}
break;
case 0xFF20: // NR41 (length)
Audio_Regs[NR41] = value;
@ -258,8 +294,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
break;
case 0xFF23: // NR44 (trigger)
Audio_Regs[NR44] = value;
WAVE.trigger = (value & 0x80) > 0;
WAVE.len_en = (value & 0x40) > 0;
NOISE.trigger = (value & 0x80) > 0;
NOISE.len_en = (value & 0x40) > 0;
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;
}
break;
case 0xFF24: // NR50 (ctrl)
Audio_Regs[NR50] = value;
@ -390,12 +437,110 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
// calculate wave output
if (WAVE.enable)
{
WAVE.internal_cntr++;
if (WAVE.internal_cntr == (2048 - WAVE.frq) * 2)
{
WAVE.wave_counter++;
WAVE.wave_counter &= 0x1F;
byte sample = Wave_RAM[WAVE.wave_counter >> 1];
if ((WAVE.wave_counter & 1) == 0)
{
sample = (byte)(sample >> 4);
}
if (WAVE.vol_code == 0)
{
sample = (byte)((sample & 0xF) >> 4);
}
else if (WAVE.vol_code == 1)
{
sample = (byte)(sample & 0xF);
}
else if (WAVE.vol_code == 2)
{
sample = (byte)((sample & 0xF) >> 1);
}
else
{
sample = (byte)((sample & 0xF) >> 2);
}
WAVE.output = sample;
if (!WAVE.DAC_pow)
{
WAVE.output = 0;
}
}
}
// calculate noise output
if (NOISE.enable)
{
NOISE.internal_cntr++;
if (NOISE.internal_cntr == DIVISOR[NOISE.div_code] << NOISE.shift)
{
NOISE.internal_cntr = 0;
int bit_lfsr = (NOISE.noise_LFSR & 1) ^ ((NOISE.noise_LFSR & 2) >> 1);
NOISE.noise_LFSR = (NOISE.noise_LFSR >> 1) & 0x3FFF;
NOISE.noise_LFSR |= (bit_lfsr << 14);
if (NOISE.wdth_md)
{
NOISE.noise_LFSR = NOISE.noise_LFSR & 0x7FBF;
NOISE.noise_LFSR |= (bit_lfsr << 6);
}
NOISE.output = NOISE.noise_LFSR & 1;
NOISE.output *= NOISE.volume_state;
}
}
// add up components to each channel
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_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);
// send out an actual sample every 94 cycles
master_audio_clock++;
if (master_audio_clock == 94)
{
master_audio_clock = 0;
if (AudioClocks < 1500)
{
AudioSamples[AudioClocks] = (short)L_final;
/*
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;
AudioClocks++;
}
}
// frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over)
sequencer_tick++;
@ -460,10 +605,46 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// clock the volume envelope
if (sequencer_vol == 0)
{
if (SQ1.per > 0) { if (SQ1.env_add) { SQ1.volume_state++; } else { SQ1.volume_state--; } }
if (SQ2.per > 0) { if (SQ2.env_add) { SQ2.volume_state++; } else { SQ2.volume_state--; } }
if (WAVE.per > 0) { if (WAVE.env_add) { WAVE.volume_state++; } else { WAVE.volume_state--; } }
if (NOISE.per > 0) { if (NOISE.env_add) { NOISE.volume_state++; } else { NOISE.volume_state--; } }
if (SQ1.per > 0)
{
SQ1.vol_per++;
if (SQ1.vol_per == SQ1.per)
{
SQ1.vol_per = 0;
if (SQ1.env_add) { SQ1.volume_state++; }
else { SQ1.volume_state--; }
}
}
if (SQ2.per > 0)
{
SQ2.vol_per++;
if (SQ2.vol_per == SQ2.per)
{
SQ2.vol_per = 0;
if (SQ2.env_add) { SQ2.volume_state++; }
else { SQ2.volume_state--; }
}
}
if (WAVE.per > 0)
{
WAVE.vol_per++;
if (WAVE.vol_per == WAVE.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--; }
}
}
}
}
}
@ -479,8 +660,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
sequencer_vol = 0;
sequencer_swp = 0;
sequencer_tick = 0;
master_audio_clock = 0;
}
public void reset()
public void Reset()
{
Wave_RAM = new byte[16];
@ -490,8 +672,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
SQ2 = new AUD_Object();
WAVE = new AUD_Object();
NOISE = new AUD_Object();
AUD_CTRL = new CTRL_Object();
AudioClocks = 0;
master_audio_clock = 0;
sequencer_len = 0;
sequencer_swp = 0;
sequencer_vol = 0;
sequencer_tick = 0;
}
public void SyncState(Serializer ser)
@ -505,6 +694,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
ser.Sync("WAVE.length_counter", ref WAVE.length_counter);
ser.Sync("NOISE.length_counter", ref NOISE.length_counter);
ser.Sync("sequencer_len", ref sequencer_len);
ser.Sync("sequencer_vol", ref sequencer_vol);
ser.Sync("sequencer_swp", ref sequencer_swp);
ser.Sync("sequencer_tick", ref sequencer_tick);
ser.Sync("master_audio_clock", ref master_audio_clock);
// get derived state
if (ser.IsReader)
{
@ -602,6 +798,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public int _spf;
public int AudioClocks;
public short[] AudioSamples = new short[1500];
public void SetSyncMode(SyncSoundMode mode)
{
@ -615,10 +812,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public void GetSamplesSync(out short[] samples, out int nsamp)
{
short[] ret = new short[_spf * 2];
nsamp = _spf;
GetSamples(ret);
samples = ret;
nsamp = AudioClocks / 2;
short[] temp_samp = new short[AudioClocks];
for (int i = 0; i < AudioClocks; i++)
{
temp_samp[i] = AudioSamples[i];
}
Console.WriteLine(AudioClocks);
samples = temp_samp;
AudioClocks = 0;
}
public void GetSamplesAsync(short[] samples)
@ -631,8 +835,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
AudioClocks = 0;
}
// Exposing this as GetSamplesAsync would allow this to provide async sound
// However, it does nothing special for async sound so I don't see a point
private void GetSamples(short[] samples)
{

View File

@ -128,6 +128,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
Register_Reset();
timer.Reset();
ppu.Reset();
audio.Reset();
cpu.SetCallbacks(ReadMemory, ReadMemory, ReadMemory, WriteMemory);