cleanup/simplify SpeexResampler

This commit is contained in:
goyuken 2012-09-07 20:12:47 +00:00
parent afa27c1a26
commit 71652b25dc
3 changed files with 120 additions and 73 deletions

View File

@ -219,7 +219,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_audio_sample(soundcb);
// start up audio resampler
resampler.StartSession(resamplingfactor);
InitAudio();
//strip header
if ((romData.Length & 0x7FFF) == 512)
@ -479,56 +479,20 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
#region audio stuff
/// <summary>stores input samples as they come from the libsnes core</summary>
Queue<short> AudioInBuffer = new Queue<short>();
/// <summary>stores samples that have been converted to 44100hz</summary>
Queue<short> AudioOutBuffer = new Queue<short>();
void InitAudio()
{
metaspu = new Sound.MetaspuSoundProvider(Sound.ESynchMethod.ESynchMethod_V);
resampler = new Sound.Utilities.SpeexResampler(6, 64081, 88200, 32041, 44100, new Action<short[], int>(metaspu.buffer.enqueue_samples));
GCHandle _gc_snes_audio_sample;
}
/// <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 resamplingfactor = 44100.0 / 32040.5;
Sound.Utilities.SpeexResampler resampler;
//Sound.Utilities.IStereoResampler resampler = new Sound.Utilities.BizhawkResampler(false);
//Sound.Utilities.IStereoResampler resampler = new Sound.Utilities.SinkResampler(12);
//Sound.Utilities.IStereoResampler resampler = new Sound.Utilities.CubicResampler();
//Sound.Utilities.IStereoResampler resampler = new Sound.Utilities.LinearResampler();
Sound.Utilities.SpeexResampler resampler = new Sound.Utilities.SpeexResampler(6);
Sound.MetaspuSoundProvider metaspu = new Sound.MetaspuSoundProvider(Sound.ESynchMethod.ESynchMethod_V);
Sound.MetaspuSoundProvider metaspu;
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.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();
}
*/
resampler.EnqueueSample((short)left, (short)right);
}
@ -536,26 +500,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public void GetSamples(short[] samples)
{
// do resampling here, instead of when the samples are generated; see "fixme" comment in snes_audio_sample()
if (true)
{
//lock (AudioInBuffer)
//{
resampler.ResampleChunk(AudioInBuffer, AudioOutBuffer, false);
//}
// drain into the metaspu immediately
// we could skip this step and drain directly by changing SampleBuffers implementation
while (AudioOutBuffer.Count > 0)
{
short l = AudioOutBuffer.Dequeue();
short r = AudioOutBuffer.Dequeue();
//metaspu.buffer.enqueue_sample(AudioOutBuffer.Dequeue(), AudioOutBuffer.Dequeue());
metaspu.buffer.enqueue_sample(l, r);
//dbgs.Write(l);
//dbgs.Write(r);
}
}
resampler.Flush();
metaspu.GetSamples(samples);
}

View File

@ -43,6 +43,10 @@ namespace BizHawk.Emulation.Sound
void clear();
//returns the number of samples actually supplied, which may not match the number requested
// ^^ what the hell is that supposed to mean.
// the entire point of an ISynchronzingAudioBuffer
// is to provide exact amounts of output samples,
// even when the input provided varies....
int output_samples(short[] buf, int samples_requested);
};

View File

