cleanup/simplify SpeexResampler
This commit is contained in:
parent
afa27c1a26
commit
71652b25dc
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue