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:
parent
0430ec91c8
commit
b0e3a332d8
|
@ -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" />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue