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
This commit is contained in:
goyuken 2012-09-05 21:21:35 +00:00
parent 0430ec91c8
commit b0e3a332d8
6 changed files with 230 additions and 30 deletions

View File

@ -355,9 +355,12 @@
<Compile Include="Properties\svnrev.cs" />
<Compile Include="QuickCollections.cs" />
<Compile Include="Sound\CDAudio.cs" />
<Compile Include="Sound\Utilities\CubicResampler.cs" />
<Compile Include="Sound\Utilities\DualSound.cs" />
<Compile Include="Sound\Utilities\Equalizer.cs" />
<Compile Include="Sound\Utilities\FilterKit.cs" />
<Compile Include="Sound\Utilities\IStereoResampler.cs" />
<Compile Include="Sound\Utilities\LinearResampler.cs" />
<Compile Include="Sound\Utilities\Resampler.cs" />
<Compile Include="Sound\Utilities\SampleBuffers.cs" />
<Compile Include="Sound\VRC6.cs" />

View File

@ -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
/// <summary>total number of samples (left and right combined) in the InBuffer before we ask for resampling</summary>
const int resamplechunk = 1000;
/// <summary>actual sampling factor used</summary>
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

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Sound.Utilities
{
/// <summary>
/// a simple cubic interpolation resampler. no lowpass. original code
/// </summary>
public class CubicResampler : IStereoResampler
{
int[] data = new int[8];
double mu;
/// <summary>input rate / output rate</summary>
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<short> input, Queue<short> 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;
}
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Sound.Utilities
{
/// <summary>
/// describes an audio resampler that works with stereo streams of shorts (interleaved)
/// </summary>
public interface IStereoResampler
{
/// <summary>
/// start a resampling session, with the given conversion rate
/// </summary>
/// <param name="ratio">outrate / inrate</param>
void StartSession(double ratio);
/// <summary>
/// process any available input
/// </summary>
/// <param name="input">input samples. all might not be consumed unless finish == true</param>
/// <param name="output">where to put output samples.</param>
/// <param name="finish">if true, consume all input and end session</param>
void ResampleChunk(Queue<short> input, Queue<short> output, bool finish);
}
}

View File

@ -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;
/// <summary>input rate / output rate</summary>
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<short> input, Queue<short> 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;
}
}
}
}

View File

@ -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
/// </summary>
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);
}
/// <summary>
/// resample some audio.
/// </summary>
/// <param name="factor">outrate / inrate</param>
/// <param name="lastBatch">true to finalize (empty buffers and finish)</param>
/// <param name="input">input samples, as interleaved stereo shorts. in general, all won't be used unless <code>lastbatch = true</code></param>
/// <param name="output">recieves output samples, as interleaved stereo shorts</param>
/// <returns>true if processing finished; only possible with <code>lastbatch = true</code></returns>
public bool process(double factor, bool lastBatch, Queue<short> input, Queue<short> output)
public void ResampleChunk(Queue<short> input, Queue<short> 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;
/// <summary>
///
/// </summary>
/// <param name="highQuality">true for high quality resampling</param>
public BizhawkResampler(bool highQuality)
{
this.highQuality = highQuality;
}
/// <summary>
@ -635,10 +642,7 @@ namespace BizHawk.Emulation.Sound.Utilities
}
}
}
}
}
}