@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Sound.Utilities
/// <summary>
/// junk wrapper around LibSpeexDSP. quite inefficient. will be replaced
/// </summary>
public class SpeexResampler
public class SpeexResampler : IDisposable
{
static class LibSpeexDSP
{
@ -203,6 +203,8 @@ namespace BizHawk.Emulation.Sound.Utilities
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_get_output_stride(IntPtr st, ref uint stride);
/*these two functions don't exist in our version of the dll
/// <summary>
/// Get the latency in input samples introduced by the resampler.
/// </summary>
@ -219,6 +221,8 @@ namespace BizHawk.Emulation.Sound.Utilities
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int speex_resampler_get_output_latency(IntPtr st);
*/
/// <summary>
/// Make sure that the first samples to go out of the resamplers don't have
/// leading zeros. This is only useful before starting to use a newly created
@ -253,21 +257,108 @@ namespace BizHawk.Emulation.Sound.Utilities
/// <summary>
/// opaque pointer to state
/// </summary>
IntPtr st;
IntPtr st = IntPtr.Zero;
public SpeexResampler(int quality)
/// <summary>
/// function to call to dispatch output
/// </summary>
Action<short[], int> drainer;
short[] inbuf = new short[512];
short[] outbuf;
/// <summary>
/// in buffer position in samples (not sample pairs)
/// </summary>
int inbufpos = 0;
/// <summary>
/// throw an exception based on error state
/// </summary>
/// <param name="e"></param>
static void CheckError(LibSpeexDSP.RESAMPLER_ERR e)
{
LibSpeexDSP.RESAMPLER_ERR err = 0;
st = LibSpeexDSP.speex_resampler_init_frac(2, 64081, 88200, 32041, 44100, quality, ref err);
switch (e)
{
case LibSpeexDSP.RESAMPLER_ERR.SUCCESS:
return;
case LibSpeexDSP.RESAMPLER_ERR.ALLOC_FAILED:
throw new InsufficientMemoryException("LibSpeexDSP: Alloc failed");
case LibSpeexDSP.RESAMPLER_ERR.BAD_STATE:
throw new Exception("LibSpeexDSP: Bad state");
case LibSpeexDSP.RESAMPLER_ERR.INVALID_ARG:
throw new ArgumentException("LibSpeexDSP: Bad Argument");
case LibSpeexDSP.RESAMPLER_ERR.PTR_OVERLAP:
throw new Exception("LibSpeexDSP: Buffers cannot overlap");
}
}
public void StartSession(double ratio)
/// <summary>
///
/// </summary>
/// <param name="quality">0 to 10</param>
/// <param name="rationum">numerator of srate change ratio (inrate / outrate)</param>
/// <param name="ratioden">demonenator of srate change ratio (inrate / outrate)</param>
/// <param name="sratein">sampling rate in, rounded to nearest hz</param>
/// <param name="srateout">sampling rate out, rounded to nearest hz</param>
/// <param name="drainer">function which accepts output as produced</param>
public SpeexResampler(int quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action<short[], int> drainer)
{
LibSpeexDSP.RESAMPLER_ERR err = LibSpeexDSP.RESAMPLER_ERR.SUCCESS;
st = LibSpeexDSP.speex_resampler_init_frac(2, rationum, ratioden, sratein, srateout, quality, ref err);
if (st == IntPtr.Zero)
throw new Exception("LibSpeexDSP returned null!");
CheckError(err);
//System.Windows.Forms.MessageBox.Show(string.Format("inlat: {0} outlat: {1}", LibSpeexDSP.speex_resampler_get_input_latency(st), LibSpeexDSP.speex_resampler_get_output_latency(st)));
this.drainer = drainer;
outbuf = new short[inbuf.Length * ratioden / rationum / 2 * 2 + 128];
//System.Windows.Forms.MessageBox.Show(string.Format("inbuf: {0} outbuf: {1}", inbuf.Length, outbuf.Length));
}
/// <summary>
/// add a sample to the queue
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
public void EnqueueSample(short left, short right)
{
inbuf[inbufpos++] = left;
inbuf[inbufpos++] = right;
if (inbufpos == inbuf.Length)
Flush();
}
/// <summary>
/// flush as many input samples as possible, generating output samples right now
/// </summary>
public void Flush()
{
uint inal = (uint)inbufpos / 2;
uint outal = (uint)outbuf.Length / 2;
LibSpeexDSP.speex_resampler_process_interleaved_int(st, inbuf, ref inal, outbuf, ref outal);
// reset inbuf
Buffer.BlockCopy(inbuf, (int)inal * 2 * sizeof(short), inbuf, 0, inbufpos - (int)inal * 2);
inbufpos -= (int)inal * 2;
// dispatch outbuf
drainer(outbuf, (int)outal);
}
/*
public void ResampleChunk(Queue<short> input, Queue<short> output, bool finish)
{
while (input.Count > 0)
@ -308,6 +399,13 @@ namespace BizHawk.Emulation.Sound.Utilities
}
}
}
*/
public void Dispose()
{
LibSpeexDSP.speex_resampler_destroy(st);
st = IntPtr.Zero;
}
}
}