From b0e3a332d8f00aedd3f4a9f730a85342ab4ed906 Mon Sep 17 00:00:00 2001 From: goyuken Date: Wed, 5 Sep 2012 21:21:35 +0000 Subject: [PATCH] abstract resampling functionality into IStereoResampler for testing purposes add two example resamplers that implement this: LinearResampler and CubicResampler. both are very basic and sound like shit compared to libresample4j ultimately, a single good resampler with a better license than libresample4j should be selected and used --- BizHawk.Emulation/BizHawk.Emulation.csproj | 3 + .../Consoles/Nintendo/SNES/LibsnesCore.cs | 23 +++-- .../Sound/Utilities/CubicResampler.cs | 87 +++++++++++++++++++ .../Sound/Utilities/IStereoResampler.cs | 27 ++++++ .../Sound/Utilities/LinearResampler.cs | 70 +++++++++++++++ .../Sound/Utilities/Resampler.cs | 50 ++++++----- 6 files changed, 230 insertions(+), 30 deletions(-) create mode 100644 BizHawk.Emulation/Sound/Utilities/CubicResampler.cs create mode 100644 BizHawk.Emulation/Sound/Utilities/IStereoResampler.cs create mode 100644 BizHawk.Emulation/Sound/Utilities/LinearResampler.cs diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index 78c6dcb950..f2e8380d3e 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -355,9 +355,12 @@ + + + diff --git a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs index 8ab5ebb941..c6db050764 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs @@ -173,6 +173,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES _gc_snes_audio_sample = GCHandle.Alloc(soundcb); BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_audio_sample(soundcb); + // start up audio resampler + resampler.StartSession(resamplingfactor); + //strip header if ((romData.Length & 0x7FFF) == 512) { @@ -437,38 +440,44 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES /// total number of samples (left and right combined) in the InBuffer before we ask for resampling const int resamplechunk = 1000; /// actual sampling factor used - const double sfactor = 44100.0 / 32040.5; + const double resamplingfactor = 44100.0 / 32040.5; - Sound.Utilities.BizhawkResampler resampler = new Sound.Utilities.BizhawkResampler(false, sfactor, sfactor); + Sound.Utilities.IStereoResampler resampler = new Sound.Utilities.BizhawkResampler(false); + //Sound.Utilities.IStereoResampler resampler = new Sound.Utilities.CubicResampler(); + //Sound.Utilities.IStereoResampler resampler = new Sound.Utilities.LinearResampler(); Sound.MetaspuSoundProvider metaspu = new Sound.MetaspuSoundProvider(Sound.ESynchMethod.ESynchMethod_Z); void snes_audio_sample(ushort left, ushort right) { + AudioInBuffer.Enqueue((short)left); AudioInBuffer.Enqueue((short)right); + /* try { // fixme: i get all sorts of crashes if i do the resampling in here. what? - /* + if (AudioInBuffer.Count >= resamplechunk) { - resampler.process(sfactor, false, AudioInBuffer, AudioOutBuffer); + resampler.ResampleChunk(AudioInBuffer, AudioOutBuffer, false); // drain into the metaspu immediately // we could skip this step and drain directly by changing SampleBuffers while (AudioOutBuffer.Count > 0) metaspu.buffer.enqueue_sample(AudioOutBuffer.Dequeue(), AudioOutBuffer.Dequeue()); } - */ + } catch (Exception e) { System.Windows.Forms.MessageBox.Show(e.ToString()); AudioOutBuffer.Clear(); } - + */ + + } @@ -480,7 +489,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES if (true) { - resampler.process(sfactor, false, AudioInBuffer, AudioOutBuffer); + resampler.ResampleChunk(AudioInBuffer, AudioOutBuffer, false); // drain into the metaspu immediately // we could skip this step and drain directly by changing SampleBuffers implementation diff --git a/BizHawk.Emulation/Sound/Utilities/CubicResampler.cs b/BizHawk.Emulation/Sound/Utilities/CubicResampler.cs new file mode 100644 index 0000000000..0e99066b9e --- /dev/null +++ b/BizHawk.Emulation/Sound/Utilities/CubicResampler.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Sound.Utilities +{ + /// + /// a simple cubic interpolation resampler. no lowpass. original code + /// + public class CubicResampler : IStereoResampler + { + int[] data = new int[8]; + + double mu; + /// input rate / output rate + double ratio; + + public CubicResampler() + { + } + + public void StartSession(double ratio) + { + this.ratio = 1.0 / ratio; + mu = 0.0; + for (int i = 0; i < data.Length; i++) + data[i] = 0; + } + + public void ResampleChunk(Queue input, Queue output, bool finish) + { + while (true) + { + while (mu >= 1.0 && input.Count >= 2) + { + mu -= 1.0; + for (int i = 0; i < 6; i++) + data[i] = data[i + 2]; + data[6] = input.Dequeue(); + data[7] = input.Dequeue(); + } + if (mu >= 1.0) + return; + + double mu2 = mu * mu; + double mu3 = mu2 * mu2; + + int l0 = data[6] - data[4] - data[0] + data[2]; + int l1 = data[0] - data[2] - l0; + int l2 = data[4] - data[0]; + int l3 = data[2]; + + int r0 = data[7] - data[5] - data[1] + data[3]; + int r1 = data[1] - data[3] - r0; + int r2 = data[5] - data[1]; + int r3 = data[3]; + + double ls = l0 * mu3 + l1 * mu2 + l2 * mu + l3; + double rs = r0 * mu3 + r1 * mu2 + r2 * mu + r3; + + short l, r; + + if (ls > 32767.0) + l = 32767; + else if (ls < -32768.0) + l = -32768; + else + l = (short)ls; + + if (rs > 32767.0) + r = 32767; + else if (ls < -32768.0) + r = -32768; + else + r = (short)ls; + + output.Enqueue(l); + output.Enqueue(r); + + mu += ratio; + + } + + } + } +} diff --git a/BizHawk.Emulation/Sound/Utilities/IStereoResampler.cs b/BizHawk.Emulation/Sound/Utilities/IStereoResampler.cs new file mode 100644 index 0000000000..31f6971bd5 --- /dev/null +++ b/BizHawk.Emulation/Sound/Utilities/IStereoResampler.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Sound.Utilities +{ + /// + /// describes an audio resampler that works with stereo streams of shorts (interleaved) + /// + public interface IStereoResampler + { + /// + /// start a resampling session, with the given conversion rate + /// + /// outrate / inrate + void StartSession(double ratio); + + /// + /// process any available input + /// + /// input samples. all might not be consumed unless finish == true + /// where to put output samples. + /// if true, consume all input and end session + void ResampleChunk(Queue input, Queue output, bool finish); + } +} diff --git a/BizHawk.Emulation/Sound/Utilities/LinearResampler.cs b/BizHawk.Emulation/Sound/Utilities/LinearResampler.cs new file mode 100644 index 0000000000..a0f2c6bf63 --- /dev/null +++ b/BizHawk.Emulation/Sound/Utilities/LinearResampler.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Sound.Utilities +{ + // a simple linear resampler + public class LinearResampler : IStereoResampler + { + short[] data = new short[4]; + + double mu; + /// input rate / output rate + double ratio; + + public LinearResampler() + { + } + + public void StartSession(double ratio) + { + this.ratio = 1.0 / ratio; + mu = 0.0; + for (int i = 0; i < data.Length; i++) + data[i] = 0; + } + + public void ResampleChunk(Queue input, Queue output, bool finish) + { + while (true) + { + while (mu >= 1.0 && input.Count >= 2) + { + mu -= 1.0; + data[0] = data[2]; + data[1] = data[3]; + data[2] = input.Dequeue(); + data[3] = input.Dequeue(); + } + if (mu >= 1.0) + return; + + double ls = data[0] * (1.0 - mu) + data[1] * mu; + double rs = data[1] * (1.0 - mu) + data[3] * mu; + + short l, r; + + if (ls > 32767.0) + l = 32767; + else if (ls < -32768.0) + l = -32768; + else + l = (short)ls; + + if (rs > 32767.0) + r = 32767; + else if (ls < -32768.0) + r = -32768; + else + r = (short)ls; + + output.Enqueue(l); + output.Enqueue(r); + + mu += ratio; + } + } + } +} diff --git a/BizHawk.Emulation/Sound/Utilities/Resampler.cs b/BizHawk.Emulation/Sound/Utilities/Resampler.cs index f1709f7a56..346eaa6a62 100644 --- a/BizHawk.Emulation/Sound/Utilities/Resampler.cs +++ b/BizHawk.Emulation/Sound/Utilities/Resampler.cs @@ -507,37 +507,44 @@ namespace BizHawk.Emulation.Sound.Utilities /// implements a pair of Resamplers that work off of interleaved stereo buffers of shorts /// this is what's used inside most of bizhawk /// - public class BizhawkResampler + public class BizhawkResampler : IStereoResampler { - private Resampler left; - private Resampler right; + bool highQuality; + double ratio; - public BizhawkResampler(bool highQuality, double minFactor, double maxFactor) + public void StartSession(double ratio) { - left = new Resampler(highQuality, minFactor, maxFactor); - right = new Resampler(highQuality, minFactor, maxFactor); + this.ratio = ratio; + left = new Resampler(highQuality, ratio, ratio); + right = new Resampler(highQuality, ratio, ratio); } - - - /// - /// resample some audio. - /// - /// outrate / inrate - /// true to finalize (empty buffers and finish) - /// input samples, as interleaved stereo shorts. in general, all won't be used unless lastbatch = true - /// recieves output samples, as interleaved stereo shorts - /// true if processing finished; only possible with lastbatch = true - public bool process(double factor, bool lastBatch, Queue input, Queue output) + public void ResampleChunk(Queue input, Queue output, bool finish) { LeftBuffer lb = new LeftBuffer(input, output); SampleBuffers rb = lb.GetRightBuffer(); - bool doneleft = left.process(factor, lb, lastBatch); - bool doneright = right.process(factor, rb, lastBatch); + bool doneleft = left.process(ratio, lb, finish); + bool doneright = right.process(ratio, rb, finish); - return doneleft && doneright; + if (finish) + { + left = null; + right = null; + } + } + + private Resampler left; + private Resampler right; + + /// + /// + /// + /// true for high quality resampling + public BizhawkResampler(bool highQuality) + { + this.highQuality = highQuality; } /// @@ -635,10 +642,7 @@ namespace BizHawk.Emulation.Sound.Utilities } } } - } - - } }