From a313d3910d88e9ca5933d65316510c54a02455d2 Mon Sep 17 00:00:00 2001 From: goyuken Date: Wed, 9 May 2012 20:21:23 +0000 Subject: [PATCH] Add sound output to sound card while dumping is occurring. Uses a proxy ISoundProvider (DualSound.cs) to guarantee emulation sound core and dumping routine are unaffected, while sending "best effort" sound to system output. Doesn't sound great, but simple and functional. --- BizHawk.Emulation/BizHawk.Emulation.csproj | 1 + .../Sound/Utilities/DualSound.cs | 109 ++++++++++++++++++ BizHawk.MultiClient/MainForm.cs | 33 ++++-- 3 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 BizHawk.Emulation/Sound/Utilities/DualSound.cs diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index 6f8f40ce3a..26ef44b106 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -295,6 +295,7 @@ + diff --git a/BizHawk.Emulation/Sound/Utilities/DualSound.cs b/BizHawk.Emulation/Sound/Utilities/DualSound.cs new file mode 100644 index 0000000000..a84c5bccc4 --- /dev/null +++ b/BizHawk.Emulation/Sound/Utilities/DualSound.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Sound.Utilities +{ + /// + /// provides pass-through sound for the dumping tool to use, while making a "best effort" + /// to have something available for audio output + /// + public class DualSound : ISoundProvider + { + /// + /// implementation of a "slave" ISoundProvider that recieves best effort audio + /// + class SecondPin : ISoundProvider + { + /// + /// the source to draw from + /// + DualSound master; + + public SecondPin(DualSound master) + { + this.master = master; + } + + public void GetSamples(short[] samples) + { + int i; + for (i = 0; i < Math.Min(samples.Length, master.ringbuffer.Count); i++) + samples[i] = master.ringbuffer.Dequeue(); + for (; i < samples.Length; i++) + // underflow + samples[i] = 0; + } + + public void DiscardSamples() + { + master.ringbuffer.Clear(); + } + + public int MaxVolume + { + // ignored + get; + set; + } + } + + /// + /// original input source + /// + ISoundProvider input; + + /// + /// threshold at which to discard samples + /// + int killsize; + + /// + /// storage of samples waiting to go to second pin + /// + Queue ringbuffer; + + /// + /// get the slave pin + /// + public ISoundProvider secondpin + { + get; + private set; + } + + /// + /// default constructor + /// + /// the ISoundProvider to use as input + /// how many sample pairs to save for the second pin + public DualSound(ISoundProvider input, int buffsize) + { + this.input = input; + killsize = buffsize * 2; + ringbuffer = new Queue(killsize); + secondpin = new SecondPin(this); + } + + public void GetSamples(short[] samples) + { + input.GetSamples(samples); + if (ringbuffer.Count >= killsize) + ringbuffer.Clear(); + foreach (var sample in samples) + ringbuffer.Enqueue(sample); + } + + public void DiscardSamples() + { + throw new Exception("Dumpers should never discard samples!"); + } + + public int MaxVolume + { + get { return input.MaxVolume; } + set { input.MaxVolume = value; } + } + } +} diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs index 3f6a49c586..0e46b545d2 100644 --- a/BizHawk.MultiClient/MainForm.cs +++ b/BizHawk.MultiClient/MainForm.cs @@ -33,6 +33,11 @@ namespace BizHawk.MultiClient //avi/wav state IVideoWriter CurrAviWriter = null; + /// + /// an audio proxy used for dumping + /// + Emulation.Sound.Utilities.DualSound DumpProxy = null; + //runloop control bool exit; @@ -1874,8 +1879,9 @@ namespace BizHawk.MultiClient //TODO - this will stray over time! have AviWriter keep an accumulation! int samples = (int)(44100 / Global.Emulator.CoreOutputComm.VsyncRate); short[] temp = new short[samples * 2]; - Global.Emulator.SoundProvider.GetSamples(temp); - genSound = false; + //Global.Emulator.SoundProvider.GetSamples(temp); + DumpProxy.GetSamples(temp); + //genSound = false; CurrAviWriter.AddFrame(Global.Emulator.VideoProvider); CurrAviWriter.AddSamples(temp); @@ -1891,10 +1897,16 @@ namespace BizHawk.MultiClient PressFrameAdvance = false; } - if (genSound) - Global.Sound.UpdateSound(Global.Emulator.SoundProvider); - else - Global.Sound.UpdateSound(NullSound.SilenceProvider); + if (genSound) + { + // change audio path if dumping is occuring + if (DumpProxy != null) + Global.Sound.UpdateSound(DumpProxy.secondpin); + else + Global.Sound.UpdateSound(Global.Emulator.SoundProvider); + } + else + Global.Sound.UpdateSound(NullSound.SilenceProvider); } /// @@ -2704,16 +2716,23 @@ namespace BizHawk.MultiClient aw.Dispose(); throw; } + // buffersize here is entirely guess + DumpProxy = new Emulation.Sound.Utilities.DualSound(Global.Emulator.SoundProvider, 8192); } public void StopAVI() { - if (CurrAviWriter == null) return; + if (CurrAviWriter == null) + { + DumpProxy = null; + return; + } CurrAviWriter.CloseFile(); CurrAviWriter = null; Global.OSD.AddMessage("AVI capture stopped"); AVIStatusLabel.Image = BizHawk.MultiClient.Properties.Resources.Blank; AVIStatusLabel.ToolTipText = ""; + DumpProxy = null; // return to normal sound output } private void SwapBackupSavestate(string path)