diff --git a/src/BizHawk.Emulation.Common/Sound/Utilities/BlipBuffer.cs b/src/BizHawk.Emulation.Common/Sound/BlipBuffer.cs
similarity index 97%
rename from src/BizHawk.Emulation.Common/Sound/Utilities/BlipBuffer.cs
rename to src/BizHawk.Emulation.Common/Sound/BlipBuffer.cs
index 84aed3d122..00c1b571a9 100644
--- a/src/BizHawk.Emulation.Common/Sound/Utilities/BlipBuffer.cs
+++ b/src/BizHawk.Emulation.Common/Sound/BlipBuffer.cs
@@ -1,181 +1,181 @@
-using System;
-using System.Runtime.InteropServices;
-
-// ReSharper disable StyleCop.SA1300
-// ReSharper disable InconsistentNaming
-namespace BizHawk.Emulation.Common
-{
- ///
- /// wrapper around blargg's unmanaged blip_buf
- ///
- public sealed class BlipBuffer : IDisposable
- {
- // this is transitional only. if the band-limited synthesis idea works out, i'll
- // make a managed MIT implementation
- private static class BlipBufDll
- {
- /** Creates new buffer that can hold at most sample_count samples. Sets rates
- so that there are blip_max_ratio clocks per sample. Returns pointer to new
- buffer, or NULL if insufficient memory. */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr blip_new(int sample_count);
-
- /** Sets approximate input clock rate and output sample rate. For every
- clock_rate input clocks, approximately sample_rate samples are generated. */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern void blip_set_rates(IntPtr context, double clock_rate, double sample_rate);
-
- /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
- clock_rate must not be greater than sample_rate*blip_max_ratio. */
- public const int BlipMaxRatio = 1 << 20;
-
- /** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern void blip_clear(IntPtr context);
-
- /** Adds positive/negative delta into buffer at specified clock time. */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern void blip_add_delta(IntPtr context, uint clock_time, int delta);
-
- /** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern void blip_add_delta_fast(IntPtr context, uint clock_time, int delta);
-
- /** Length of time frame, in clocks, needed to make sample_count additional
- samples available. */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern int blip_clocks_needed(IntPtr context, int sample_count);
-
- /** Maximum number of samples that can be generated from one time frame. */
- public const int BlipMaxFrame = 4000;
-
- /** Makes input clocks before clock_duration available for reading as output
- samples. Also begins new time frame at clock_duration, so that clock time 0 in
- the new time frame specifies the same clock as clock_duration in the old time
- frame specified. Deltas can have been added slightly past clock_duration (up to
- however many clocks there are in two output samples). */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern void blip_end_frame(IntPtr context, uint clock_duration);
-
- /** Number of buffered samples available for reading. */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern int blip_samples_avail(IntPtr context);
-
- /** Reads and removes at most 'count' samples and writes them to 'out'. If
- 'stereo' is true, writes output to every other element of 'out', allowing easy
- interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
- samples. Returns number of samples actually read. */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern int blip_read_samples(IntPtr context, short[] @out, int count, int stereo);
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern int blip_read_samples(IntPtr context, IntPtr @out, int count, int stereo);
-
- /** Frees buffer. No effect if NULL is passed. */
- [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
- public static extern void blip_delete(IntPtr context);
- }
-
- private IntPtr _context;
-
- /// unmanaged call failed
- public BlipBuffer(int sampleCount)
- {
- _context = BlipBufDll.blip_new(sampleCount);
- if (_context == IntPtr.Zero)
- {
- throw new Exception("blip_new returned NULL!");
- }
- }
-
- ~BlipBuffer()
- {
- Dispose();
- }
-
- public void Dispose()
- {
- if (_context != IntPtr.Zero)
- {
- BlipBufDll.blip_delete(_context);
- _context = IntPtr.Zero;
- GC.SuppressFinalize(this);
- }
- }
-
- public void SetRates(double clockRate, double sampleRate)
- {
- BlipBufDll.blip_set_rates(_context, clockRate, sampleRate);
- }
-
- public const int MaxRatio = BlipBufDll.BlipMaxRatio;
-
- public void Clear()
- {
- BlipBufDll.blip_clear(_context);
- }
-
- public void AddDelta(uint clockTime, int delta)
- {
- BlipBufDll.blip_add_delta(_context, clockTime, delta);
- }
-
- public void AddDeltaFast(uint clockTime, int delta)
- {
- BlipBufDll.blip_add_delta_fast(_context, clockTime, delta);
- }
-
- public int ClocksNeeded(int sampleCount)
- {
- return BlipBufDll.blip_clocks_needed(_context, sampleCount);
- }
-
- public const int MaxFrame = BlipBufDll.BlipMaxFrame;
-
- public void EndFrame(uint clockDuration)
- {
- BlipBufDll.blip_end_frame(_context, clockDuration);
- }
-
- public int SamplesAvailable()
- {
- return BlipBufDll.blip_samples_avail(_context);
- }
-
- /// can't hold samples (or twice that if is )
- public int ReadSamples(short[] output, int count, bool stereo)
- {
- if (output.Length < count * (stereo ? 2 : 1))
- {
- throw new ArgumentOutOfRangeException();
- }
-
- return BlipBufDll.blip_read_samples(_context, output, count, stereo ? 1 : 0);
- }
-
- /// can't hold 2 * samples
- public int ReadSamplesLeft(short[] output, int count)
- {
- if (output.Length < count * 2)
- {
- throw new ArgumentOutOfRangeException();
- }
-
- return BlipBufDll.blip_read_samples(_context, output, count, 1);
- }
-
- /// can't hold 2 * samples
- public int ReadSamplesRight(short[] output, int count)
- {
- if (output.Length < count * 2)
- {
- throw new ArgumentOutOfRangeException();
- }
-
- unsafe
- {
- fixed (short* s = &output[1])
- return BlipBufDll.blip_read_samples(_context, new IntPtr(s), count, 1);
- }
- }
- }
-}
+using System;
+using System.Runtime.InteropServices;
+
+// ReSharper disable StyleCop.SA1300
+// ReSharper disable InconsistentNaming
+namespace BizHawk.Emulation.Common
+{
+ ///
+ /// wrapper around blargg's unmanaged blip_buf
+ ///
+ public sealed class BlipBuffer : IDisposable
+ {
+ // this is transitional only. if the band-limited synthesis idea works out, i'll
+ // make a managed MIT implementation
+ private static class BlipBufDll
+ {
+ /** Creates new buffer that can hold at most sample_count samples. Sets rates
+ so that there are blip_max_ratio clocks per sample. Returns pointer to new
+ buffer, or NULL if insufficient memory. */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr blip_new(int sample_count);
+
+ /** Sets approximate input clock rate and output sample rate. For every
+ clock_rate input clocks, approximately sample_rate samples are generated. */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void blip_set_rates(IntPtr context, double clock_rate, double sample_rate);
+
+ /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
+ clock_rate must not be greater than sample_rate*blip_max_ratio. */
+ public const int BlipMaxRatio = 1 << 20;
+
+ /** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void blip_clear(IntPtr context);
+
+ /** Adds positive/negative delta into buffer at specified clock time. */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void blip_add_delta(IntPtr context, uint clock_time, int delta);
+
+ /** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void blip_add_delta_fast(IntPtr context, uint clock_time, int delta);
+
+ /** Length of time frame, in clocks, needed to make sample_count additional
+ samples available. */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int blip_clocks_needed(IntPtr context, int sample_count);
+
+ /** Maximum number of samples that can be generated from one time frame. */
+ public const int BlipMaxFrame = 4000;
+
+ /** Makes input clocks before clock_duration available for reading as output
+ samples. Also begins new time frame at clock_duration, so that clock time 0 in
+ the new time frame specifies the same clock as clock_duration in the old time
+ frame specified. Deltas can have been added slightly past clock_duration (up to
+ however many clocks there are in two output samples). */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void blip_end_frame(IntPtr context, uint clock_duration);
+
+ /** Number of buffered samples available for reading. */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int blip_samples_avail(IntPtr context);
+
+ /** Reads and removes at most 'count' samples and writes them to 'out'. If
+ 'stereo' is true, writes output to every other element of 'out', allowing easy
+ interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
+ samples. Returns number of samples actually read. */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int blip_read_samples(IntPtr context, short[] @out, int count, int stereo);
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int blip_read_samples(IntPtr context, IntPtr @out, int count, int stereo);
+
+ /** Frees buffer. No effect if NULL is passed. */
+ [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void blip_delete(IntPtr context);
+ }
+
+ private IntPtr _context;
+
+ /// unmanaged call failed
+ public BlipBuffer(int sampleCount)
+ {
+ _context = BlipBufDll.blip_new(sampleCount);
+ if (_context == IntPtr.Zero)
+ {
+ throw new Exception("blip_new returned NULL!");
+ }
+ }
+
+ ~BlipBuffer()
+ {
+ Dispose();
+ }
+
+ public void Dispose()
+ {
+ if (_context != IntPtr.Zero)
+ {
+ BlipBufDll.blip_delete(_context);
+ _context = IntPtr.Zero;
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ public void SetRates(double clockRate, double sampleRate)
+ {
+ BlipBufDll.blip_set_rates(_context, clockRate, sampleRate);
+ }
+
+ public const int MaxRatio = BlipBufDll.BlipMaxRatio;
+
+ public void Clear()
+ {
+ BlipBufDll.blip_clear(_context);
+ }
+
+ public void AddDelta(uint clockTime, int delta)
+ {
+ BlipBufDll.blip_add_delta(_context, clockTime, delta);
+ }
+
+ public void AddDeltaFast(uint clockTime, int delta)
+ {
+ BlipBufDll.blip_add_delta_fast(_context, clockTime, delta);
+ }
+
+ public int ClocksNeeded(int sampleCount)
+ {
+ return BlipBufDll.blip_clocks_needed(_context, sampleCount);
+ }
+
+ public const int MaxFrame = BlipBufDll.BlipMaxFrame;
+
+ public void EndFrame(uint clockDuration)
+ {
+ BlipBufDll.blip_end_frame(_context, clockDuration);
+ }
+
+ public int SamplesAvailable()
+ {
+ return BlipBufDll.blip_samples_avail(_context);
+ }
+
+ /// can't hold samples (or twice that if is )
+ public int ReadSamples(short[] output, int count, bool stereo)
+ {
+ if (output.Length < count * (stereo ? 2 : 1))
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return BlipBufDll.blip_read_samples(_context, output, count, stereo ? 1 : 0);
+ }
+
+ /// can't hold 2 * samples
+ public int ReadSamplesLeft(short[] output, int count)
+ {
+ if (output.Length < count * 2)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return BlipBufDll.blip_read_samples(_context, output, count, 1);
+ }
+
+ /// can't hold 2 * samples
+ public int ReadSamplesRight(short[] output, int count)
+ {
+ if (output.Length < count * 2)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ unsafe
+ {
+ fixed (short* s = &output[1])
+ return BlipBufDll.blip_read_samples(_context, new IntPtr(s), count, 1);
+ }
+ }
+ }
+}
diff --git a/src/BizHawk.Emulation.Common/Sound/Utilities/DCFilter.cs b/src/BizHawk.Emulation.Common/Sound/DCFilter.cs
similarity index 95%
rename from src/BizHawk.Emulation.Common/Sound/Utilities/DCFilter.cs
rename to src/BizHawk.Emulation.Common/Sound/DCFilter.cs
index 4720764aba..3d4e7a9c36 100644
--- a/src/BizHawk.Emulation.Common/Sound/Utilities/DCFilter.cs
+++ b/src/BizHawk.Emulation.Common/Sound/DCFilter.cs
@@ -1,134 +1,134 @@
-using System;
-
-namespace BizHawk.Emulation.Common
-{
- ///
- /// implements a DC block filter on top of an ISoundProvider. rather simple.
- ///
- public sealed class DCFilter : ISoundProvider
- {
- private readonly ISoundProvider _soundProvider;
- private readonly int _depth;
-
- private int _latchL;
- private int _latchR;
- private int _accumL;
- private int _accumR;
-
- private static int DepthFromFilterWidth(int filterWidth)
- {
- int ret = -2;
- while (filterWidth > 0)
- {
- filterWidth >>= 1;
- ret++;
- }
-
- return ret;
- }
-
- /// is null
- /// is not in 8..65536
- public DCFilter(ISoundProvider input, int filterWidth)
- {
- if (input == null)
- {
- throw new ArgumentNullException();
- }
-
- if (filterWidth < 8 || filterWidth > 65536)
- {
- throw new ArgumentOutOfRangeException();
- }
-
- _depth = DepthFromFilterWidth(filterWidth);
-
- _soundProvider = input;
- }
-
- ///
- /// pass a set of samples through the filter. should only be used in detached mode
- ///
- /// sample buffer to modify
- /// number of samples (not pairs). stereo
- public void PushThroughSamples(short[] samples, int length)
- {
- PushThroughSamples(samples, samples, length);
- }
-
- private void PushThroughSamples(short[] samplesIn, short[] samplesOut, int length)
- {
- for (int i = 0; i < length; i += 2)
- {
- int l = samplesIn[i] << 12;
- int r = samplesIn[i + 1] << 12;
- _accumL -= _accumL >> _depth;
- _accumR -= _accumR >> _depth;
- _accumL += l - _latchL;
- _accumR += r - _latchR;
- _latchL = l;
- _latchR = r;
-
- int bigL = _accumL >> 12;
- int bigR = _accumR >> 12;
-
- // check for clipping
- if (bigL > 32767)
- {
- samplesOut[i] = 32767;
- }
- else if (bigL < -32768)
- {
- samplesOut[i] = -32768;
- }
- else
- {
- samplesOut[i] = (short)bigL;
- }
-
- if (bigR > 32767)
- {
- samplesOut[i + 1] = 32767;
- }
- else if (bigR < -32768)
- {
- samplesOut[i + 1] = -32768;
- }
- else
- {
- samplesOut[i + 1] = (short)bigR;
- }
- }
- }
-
- public void GetSamplesAsync(short[] samples)
- {
- _soundProvider.GetSamplesAsync(samples);
- PushThroughSamples(samples, samples.Length);
- }
-
- public void DiscardSamples()
- {
- _soundProvider.DiscardSamples();
- }
-
- public void GetSamplesSync(out short[] samples, out int nsamp)
- {
- _soundProvider.GetSamplesSync(out var sampIn, out var nsampIn);
-
- short[] ret = new short[nsampIn * 2];
- PushThroughSamples(sampIn, ret, nsampIn * 2);
- samples = ret;
- nsamp = nsampIn;
- }
-
- public SyncSoundMode SyncMode => _soundProvider.SyncMode;
-
- public bool CanProvideAsync => _soundProvider.CanProvideAsync;
-
- public void SetSyncMode(SyncSoundMode mode)
- {
- _soundProvider.SetSyncMode(mode);
- }
- }
-}
+using System;
+
+namespace BizHawk.Emulation.Common
+{
+ ///
+ /// implements a DC block filter on top of an ISoundProvider. rather simple.
+ ///
+ public sealed class DCFilter : ISoundProvider
+ {
+ private readonly ISoundProvider _soundProvider;
+ private readonly int _depth;
+
+ private int _latchL;
+ private int _latchR;
+ private int _accumL;
+ private int _accumR;
+
+ private static int DepthFromFilterWidth(int filterWidth)
+ {
+ int ret = -2;
+ while (filterWidth > 0)
+ {
+ filterWidth >>= 1;
+ ret++;
+ }
+
+ return ret;
+ }
+
+ /// is null
+ /// is not in 8..65536
+ public DCFilter(ISoundProvider input, int filterWidth)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ if (filterWidth < 8 || filterWidth > 65536)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ _depth = DepthFromFilterWidth(filterWidth);
+
+ _soundProvider = input;
+ }
+
+ ///
+ /// pass a set of samples through the filter. should only be used in detached mode
+ ///
+ /// sample buffer to modify
+ /// number of samples (not pairs). stereo
+ public void PushThroughSamples(short[] samples, int length)
+ {
+ PushThroughSamples(samples, samples, length);
+ }
+
+ private void PushThroughSamples(short[] samplesIn, short[] samplesOut, int length)
+ {
+ for (int i = 0; i < length; i += 2)
+ {
+ int l = samplesIn[i] << 12;
+ int r = samplesIn[i + 1] << 12;
+ _accumL -= _accumL >> _depth;
+ _accumR -= _accumR >> _depth;
+ _accumL += l - _latchL;
+ _accumR += r - _latchR;
+ _latchL = l;
+ _latchR = r;
+
+ int bigL = _accumL >> 12;
+ int bigR = _accumR >> 12;
+
+ // check for clipping
+ if (bigL > 32767)
+ {
+ samplesOut[i] = 32767;
+ }
+ else if (bigL < -32768)
+ {
+ samplesOut[i] = -32768;
+ }
+ else
+ {
+ samplesOut[i] = (short)bigL;
+ }
+
+ if (bigR > 32767)
+ {
+ samplesOut[i + 1] = 32767;
+ }
+ else if (bigR < -32768)
+ {
+ samplesOut[i + 1] = -32768;
+ }
+ else
+ {
+ samplesOut[i + 1] = (short)bigR;
+ }
+ }
+ }
+
+ public void GetSamplesAsync(short[] samples)
+ {
+ _soundProvider.GetSamplesAsync(samples);
+ PushThroughSamples(samples, samples.Length);
+ }
+
+ public void DiscardSamples()
+ {
+ _soundProvider.DiscardSamples();
+ }
+
+ public void GetSamplesSync(out short[] samples, out int nsamp)
+ {
+ _soundProvider.GetSamplesSync(out var sampIn, out var nsampIn);
+
+ short[] ret = new short[nsampIn * 2];
+ PushThroughSamples(sampIn, ret, nsampIn * 2);
+ samples = ret;
+ nsamp = nsampIn;
+ }
+
+ public SyncSoundMode SyncMode => _soundProvider.SyncMode;
+
+ public bool CanProvideAsync => _soundProvider.CanProvideAsync;
+
+ public void SetSyncMode(SyncSoundMode mode)
+ {
+ _soundProvider.SetSyncMode(mode);
+ }
+ }
+}
diff --git a/src/BizHawk.Emulation.Common/Sound/Utilities/SpeexResampler.cs b/src/BizHawk.Emulation.Common/Sound/SpeexResampler.cs
similarity index 97%
rename from src/BizHawk.Emulation.Common/Sound/Utilities/SpeexResampler.cs
rename to src/BizHawk.Emulation.Common/Sound/SpeexResampler.cs
index f170ef1704..8a168f224a 100644
--- a/src/BizHawk.Emulation.Common/Sound/Utilities/SpeexResampler.cs
+++ b/src/BizHawk.Emulation.Common/Sound/SpeexResampler.cs
@@ -1,460 +1,460 @@
-using System;
-using System.Runtime.InteropServices;
-
-// ReSharper disable UnusedMember.Local
-// ReSharper disable UnusedMember.Global
-// ReSharper disable IdentifierTypo
-// ReSharper disable StyleCop.SA1300
-// ReSharper disable InconsistentNaming
-namespace BizHawk.Emulation.Common
-{
- ///
- /// junk wrapper around LibSpeexDSP. quite inefficient. will be replaced
- ///
- public class SpeexResampler : IDisposable, ISoundProvider
- {
- // to accept an ISyncSoundProvider input
- private readonly ISoundProvider _input;
-
- // function to call to dispatch output
- private readonly Action _drainer;
-
- // TODO: this size is roughly based on how big you can make the buffer before the snes resampling (32040.5 -> 44100) gets screwed up
- private readonly short[] _inbuf = new short[512]; // [8192]; // [512];
-
- ///
- /// quality of the resampler. values other than those listed are valid, provided they are between MIN and MAX
- ///
- public enum Quality
- {
- QUALITY_MAX = 10,
- QUALITY_MIN = 0,
- QUALITY_DEFAULT = 4,
- QUALITY_VOIP = 3,
- QUALITY_DESKTOP = 5
- }
-
- private static class LibSpeexDSP
- {
- public enum RESAMPLER_ERR
- {
- SUCCESS = 0,
- ALLOC_FAILED = 1,
- BAD_STATE = 2,
- INVALID_ARG = 3,
- PTR_OVERLAP = 4,
- MAX_ERROR
- }
-
- ///
- /// Create a new resampler with integer input and output rates.
- ///
- /// Number of channels to be processed
- /// Input sampling rate (integer number of Hz).
- /// Output sampling rate (integer number of Hz).
- /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
- /// The error state
- /// Newly created resampler state
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr speex_resampler_init(uint nb_channels, uint in_rate, uint out_rate, Quality quality, ref RESAMPLER_ERR err);
-
- ///
- /// Create a new resampler with fractional input/output rates. The sampling
- /// rate ratio is an arbitrary rational number with both the numerator and
- /// denominator being 32-bit integers.
- ///
- /// Number of channels to be processed
- /// Numerator of the sampling rate ratio
- /// Denominator of the sampling rate ratio
- /// Input sampling rate rounded to the nearest integer (in Hz).
- /// Output sampling rate rounded to the nearest integer (in Hz).
- /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
- /// The error state
- /// Newly created resampler state
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr speex_resampler_init_frac(uint nb_channels, uint ratio_num, uint ratio_den, uint in_rate, uint out_rate, Quality quality, ref RESAMPLER_ERR err);
-
- ///
- /// Destroy a resampler state.
- ///
- /// Resampler state
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern void speex_resampler_destroy(IntPtr st);
-
- ///
- /// Resample a float array. The input and output buffers must *not* overlap.
- ///
- /// Resampler state
- /// Index of the channel to process for the multi-channel base (0 otherwise)
- /// Input buffer
- /// Number of input samples in the input buffer. Returns the number of samples processed
- /// Output buffer
- /// Size of the output buffer. Returns the number of samples written
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern RESAMPLER_ERR speex_resampler_process_float(IntPtr st, uint channel_index, float[] inp, ref uint in_len, float[] outp, ref uint out_len);
-
- ///
- /// Resample an int array. The input and output buffers must *not* overlap.
- ///
- /// Resampler state
- /// Index of the channel to process for the multi-channel base (0 otherwise)
- /// Input buffer
- /// Number of input samples in the input buffer. Returns the number of samples processed
- /// Output buffer
- /// Size of the output buffer. Returns the number of samples written
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern RESAMPLER_ERR speex_resampler_process_int(IntPtr st, uint channel_index, short[] inp, ref uint in_len, short[] outp, ref uint out_len);
-
- ///
- /// Resample an interleaved float array. The input and output buffers must *not* overlap.
- ///
- /// Resampler state
- /// Input buffer
- /// Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel.
- /// Output buffer
- /// Size of the output buffer. Returns the number of samples written. This is all per-channel.
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern RESAMPLER_ERR speex_resampler_process_interleaved_float(IntPtr st, float[] inp, ref uint in_len, float[] outp, ref uint out_len);
-
- ///
- /// Resample an interleaved int array. The input and output buffers must *not* overlap.
- ///
- /// Resampler state
- /// Input buffer
- /// Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel.
- /// Output buffer
- /// Size of the output buffer. Returns the number of samples written. This is all per-channel.
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern RESAMPLER_ERR speex_resampler_process_interleaved_int(IntPtr st, short[] inp, ref uint in_len, short[] outp, ref uint out_len);
-
- ///
- /// Set (change) the input/output sampling rates (integer value).
- ///
- /// Resampler state
- /// Input sampling rate (integer number of Hz).
- /// Output sampling rate (integer number of Hz).
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern RESAMPLER_ERR speex_resampler_set_rate(IntPtr st, uint in_rate, uint out_rate);
-
- ///
- /// Get the current input/output sampling rates (integer value).
- ///
- /// Resampler state
- /// Input sampling rate (integer number of Hz) copied.
- /// Output sampling rate (integer number of Hz) copied.
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern void speex_resampler_get_rate(IntPtr st, ref uint in_rate, ref uint out_rate);
-
- ///
- /// Set (change) the input/output sampling rates and resampling ratio (fractional values in Hz supported).
- ///
- /// resampler state
- /// Numerator of the sampling rate ratio
- /// Denominator of the sampling rate ratio
- /// Input sampling rate rounded to the nearest integer (in Hz).
- /// Output sampling rate rounded to the nearest integer (in Hz).
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern RESAMPLER_ERR speex_resampler_set_rate_frac(IntPtr st, uint ratio_num, uint ratio_den, uint in_rate, uint out_rate);
-
- ///
- /// Get the current resampling ratio. This will be reduced to the least common denominator.
- ///
- /// Resampler state
- /// Numerator of the sampling rate ratio copied
- /// Denominator of the sampling rate ratio copied
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern void speex_resampler_get_ratio(IntPtr st, ref uint ratio_num, ref uint ratio_den);
-
- ///
- /// Set (change) the conversion quality.
- ///
- /// Resampler state
- /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern RESAMPLER_ERR speex_resampler_set_quality(IntPtr st, Quality quality);
-
- ///
- /// Get the conversion quality.
- ///
- /// Resampler state
- /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern void speex_resampler_get_quality(IntPtr st, ref Quality quality);
-
- ///
- /// Set (change) the input stride.
- ///
- /// Resampler state
- /// Input stride
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern void speex_resampler_set_input_stride(IntPtr st, uint stride);
-
- ///
- /// Get the input stride.
- ///
- /// Resampler state
- /// Input stride copied
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern void speex_resampler_get_input_stride(IntPtr st, ref uint stride);
-
- ///
- /// Set (change) the output stride.
- ///
- /// Resampler state
- /// Output stride
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern void speex_resampler_set_output_stride(IntPtr st, uint stride);
-
- ///
- /// Get the output stride.
- ///
- /// Resampler state
- /// Output stride copied
- [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.
- ///
- /// Resampler state
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern int speex_resampler_get_input_latency(IntPtr st);
-
- ///
- /// Get the latency in output samples introduced by the resampler.
- ///
- /// Resampler state
- [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 resampler don't have
- /// leading zeros. This is only useful before starting to use a newly created
- /// resampler. It is recommended to use that when resampling an audio file, as
- /// it will generate a file with the same length. For real-time processing,
- /// it is probably easier not to use this call (so that the output duration
- /// is the same for the first frame).
- ///
- /// Resampler state
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern RESAMPLER_ERR speex_resampler_skip_zeroes(IntPtr st);
-
- ///
- /// Reset a resampler so a new (unrelated) stream can be processed.
- ///
- /// Resampler state
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern RESAMPLER_ERR speex_resampler_reset_mem(IntPtr st);
-
- ///
- /// Returns the English meaning for an error code
- ///
- /// Error code
- /// English string
- [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern string speex_resampler_strerror(RESAMPLER_ERR err);
- }
-
- // opaque pointer to state
- private IntPtr _st = IntPtr.Zero;
-
- private short[] _outbuf;
-
- // for sync
- private short[] _outbuf2 = new short[16];
- private int _outbuf2pos;
-
- // in buffer position in samples (not sample pairs)
- private int _inbufpos;
-
- ///
- /// throw an exception based on error state
- ///
- private static void CheckError(LibSpeexDSP.RESAMPLER_ERR e)
- {
- switch (e)
- {
- case LibSpeexDSP.RESAMPLER_ERR.SUCCESS:
- return;
- case LibSpeexDSP.RESAMPLER_ERR.ALLOC_FAILED:
- throw new InsufficientMemoryException($"{nameof(LibSpeexDSP)}: Alloc failed");
- case LibSpeexDSP.RESAMPLER_ERR.BAD_STATE:
- throw new Exception($"{nameof(LibSpeexDSP)}: Bad state");
- case LibSpeexDSP.RESAMPLER_ERR.INVALID_ARG:
- throw new ArgumentException($"{nameof(LibSpeexDSP)}: Bad Argument");
- case LibSpeexDSP.RESAMPLER_ERR.PTR_OVERLAP:
- throw new Exception($"{nameof(LibSpeexDSP)}: Buffers cannot overlap");
- }
- }
-
- /// 0 to 10
- /// numerator of sample rate change ratio (inrate / outrate)
- /// denominator of sample rate change ratio (inrate / outrate)
- /// sampling rate in, rounded to nearest hz
- /// sampling rate out, rounded to nearest hz
- /// function which accepts output as produced. if null, act as an
- /// source to take input from when output is requested. if null, no auto-fetching
- /// and are both non-null
- /// unmanaged call failed
- public SpeexResampler(Quality quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action drainer = null, ISoundProvider input = null)
- {
- if (drainer != null && input != null)
- {
- throw new ArgumentException($"Can't autofetch without being an {nameof(ISoundProvider)}?");
- }
-
- var 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($"{nameof(LibSpeexDSP)} returned null!");
- }
-
- CheckError(err);
-
- _drainer = drainer ?? InternalDrain;
- _input = input;
-
- _outbuf = new short[(_inbuf.Length * ratioden / rationum / 2 * 2) + 128];
- }
-
- /// change sampling rate on the fly
- /// numerator of sample rate change ratio (inrate / outrate)
- /// denominator of sample rate change ratio (inrate / outrate)
- /// sampling rate in, rounded to nearest hz
- /// sampling rate out, rounded to nearest hz
- public void ChangeRate(uint rationum, uint ratioden, uint sratein, uint srateout)
- {
- CheckError(LibSpeexDSP.speex_resampler_set_rate_frac(_st, rationum, ratioden, sratein, srateout));
- _outbuf = new short[(_inbuf.Length * ratioden / rationum / 2 * 2) + 128];
- }
-
- ///
- /// add a sample to the queue
- ///
- public void EnqueueSample(short left, short right)
- {
- _inbuf[_inbufpos++] = left;
- _inbuf[_inbufpos++] = right;
-
- if (_inbufpos == _inbuf.Length)
- {
- Flush();
- }
- }
-
- ///
- /// add multiple samples to the queue
- ///
- /// interleaved stereo samples
- /// number of sample pairs
- public void EnqueueSamples(short[] userbuf, int nsamp)
- {
- int numused = 0;
- while (numused < nsamp)
- {
- int shortstocopy = Math.Min(_inbuf.Length - _inbufpos, (nsamp - numused) * 2);
-
- Buffer.BlockCopy(userbuf, numused * 2 * sizeof(short), _inbuf, _inbufpos * sizeof(short), shortstocopy * sizeof(short));
- _inbufpos += shortstocopy;
- numused += shortstocopy / 2;
-
- if (_inbufpos == _inbuf.Length)
- {
- Flush();
- }
- }
- }
-
- /// flush as many input samples as possible, generating output samples right now
- /// unmanaged call failed
- 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
- if (inal != _inbufpos / 2)
- {
- throw new Exception("Speexresampler didn't eat the whole array?");
- }
-
- _inbufpos = 0;
- _drainer(_outbuf, (int)outal);
- }
-
- public void Dispose()
- {
- if (_st != IntPtr.Zero)
- {
- LibSpeexDSP.speex_resampler_destroy(_st);
- _st = IntPtr.Zero;
- GC.SuppressFinalize(this);
- }
- }
-
- ~SpeexResampler()
- {
- Dispose();
- }
-
- private void InternalDrain(short[] buf, int nsamp)
- {
- if (_outbuf2pos + (nsamp * 2) > _outbuf2.Length)
- {
- short[] newbuf = new short[_outbuf2pos + (nsamp * 2)];
- Buffer.BlockCopy(_outbuf2, 0, newbuf, 0, _outbuf2pos * sizeof(short));
- _outbuf2 = newbuf;
- }
-
- Buffer.BlockCopy(buf, 0, _outbuf2, _outbuf2pos * sizeof(short), nsamp * 2 * sizeof(short));
- _outbuf2pos += nsamp * 2;
- }
-
- public void GetSamplesSync(out short[] samples, out int nsamp)
- {
- if (_input != null)
- {
- _input.GetSamplesSync(out var sampin, out int nsampin);
- EnqueueSamples(sampin, nsampin);
- }
-
- Flush();
- nsamp = _outbuf2pos / 2;
- samples = _outbuf2;
- _outbuf2pos = 0;
- }
-
- public void DiscardSamples()
- {
- _outbuf2pos = 0;
- }
-
- public bool CanProvideAsync => false;
-
- public SyncSoundMode SyncMode => SyncSoundMode.Sync;
-
- /// always
- public void GetSamplesAsync(short[] samples)
- {
- throw new InvalidOperationException("Async mode is not supported.");
- }
-
- /// is
- public void SetSyncMode(SyncSoundMode mode)
- {
- if (mode == SyncSoundMode.Async)
- {
- throw new NotSupportedException("Async mode is not supported.");
- }
- }
- }
-}
+using System;
+using System.Runtime.InteropServices;
+
+// ReSharper disable UnusedMember.Local
+// ReSharper disable UnusedMember.Global
+// ReSharper disable IdentifierTypo
+// ReSharper disable StyleCop.SA1300
+// ReSharper disable InconsistentNaming
+namespace BizHawk.Emulation.Common
+{
+ ///
+ /// junk wrapper around LibSpeexDSP. quite inefficient. will be replaced
+ ///
+ public class SpeexResampler : IDisposable, ISoundProvider
+ {
+ // to accept an ISyncSoundProvider input
+ private readonly ISoundProvider _input;
+
+ // function to call to dispatch output
+ private readonly Action _drainer;
+
+ // TODO: this size is roughly based on how big you can make the buffer before the snes resampling (32040.5 -> 44100) gets screwed up
+ private readonly short[] _inbuf = new short[512]; // [8192]; // [512];
+
+ ///
+ /// quality of the resampler. values other than those listed are valid, provided they are between MIN and MAX
+ ///
+ public enum Quality
+ {
+ QUALITY_MAX = 10,
+ QUALITY_MIN = 0,
+ QUALITY_DEFAULT = 4,
+ QUALITY_VOIP = 3,
+ QUALITY_DESKTOP = 5
+ }
+
+ private static class LibSpeexDSP
+ {
+ public enum RESAMPLER_ERR
+ {
+ SUCCESS = 0,
+ ALLOC_FAILED = 1,
+ BAD_STATE = 2,
+ INVALID_ARG = 3,
+ PTR_OVERLAP = 4,
+ MAX_ERROR
+ }
+
+ ///
+ /// Create a new resampler with integer input and output rates.
+ ///
+ /// Number of channels to be processed
+ /// Input sampling rate (integer number of Hz).
+ /// Output sampling rate (integer number of Hz).
+ /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
+ /// The error state
+ /// Newly created resampler state
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr speex_resampler_init(uint nb_channels, uint in_rate, uint out_rate, Quality quality, ref RESAMPLER_ERR err);
+
+ ///
+ /// Create a new resampler with fractional input/output rates. The sampling
+ /// rate ratio is an arbitrary rational number with both the numerator and
+ /// denominator being 32-bit integers.
+ ///
+ /// Number of channels to be processed
+ /// Numerator of the sampling rate ratio
+ /// Denominator of the sampling rate ratio
+ /// Input sampling rate rounded to the nearest integer (in Hz).
+ /// Output sampling rate rounded to the nearest integer (in Hz).
+ /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
+ /// The error state
+ /// Newly created resampler state
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr speex_resampler_init_frac(uint nb_channels, uint ratio_num, uint ratio_den, uint in_rate, uint out_rate, Quality quality, ref RESAMPLER_ERR err);
+
+ ///
+ /// Destroy a resampler state.
+ ///
+ /// Resampler state
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void speex_resampler_destroy(IntPtr st);
+
+ ///
+ /// Resample a float array. The input and output buffers must *not* overlap.
+ ///
+ /// Resampler state
+ /// Index of the channel to process for the multi-channel base (0 otherwise)
+ /// Input buffer
+ /// Number of input samples in the input buffer. Returns the number of samples processed
+ /// Output buffer
+ /// Size of the output buffer. Returns the number of samples written
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern RESAMPLER_ERR speex_resampler_process_float(IntPtr st, uint channel_index, float[] inp, ref uint in_len, float[] outp, ref uint out_len);
+
+ ///
+ /// Resample an int array. The input and output buffers must *not* overlap.
+ ///
+ /// Resampler state
+ /// Index of the channel to process for the multi-channel base (0 otherwise)
+ /// Input buffer
+ /// Number of input samples in the input buffer. Returns the number of samples processed
+ /// Output buffer
+ /// Size of the output buffer. Returns the number of samples written
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern RESAMPLER_ERR speex_resampler_process_int(IntPtr st, uint channel_index, short[] inp, ref uint in_len, short[] outp, ref uint out_len);
+
+ ///
+ /// Resample an interleaved float array. The input and output buffers must *not* overlap.
+ ///
+ /// Resampler state
+ /// Input buffer
+ /// Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel.
+ /// Output buffer
+ /// Size of the output buffer. Returns the number of samples written. This is all per-channel.
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern RESAMPLER_ERR speex_resampler_process_interleaved_float(IntPtr st, float[] inp, ref uint in_len, float[] outp, ref uint out_len);
+
+ ///
+ /// Resample an interleaved int array. The input and output buffers must *not* overlap.
+ ///
+ /// Resampler state
+ /// Input buffer
+ /// Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel.
+ /// Output buffer
+ /// Size of the output buffer. Returns the number of samples written. This is all per-channel.
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern RESAMPLER_ERR speex_resampler_process_interleaved_int(IntPtr st, short[] inp, ref uint in_len, short[] outp, ref uint out_len);
+
+ ///
+ /// Set (change) the input/output sampling rates (integer value).
+ ///
+ /// Resampler state
+ /// Input sampling rate (integer number of Hz).
+ /// Output sampling rate (integer number of Hz).
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern RESAMPLER_ERR speex_resampler_set_rate(IntPtr st, uint in_rate, uint out_rate);
+
+ ///
+ /// Get the current input/output sampling rates (integer value).
+ ///
+ /// Resampler state
+ /// Input sampling rate (integer number of Hz) copied.
+ /// Output sampling rate (integer number of Hz) copied.
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void speex_resampler_get_rate(IntPtr st, ref uint in_rate, ref uint out_rate);
+
+ ///
+ /// Set (change) the input/output sampling rates and resampling ratio (fractional values in Hz supported).
+ ///
+ /// resampler state
+ /// Numerator of the sampling rate ratio
+ /// Denominator of the sampling rate ratio
+ /// Input sampling rate rounded to the nearest integer (in Hz).
+ /// Output sampling rate rounded to the nearest integer (in Hz).
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern RESAMPLER_ERR speex_resampler_set_rate_frac(IntPtr st, uint ratio_num, uint ratio_den, uint in_rate, uint out_rate);
+
+ ///
+ /// Get the current resampling ratio. This will be reduced to the least common denominator.
+ ///
+ /// Resampler state
+ /// Numerator of the sampling rate ratio copied
+ /// Denominator of the sampling rate ratio copied
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void speex_resampler_get_ratio(IntPtr st, ref uint ratio_num, ref uint ratio_den);
+
+ ///
+ /// Set (change) the conversion quality.
+ ///
+ /// Resampler state
+ /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern RESAMPLER_ERR speex_resampler_set_quality(IntPtr st, Quality quality);
+
+ ///
+ /// Get the conversion quality.
+ ///
+ /// Resampler state
+ /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void speex_resampler_get_quality(IntPtr st, ref Quality quality);
+
+ ///
+ /// Set (change) the input stride.
+ ///
+ /// Resampler state
+ /// Input stride
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void speex_resampler_set_input_stride(IntPtr st, uint stride);
+
+ ///
+ /// Get the input stride.
+ ///
+ /// Resampler state
+ /// Input stride copied
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void speex_resampler_get_input_stride(IntPtr st, ref uint stride);
+
+ ///
+ /// Set (change) the output stride.
+ ///
+ /// Resampler state
+ /// Output stride
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void speex_resampler_set_output_stride(IntPtr st, uint stride);
+
+ ///
+ /// Get the output stride.
+ ///
+ /// Resampler state
+ /// Output stride copied
+ [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.
+ ///
+ /// Resampler state
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int speex_resampler_get_input_latency(IntPtr st);
+
+ ///
+ /// Get the latency in output samples introduced by the resampler.
+ ///
+ /// Resampler state
+ [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 resampler don't have
+ /// leading zeros. This is only useful before starting to use a newly created
+ /// resampler. It is recommended to use that when resampling an audio file, as
+ /// it will generate a file with the same length. For real-time processing,
+ /// it is probably easier not to use this call (so that the output duration
+ /// is the same for the first frame).
+ ///
+ /// Resampler state
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern RESAMPLER_ERR speex_resampler_skip_zeroes(IntPtr st);
+
+ ///
+ /// Reset a resampler so a new (unrelated) stream can be processed.
+ ///
+ /// Resampler state
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern RESAMPLER_ERR speex_resampler_reset_mem(IntPtr st);
+
+ ///
+ /// Returns the English meaning for an error code
+ ///
+ /// Error code
+ /// English string
+ [DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern string speex_resampler_strerror(RESAMPLER_ERR err);
+ }
+
+ // opaque pointer to state
+ private IntPtr _st = IntPtr.Zero;
+
+ private short[] _outbuf;
+
+ // for sync
+ private short[] _outbuf2 = new short[16];
+ private int _outbuf2pos;
+
+ // in buffer position in samples (not sample pairs)
+ private int _inbufpos;
+
+ ///
+ /// throw an exception based on error state
+ ///
+ private static void CheckError(LibSpeexDSP.RESAMPLER_ERR e)
+ {
+ switch (e)
+ {
+ case LibSpeexDSP.RESAMPLER_ERR.SUCCESS:
+ return;
+ case LibSpeexDSP.RESAMPLER_ERR.ALLOC_FAILED:
+ throw new InsufficientMemoryException($"{nameof(LibSpeexDSP)}: Alloc failed");
+ case LibSpeexDSP.RESAMPLER_ERR.BAD_STATE:
+ throw new Exception($"{nameof(LibSpeexDSP)}: Bad state");
+ case LibSpeexDSP.RESAMPLER_ERR.INVALID_ARG:
+ throw new ArgumentException($"{nameof(LibSpeexDSP)}: Bad Argument");
+ case LibSpeexDSP.RESAMPLER_ERR.PTR_OVERLAP:
+ throw new Exception($"{nameof(LibSpeexDSP)}: Buffers cannot overlap");
+ }
+ }
+
+ /// 0 to 10
+ /// numerator of sample rate change ratio (inrate / outrate)
+ /// denominator of sample rate change ratio (inrate / outrate)
+ /// sampling rate in, rounded to nearest hz
+ /// sampling rate out, rounded to nearest hz
+ /// function which accepts output as produced. if null, act as an
+ /// source to take input from when output is requested. if null, no auto-fetching
+ /// and are both non-null
+ /// unmanaged call failed
+ public SpeexResampler(Quality quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action drainer = null, ISoundProvider input = null)
+ {
+ if (drainer != null && input != null)
+ {
+ throw new ArgumentException($"Can't autofetch without being an {nameof(ISoundProvider)}?");
+ }
+
+ var 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($"{nameof(LibSpeexDSP)} returned null!");
+ }
+
+ CheckError(err);
+
+ _drainer = drainer ?? InternalDrain;
+ _input = input;
+
+ _outbuf = new short[(_inbuf.Length * ratioden / rationum / 2 * 2) + 128];
+ }
+
+ /// change sampling rate on the fly
+ /// numerator of sample rate change ratio (inrate / outrate)
+ /// denominator of sample rate change ratio (inrate / outrate)
+ /// sampling rate in, rounded to nearest hz
+ /// sampling rate out, rounded to nearest hz
+ public void ChangeRate(uint rationum, uint ratioden, uint sratein, uint srateout)
+ {
+ CheckError(LibSpeexDSP.speex_resampler_set_rate_frac(_st, rationum, ratioden, sratein, srateout));
+ _outbuf = new short[(_inbuf.Length * ratioden / rationum / 2 * 2) + 128];
+ }
+
+ ///
+ /// add a sample to the queue
+ ///
+ public void EnqueueSample(short left, short right)
+ {
+ _inbuf[_inbufpos++] = left;
+ _inbuf[_inbufpos++] = right;
+
+ if (_inbufpos == _inbuf.Length)
+ {
+ Flush();
+ }
+ }
+
+ ///
+ /// add multiple samples to the queue
+ ///
+ /// interleaved stereo samples
+ /// number of sample pairs
+ public void EnqueueSamples(short[] userbuf, int nsamp)
+ {
+ int numused = 0;
+ while (numused < nsamp)
+ {
+ int shortstocopy = Math.Min(_inbuf.Length - _inbufpos, (nsamp - numused) * 2);
+
+ Buffer.BlockCopy(userbuf, numused * 2 * sizeof(short), _inbuf, _inbufpos * sizeof(short), shortstocopy * sizeof(short));
+ _inbufpos += shortstocopy;
+ numused += shortstocopy / 2;
+
+ if (_inbufpos == _inbuf.Length)
+ {
+ Flush();
+ }
+ }
+ }
+
+ /// flush as many input samples as possible, generating output samples right now
+ /// unmanaged call failed
+ 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
+ if (inal != _inbufpos / 2)
+ {
+ throw new Exception("Speexresampler didn't eat the whole array?");
+ }
+
+ _inbufpos = 0;
+ _drainer(_outbuf, (int)outal);
+ }
+
+ public void Dispose()
+ {
+ if (_st != IntPtr.Zero)
+ {
+ LibSpeexDSP.speex_resampler_destroy(_st);
+ _st = IntPtr.Zero;
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ ~SpeexResampler()
+ {
+ Dispose();
+ }
+
+ private void InternalDrain(short[] buf, int nsamp)
+ {
+ if (_outbuf2pos + (nsamp * 2) > _outbuf2.Length)
+ {
+ short[] newbuf = new short[_outbuf2pos + (nsamp * 2)];
+ Buffer.BlockCopy(_outbuf2, 0, newbuf, 0, _outbuf2pos * sizeof(short));
+ _outbuf2 = newbuf;
+ }
+
+ Buffer.BlockCopy(buf, 0, _outbuf2, _outbuf2pos * sizeof(short), nsamp * 2 * sizeof(short));
+ _outbuf2pos += nsamp * 2;
+ }
+
+ public void GetSamplesSync(out short[] samples, out int nsamp)
+ {
+ if (_input != null)
+ {
+ _input.GetSamplesSync(out var sampin, out int nsampin);
+ EnqueueSamples(sampin, nsampin);
+ }
+
+ Flush();
+ nsamp = _outbuf2pos / 2;
+ samples = _outbuf2;
+ _outbuf2pos = 0;
+ }
+
+ public void DiscardSamples()
+ {
+ _outbuf2pos = 0;
+ }
+
+ public bool CanProvideAsync => false;
+
+ public SyncSoundMode SyncMode => SyncSoundMode.Sync;
+
+ /// always
+ public void GetSamplesAsync(short[] samples)
+ {
+ throw new InvalidOperationException("Async mode is not supported.");
+ }
+
+ /// is
+ public void SetSyncMode(SyncSoundMode mode)
+ {
+ if (mode == SyncSoundMode.Async)
+ {
+ throw new NotSupportedException("Async mode is not supported.");
+ }
+ }
+ }
+}
diff --git a/src/BizHawk.Emulation.Common/Sound/Utilities/Waves.cs b/src/BizHawk.Emulation.Common/Sound/Waves.cs
similarity index 96%
rename from src/BizHawk.Emulation.Common/Sound/Utilities/Waves.cs
rename to src/BizHawk.Emulation.Common/Sound/Waves.cs
index 466d3a60a5..28d4ba2868 100644
--- a/src/BizHawk.Emulation.Common/Sound/Utilities/Waves.cs
+++ b/src/BizHawk.Emulation.Common/Sound/Waves.cs
@@ -1,57 +1,57 @@
-namespace BizHawk.Emulation.Common
-{
- public static class Waves
- {
- public static short[] SquareWave;
- public static short[] ImperfectSquareWave;
- public static short[] NoiseWave;
- public static short[] PeriodicWave16;
-
- public static void InitWaves()
- {
- SquareWave = new short[]
- {
- -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768,
- 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767
- };
-
- ImperfectSquareWave = new short[]
- {
- -32768, -30145, -27852, -26213, -24902, -23592, -22282, -20971, -19988, -19005, -18350, -17694, -17366, -17039, -16711, -16711,
- 32767, 30145, 27852, 26213, 24902, 23592, 22282, 20971, 19988, 19005, 18350, 17694, 17366, 17039, 16711, 16711
- };
-
- PeriodicWave16 = new short[] { 32767, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- NoiseWave = new short[0x1000];
- var rnd = new System.Random(unchecked((int)0xDEADBEEF));
- for (int i = 0; i < NoiseWave.Length; i++)
- {
- int r = rnd.Next();
- if ((r & 1) > 0)
- {
- NoiseWave[i] = short.MaxValue;
- }
- }
-
-#if false
- TriangleWave = new short[512];
- for (int i = 0; i < 256; i++)
- TriangleWave[i] = (short)((ushort.MaxValue*i/256)-short.MinValue);
- for (int i = 0; i < 256; i++)
- TriangleWave[256+i] = TriangleWave[256-i];
- TriangleWave[256] = short.MaxValue;
-
- SawWave = new short[512];
- for (int i = 0; i < 512; i++)
- SawWave[i] = (short)((ushort.MaxValue * i / 512) - short.MinValue);
-
- SineWave = new short[1024];
- for (int i=0; i<1024; i++)
- {
- SineWave[i] = (short) (Math.Sin(i*Math.PI*2/1024d)*32767);
- }
-#endif
- }
- }
+namespace BizHawk.Emulation.Common
+{
+ public static class Waves
+ {
+ public static short[] SquareWave;
+ public static short[] ImperfectSquareWave;
+ public static short[] NoiseWave;
+ public static short[] PeriodicWave16;
+
+ public static void InitWaves()
+ {
+ SquareWave = new short[]
+ {
+ -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768,
+ 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767
+ };
+
+ ImperfectSquareWave = new short[]
+ {
+ -32768, -30145, -27852, -26213, -24902, -23592, -22282, -20971, -19988, -19005, -18350, -17694, -17366, -17039, -16711, -16711,
+ 32767, 30145, 27852, 26213, 24902, 23592, 22282, 20971, 19988, 19005, 18350, 17694, 17366, 17039, 16711, 16711
+ };
+
+ PeriodicWave16 = new short[] { 32767, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ NoiseWave = new short[0x1000];
+ var rnd = new System.Random(unchecked((int)0xDEADBEEF));
+ for (int i = 0; i < NoiseWave.Length; i++)
+ {
+ int r = rnd.Next();
+ if ((r & 1) > 0)
+ {
+ NoiseWave[i] = short.MaxValue;
+ }
+ }
+
+#if false
+ TriangleWave = new short[512];
+ for (int i = 0; i < 256; i++)
+ TriangleWave[i] = (short)((ushort.MaxValue*i/256)-short.MinValue);
+ for (int i = 0; i < 256; i++)
+ TriangleWave[256+i] = TriangleWave[256-i];
+ TriangleWave[256] = short.MaxValue;
+
+ SawWave = new short[512];
+ for (int i = 0; i < 512; i++)
+ SawWave[i] = (short)((ushort.MaxValue * i / 512) - short.MinValue);
+
+ SineWave = new short[1024];
+ for (int i=0; i<1024; i++)
+ {
+ SineWave[i] = (short) (Math.Sin(i*Math.PI*2/1024d)*32767);
+ }
+#endif
+ }
+ }
}
\ No newline at end of file