diff --git a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs
index d1449345ef..44f314b7ef 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs
@@ -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
- /// stores input samples as they come from the libsnes core
- Queue AudioInBuffer = new Queue();
- /// stores samples that have been converted to 44100hz
- Queue AudioOutBuffer = new Queue();
+ void InitAudio()
+ {
+ metaspu = new Sound.MetaspuSoundProvider(Sound.ESynchMethod.ESynchMethod_V);
+ resampler = new Sound.Utilities.SpeexResampler(6, 64081, 88200, 32041, 44100, new Action(metaspu.buffer.enqueue_samples));
- GCHandle _gc_snes_audio_sample;
+ }
- /// 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 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);
}
diff --git a/BizHawk.Emulation/Sound/Utilities/Metaspu.cs b/BizHawk.Emulation/Sound/Utilities/Metaspu.cs
index ca776eae6c..48b6789c3c 100644
--- a/BizHawk.Emulation/Sound/Utilities/Metaspu.cs
+++ b/BizHawk.Emulation/Sound/Utilities/Metaspu.cs
@@ -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);
};
diff --git a/BizHawk.Emulation/Sound/Utilities/SpeexResampler.cs b/BizHawk.Emulation/Sound/Utilities/SpeexResampler.cs
index ea95eed21b..e442c43c3d 100644
--- a/BizHawk.Emulation/Sound/Utilities/SpeexResampler.cs
+++ b/BizHawk.Emulation/Sound/Utilities/SpeexResampler.cs
@@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Sound.Utilities
///
/// junk wrapper around LibSpeexDSP. quite inefficient. will be replaced
///
- 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
+
///
/// Get the latency in input samples introduced by the resampler.
///
@@ -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);
+ */
+
///
/// 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
///
/// opaque pointer to state
///
- IntPtr st;
+ IntPtr st = IntPtr.Zero;
- public SpeexResampler(int quality)
+ ///
+ /// function to call to dispatch output
+ ///
+ Action drainer;
+
+ short[] inbuf = new short[512];
+
+ short[] outbuf;
+
+ ///
+ /// in buffer position in samples (not sample pairs)
+ ///
+ int inbufpos = 0;
+
+ ///
+ /// throw an exception based on error state
+ ///
+ ///
+ 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)
+ ///
+ ///
+ ///
+ /// 0 to 10
+ /// numerator of srate change ratio (inrate / outrate)
+ /// demonenator of srate change ratio (inrate / outrate)
+ /// sampling rate in, rounded to nearest hz
+ /// sampling rate out, rounded to nearest hz
+ /// function which accepts output as produced
+ public SpeexResampler(int quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action 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));
+
+ }
+
+ ///
+ /// add a sample to the queue
+ ///
+ ///
+ ///
+ public void EnqueueSample(short left, short right)
+ {
+ inbuf[inbufpos++] = left;
+ inbuf[inbufpos++] = right;
+
+ if (inbufpos == inbuf.Length)
+ Flush();
}
+ ///
+ /// flush as many input samples as possible, generating output samples right now
+ ///
+ 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 input, Queue 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;
+ }
}
}