diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs index 632f6acd96..a2b85e9a34 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -727,11 +727,11 @@ namespace BizHawk.Emulation.Consoles.GB /// /// sample pairs before resampling /// - internal short[] soundbuff = new short[(35112 + 2064) * 2]; + short[] soundbuff = new short[(35112 + 2064) * 2]; /// /// how many sample pairs are in soundbuff /// - internal int soundbuffcontains = 0; + int soundbuffcontains = 0; Sound.Utilities.SpeexResampler resampler; Sound.Utilities.DCFilter dcfilter; diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/GambatteLink.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/GambatteLink.cs index 7e6a465d83..97bc8a5d0a 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/GambatteLink.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/GambatteLink.cs @@ -8,8 +8,15 @@ namespace BizHawk.Emulation.Consoles.GB { public class GambatteLink : IEmulator, IVideoProvider, ISyncSoundProvider { + bool disposed = false; + Gameboy L; Gameboy R; + // counter to ensure we do 35112 samples per frame + int overflowL = 0; + int overflowR = 0; + + const int SampPerFrame = 35112; Consoles.Nintendo.SNES.LibsnesCore.SnesSaveController LCont = new Nintendo.SNES.LibsnesCore.SnesSaveController(Gameboy.GbController); Consoles.Nintendo.SNES.LibsnesCore.SnesSaveController RCont = new Nintendo.SNES.LibsnesCore.SnesSaveController(Gameboy.GbController); @@ -38,6 +45,11 @@ namespace BizHawk.Emulation.Consoles.GB Frame = 0; LagCount = 0; IsLagFrame = false; + + blip_left = new Sound.Utilities.BlipBuffer(1024); + blip_right = new Sound.Utilities.BlipBuffer(1024); + blip_left.SetRates(2097152 * 2, 44100); + blip_right.SetRates(2097152 * 2, 44100); } public IVideoProvider VideoProvider { get { return this; } } @@ -83,19 +95,22 @@ namespace BizHawk.Emulation.Consoles.GB { fixed (int* leftvbuff = &VideoBuffer[0]) { + // use pitch to have both cores write to the same video buffer, interleaved int* rightvbuff = leftvbuff + 160; const int pitch = 160 * 2; - fixed (short* leftsbuff = L.soundbuff, rightsbuff = R.soundbuff) + fixed (short* leftsbuff = LeftBuffer, rightsbuff = RightBuffer) { const int step = 32; // could be 1024 for GB - int nL = 0; - int nR = 0; + int nL = overflowL; + int nR = overflowR; - for (int target = step; target <= 35112; target += step) + // slowly step our way through the frame, while continually checking and resolving link cable status + for (int target = 0; target < SampPerFrame;) { + target += step; if (nL < target) { @@ -129,18 +144,21 @@ namespace BizHawk.Emulation.Consoles.GB } } + overflowL = nL - SampPerFrame; + overflowR = nR - SampPerFrame; + if (overflowL < 0 || overflowR < 0) + throw new Exception("Sound problem?"); if (rendersound) { - L.soundbuffcontains = nL; - R.soundbuffcontains = nR; + PrepSound(); } - else - { - L.soundbuffcontains = 0; - R.soundbuffcontains = 0; - } - + // copy extra samples back to beginning + for (int i = 0; i < overflowL * 2; i++) + LeftBuffer[i] = LeftBuffer[i + SampPerFrame * 2]; + for (int i = 0; i < overflowR * 2; i++) + RightBuffer[i] = RightBuffer[i + SampPerFrame * 2]; + } } @@ -234,6 +252,10 @@ namespace BizHawk.Emulation.Consoles.GB writer.Write(IsLagFrame); writer.Write(LagCount); writer.Write(Frame); + writer.Write(overflowL); + writer.Write(overflowR); + writer.Write(LatchL); + writer.Write(LatchR); } public void LoadStateBinary(BinaryReader reader) @@ -244,6 +266,10 @@ namespace BizHawk.Emulation.Consoles.GB IsLagFrame = reader.ReadBoolean(); LagCount = reader.ReadInt32(); Frame = reader.ReadInt32(); + overflowL = reader.ReadInt32(); + overflowR = reader.ReadInt32(); + LatchL = reader.ReadInt32(); + LatchR = reader.ReadInt32(); } public byte[] SaveStateBinary() @@ -271,15 +297,18 @@ namespace BizHawk.Emulation.Consoles.GB public void Dispose() { - if (L != null) + if (!disposed) { L.Dispose(); L = null; - } - if (R != null) - { R.Dispose(); R = null; + blip_left.Dispose(); + blip_left = null; + blip_right.Dispose(); + blip_right = null; + + disposed = true; } } @@ -290,16 +319,66 @@ namespace BizHawk.Emulation.Consoles.GB public int BufferHeight { get { return 144; } } public int BackgroundColor { get { return unchecked((int)0xff000000); } } + // we tried using the left and right buffers and then mixing them together... it was kind of a mess of code, and slow + + Sound.Utilities.BlipBuffer blip_left; + Sound.Utilities.BlipBuffer blip_right; + + + short[] LeftBuffer = new short[(35112 + 2064) * 2]; + short[] RightBuffer = new short[(35112 + 2064) * 2]; + + short[] SampleBuffer = new short[1536]; + int SampleBufferContains = 0; + + int LatchL; + int LatchR; + + void PrepSound() + { + unsafe + { + fixed (short* sl = LeftBuffer, sr = RightBuffer) + { + for (uint i = 0; i < SampPerFrame * 2; i += 2) + { + // gameboy audio output is mono, so ignore one sample + int s = sl[i]; + if (s != LatchL) + { + blip_left.AddDelta(i, s - LatchL); + LatchL = s; + } + s = sr[i]; + if (s != LatchR) + { + blip_right.AddDelta(i, s - LatchR); + LatchR = s; + } + } + + } + } + blip_left.EndFrame(SampPerFrame * 2); + blip_right.EndFrame(SampPerFrame * 2); + int count = blip_left.SamplesAvailable(); + if (count != blip_right.SamplesAvailable()) + throw new Exception("Sound problem?"); + + blip_left.ReadSamplesLeft(SampleBuffer, count); + blip_right.ReadSamplesRight(SampleBuffer, count); + SampleBufferContains = count; + } + public void GetSamples(out short[] samples, out int nsamp) { - // TODO - samples = new short[735 * 2]; - nsamp = 735; + nsamp = SampleBufferContains; + samples = SampleBuffer; } public void DiscardSamples() { - // TODO + SampleBufferContains = 0; } } } diff --git a/BizHawk.Emulation/Sound/Utilities/BlipBuffer.cs b/BizHawk.Emulation/Sound/Utilities/BlipBuffer.cs index 4ab0549e64..404c31a162 100644 --- a/BizHawk.Emulation/Sound/Utilities/BlipBuffer.cs +++ b/BizHawk.Emulation/Sound/Utilities/BlipBuffer.cs @@ -69,6 +69,8 @@ namespace BizHawk.Emulation.Sound.Utilities samples. Returns number of samples actually read. */ [DllImport("blip_buf.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int blip_read_samples(IntPtr context, short[] @out, int count, int stereo); + [DllImport("blip_buf.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern int blip_read_samples(IntPtr context, IntPtr @out, int count, int stereo); /** Frees buffer. No effect if NULL is passed. */ [DllImport("blip_buf.dll", CallingConvention = CallingConvention.Cdecl)] @@ -136,5 +138,24 @@ namespace BizHawk.Emulation.Sound.Utilities throw new ArgumentOutOfRangeException(); return BlipBufDll.blip_read_samples(context, output, count, stereo ? 1 : 0); } + + public int ReadSamplesLeft(short[] output, int count) + { + if (output.Length < count * 2) + throw new ArgumentOutOfRangeException(); + return BlipBufDll.blip_read_samples(context, output, count, 1); + } + + public int ReadSamplesRight(short[] output, int count) + { + if (output.Length < count * 2) + throw new ArgumentOutOfRangeException(); + unsafe + { + fixed (short* s = &output[1]) + return BlipBufDll.blip_read_samples(context, new IntPtr(s), count, 1); + } + } + } }