a bit of reorg - put Emulation.Common sound files in the sound folder not unecessarily nested

This commit is contained in:
adelikat 2020-05-17 09:50:01 -05:00
parent 0632560899
commit 0154222737
4 changed files with 831 additions and 831 deletions

View File

@ -1,181 +1,181 @@
using System;
using System.Runtime.InteropServices;
// ReSharper disable StyleCop.SA1300
// ReSharper disable InconsistentNaming
namespace BizHawk.Emulation.Common
{
/// <summary>
/// wrapper around blargg's unmanaged blip_buf
/// </summary>
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;
/// <exception cref="Exception">unmanaged call failed</exception>
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);
}
/// <exception cref="ArgumentOutOfRangeException"><paramref name="output"/> can't hold <paramref name="count"/> samples (or twice that if <paramref name="stereo"/> is <see langword="true"/>)</exception>
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);
}
/// <exception cref="ArgumentOutOfRangeException"><paramref name="output"/> can't hold 2 * <paramref name="count"/> samples</exception>
public int ReadSamplesLeft(short[] output, int count)
{
if (output.Length < count * 2)
{
throw new ArgumentOutOfRangeException();
}
return BlipBufDll.blip_read_samples(_context, output, count, 1);
}
/// <exception cref="ArgumentOutOfRangeException"><paramref name="output"/> can't hold 2 * <paramref name="count"/> samples</exception>
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
{
/// <summary>
/// wrapper around blargg's unmanaged blip_buf
/// </summary>
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;
/// <exception cref="Exception">unmanaged call failed</exception>
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);
}
/// <exception cref="ArgumentOutOfRangeException"><paramref name="output"/> can't hold <paramref name="count"/> samples (or twice that if <paramref name="stereo"/> is <see langword="true"/>)</exception>
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);
}
/// <exception cref="ArgumentOutOfRangeException"><paramref name="output"/> can't hold 2 * <paramref name="count"/> samples</exception>
public int ReadSamplesLeft(short[] output, int count)
{
if (output.Length < count * 2)
{
throw new ArgumentOutOfRangeException();
}
return BlipBufDll.blip_read_samples(_context, output, count, 1);
}
/// <exception cref="ArgumentOutOfRangeException"><paramref name="output"/> can't hold 2 * <paramref name="count"/> samples</exception>
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);
}
}
}
}

View File

@ -1,134 +1,134 @@
using System;
namespace BizHawk.Emulation.Common
{
/// <summary>
/// implements a DC block filter on top of an ISoundProvider. rather simple.
/// </summary>
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;
}
/// <exception cref="ArgumentNullException"><paramref name="input"/> is null</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="filterWidth"/> is not in 8..65536</exception>
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;
}
/// <summary>
/// pass a set of samples through the filter. should only be used in detached mode
/// </summary>
/// <param name="samples">sample buffer to modify</param>
/// <param name="length">number of samples (not pairs). stereo</param>
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
{
/// <summary>
/// implements a DC block filter on top of an ISoundProvider. rather simple.
/// </summary>
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;
}
/// <exception cref="ArgumentNullException"><paramref name="input"/> is null</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="filterWidth"/> is not in 8..65536</exception>
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;
}
/// <summary>
/// pass a set of samples through the filter. should only be used in detached mode
/// </summary>
/// <param name="samples">sample buffer to modify</param>
/// <param name="length">number of samples (not pairs). stereo</param>
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);
}
}
}

View File

@ -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
{
/// <summary>
/// junk wrapper around LibSpeexDSP. quite inefficient. will be replaced
/// </summary>
public class SpeexResampler : IDisposable, ISoundProvider
{
// to accept an ISyncSoundProvider input
private readonly ISoundProvider _input;
// function to call to dispatch output
private readonly Action<short[], int> _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];
/// <summary>
/// quality of the resampler. values other than those listed are valid, provided they are between MIN and MAX
/// </summary>
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
}
/// <summary>
/// Create a new resampler with integer input and output rates.
/// </summary>
/// <param name="nb_channels">Number of channels to be processed</param>
/// <param name="in_rate">Input sampling rate (integer number of Hz).</param>
/// <param name="out_rate">Output sampling rate (integer number of Hz).</param>
/// <param name="quality">Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.</param>
/// <param name="err">The error state</param>
/// <returns>Newly created resampler state</returns>
[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);
/// <summary>
/// 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.
/// </summary>
/// <param name="nb_channels">Number of channels to be processed</param>
/// <param name="ratio_num">Numerator of the sampling rate ratio</param>
/// <param name="ratio_den">Denominator of the sampling rate ratio</param>
/// <param name="in_rate">Input sampling rate rounded to the nearest integer (in Hz).</param>
/// <param name="out_rate">Output sampling rate rounded to the nearest integer (in Hz).</param>
/// <param name="quality">Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.</param>
/// <param name="err">The error state</param>
/// <returns>Newly created resampler state</returns>
[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);
/// <summary>
/// Destroy a resampler state.
/// </summary>
/// <param name="st">Resampler state</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_destroy(IntPtr st);
/// <summary>
/// Resample a float array. The input and output buffers must *not* overlap.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="channel_index">Index of the channel to process for the multi-channel base (0 otherwise)</param>
/// <param name="inp">Input buffer</param>
/// <param name="in_len">Number of input samples in the input buffer. Returns the number of samples processed</param>
/// <param name="outp">Output buffer</param>
/// <param name="out_len">Size of the output buffer. Returns the number of samples written</param>
[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);
/// <summary>
/// Resample an int array. The input and output buffers must *not* overlap.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="channel_index">Index of the channel to process for the multi-channel base (0 otherwise)</param>
/// <param name="inp">Input buffer</param>
/// <param name="in_len">Number of input samples in the input buffer. Returns the number of samples processed</param>
/// <param name="outp">Output buffer</param>
/// <param name="out_len">Size of the output buffer. Returns the number of samples written</param>
[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);
/// <summary>
/// Resample an interleaved float array. The input and output buffers must *not* overlap.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="inp">Input buffer</param>
/// <param name="in_len">Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel.</param>
/// <param name="outp">Output buffer</param>
/// <param name="out_len">Size of the output buffer. Returns the number of samples written. This is all per-channel.</param>
[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);
/// <summary>
/// Resample an interleaved int array. The input and output buffers must *not* overlap.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="inp">Input buffer</param>
/// <param name="in_len">Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel.</param>
/// <param name="outp">Output buffer</param>
/// <param name="out_len">Size of the output buffer. Returns the number of samples written. This is all per-channel.</param>
[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);
/// <summary>
/// Set (change) the input/output sampling rates (integer value).
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="in_rate">Input sampling rate (integer number of Hz).</param>
/// <param name="out_rate">Output sampling rate (integer number of Hz).</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern RESAMPLER_ERR speex_resampler_set_rate(IntPtr st, uint in_rate, uint out_rate);
/// <summary>
/// Get the current input/output sampling rates (integer value).
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="in_rate">Input sampling rate (integer number of Hz) copied.</param>
/// <param name="out_rate">Output sampling rate (integer number of Hz) copied.</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_get_rate(IntPtr st, ref uint in_rate, ref uint out_rate);
/// <summary>
/// Set (change) the input/output sampling rates and resampling ratio (fractional values in Hz supported).
/// </summary>
/// <param name="st">resampler state</param>
/// <param name="ratio_num">Numerator of the sampling rate ratio</param>
/// <param name="ratio_den">Denominator of the sampling rate ratio</param>
/// <param name="in_rate">Input sampling rate rounded to the nearest integer (in Hz).</param>
/// <param name="out_rate">Output sampling rate rounded to the nearest integer (in Hz).</param>
[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);
/// <summary>
/// Get the current resampling ratio. This will be reduced to the least common denominator.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="ratio_num">Numerator of the sampling rate ratio copied</param>
/// <param name="ratio_den">Denominator of the sampling rate ratio copied</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_get_ratio(IntPtr st, ref uint ratio_num, ref uint ratio_den);
/// <summary>
/// Set (change) the conversion quality.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="quality">Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern RESAMPLER_ERR speex_resampler_set_quality(IntPtr st, Quality quality);
/// <summary>
/// Get the conversion quality.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="quality">Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_get_quality(IntPtr st, ref Quality quality);
/// <summary>
/// Set (change) the input stride.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="stride">Input stride</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_set_input_stride(IntPtr st, uint stride);
/// <summary>
/// Get the input stride.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="stride">Input stride copied</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_get_input_stride(IntPtr st, ref uint stride);
/// <summary>
/// Set (change) the output stride.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="stride">Output stride</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_set_output_stride(IntPtr st, uint stride);
/// <summary>
/// Get the output stride.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="stride">Output stride copied</param>
[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>
/// <param name="st">Resampler state</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int speex_resampler_get_input_latency(IntPtr st);
/// <summary>
/// Get the latency in output samples introduced by the resampler.
/// </summary>
/// <param name="st">Resampler state</param>
[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 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).
/// </summary>
/// <param name="st">Resampler state</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern RESAMPLER_ERR speex_resampler_skip_zeroes(IntPtr st);
/// <summary>
/// Reset a resampler so a new (unrelated) stream can be processed.
/// </summary>
/// <param name="st">Resampler state</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern RESAMPLER_ERR speex_resampler_reset_mem(IntPtr st);
/// <summary>
/// Returns the English meaning for an error code
/// </summary>
/// <param name="err">Error code</param>
/// <returns>English string</returns>
[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;
/// <summary>
/// throw an exception based on error state
/// </summary>
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");
}
}
/// <param name="quality">0 to 10</param>
/// <param name="rationum">numerator of sample rate change ratio (inrate / outrate)</param>
/// <param name="ratioden">denominator of sample rate 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. if null, act as an <seealso cref="ISoundProvider"/></param>
/// <param name="input">source to take input from when output is requested. if null, no auto-fetching</param>
/// <exception cref="ArgumentException"><paramref name="drainer"/> and <paramref name="input"/> are both non-null</exception>
/// <exception cref="Exception">unmanaged call failed</exception>
public SpeexResampler(Quality quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action<short[], int> 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];
}
/// <summary>change sampling rate on the fly</summary>
/// <param name="rationum">numerator of sample rate change ratio (inrate / outrate)</param>
/// <param name="ratioden">denominator of sample rate 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>
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];
}
/// <summary>
/// add a sample to the queue
/// </summary>
public void EnqueueSample(short left, short right)
{
_inbuf[_inbufpos++] = left;
_inbuf[_inbufpos++] = right;
if (_inbufpos == _inbuf.Length)
{
Flush();
}
}
/// <summary>
/// add multiple samples to the queue
/// </summary>
/// <param name="userbuf">interleaved stereo samples</param>
/// <param name="nsamp">number of sample pairs</param>
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();
}
}
}
/// <summary>flush as many input samples as possible, generating output samples right now</summary>
/// <exception cref="Exception">unmanaged call failed</exception>
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;
/// <exception cref="InvalidOperationException">always</exception>
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
/// <exception cref="NotSupportedException"><paramref name="mode"/> is <see cref="SyncSoundMode.Async"/></exception>
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
{
/// <summary>
/// junk wrapper around LibSpeexDSP. quite inefficient. will be replaced
/// </summary>
public class SpeexResampler : IDisposable, ISoundProvider
{
// to accept an ISyncSoundProvider input
private readonly ISoundProvider _input;
// function to call to dispatch output
private readonly Action<short[], int> _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];
/// <summary>
/// quality of the resampler. values other than those listed are valid, provided they are between MIN and MAX
/// </summary>
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
}
/// <summary>
/// Create a new resampler with integer input and output rates.
/// </summary>
/// <param name="nb_channels">Number of channels to be processed</param>
/// <param name="in_rate">Input sampling rate (integer number of Hz).</param>
/// <param name="out_rate">Output sampling rate (integer number of Hz).</param>
/// <param name="quality">Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.</param>
/// <param name="err">The error state</param>
/// <returns>Newly created resampler state</returns>
[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);
/// <summary>
/// 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.
/// </summary>
/// <param name="nb_channels">Number of channels to be processed</param>
/// <param name="ratio_num">Numerator of the sampling rate ratio</param>
/// <param name="ratio_den">Denominator of the sampling rate ratio</param>
/// <param name="in_rate">Input sampling rate rounded to the nearest integer (in Hz).</param>
/// <param name="out_rate">Output sampling rate rounded to the nearest integer (in Hz).</param>
/// <param name="quality">Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.</param>
/// <param name="err">The error state</param>
/// <returns>Newly created resampler state</returns>
[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);
/// <summary>
/// Destroy a resampler state.
/// </summary>
/// <param name="st">Resampler state</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_destroy(IntPtr st);
/// <summary>
/// Resample a float array. The input and output buffers must *not* overlap.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="channel_index">Index of the channel to process for the multi-channel base (0 otherwise)</param>
/// <param name="inp">Input buffer</param>
/// <param name="in_len">Number of input samples in the input buffer. Returns the number of samples processed</param>
/// <param name="outp">Output buffer</param>
/// <param name="out_len">Size of the output buffer. Returns the number of samples written</param>
[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);
/// <summary>
/// Resample an int array. The input and output buffers must *not* overlap.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="channel_index">Index of the channel to process for the multi-channel base (0 otherwise)</param>
/// <param name="inp">Input buffer</param>
/// <param name="in_len">Number of input samples in the input buffer. Returns the number of samples processed</param>
/// <param name="outp">Output buffer</param>
/// <param name="out_len">Size of the output buffer. Returns the number of samples written</param>
[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);
/// <summary>
/// Resample an interleaved float array. The input and output buffers must *not* overlap.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="inp">Input buffer</param>
/// <param name="in_len">Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel.</param>
/// <param name="outp">Output buffer</param>
/// <param name="out_len">Size of the output buffer. Returns the number of samples written. This is all per-channel.</param>
[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);
/// <summary>
/// Resample an interleaved int array. The input and output buffers must *not* overlap.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="inp">Input buffer</param>
/// <param name="in_len">Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel.</param>
/// <param name="outp">Output buffer</param>
/// <param name="out_len">Size of the output buffer. Returns the number of samples written. This is all per-channel.</param>
[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);
/// <summary>
/// Set (change) the input/output sampling rates (integer value).
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="in_rate">Input sampling rate (integer number of Hz).</param>
/// <param name="out_rate">Output sampling rate (integer number of Hz).</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern RESAMPLER_ERR speex_resampler_set_rate(IntPtr st, uint in_rate, uint out_rate);
/// <summary>
/// Get the current input/output sampling rates (integer value).
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="in_rate">Input sampling rate (integer number of Hz) copied.</param>
/// <param name="out_rate">Output sampling rate (integer number of Hz) copied.</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_get_rate(IntPtr st, ref uint in_rate, ref uint out_rate);
/// <summary>
/// Set (change) the input/output sampling rates and resampling ratio (fractional values in Hz supported).
/// </summary>
/// <param name="st">resampler state</param>
/// <param name="ratio_num">Numerator of the sampling rate ratio</param>
/// <param name="ratio_den">Denominator of the sampling rate ratio</param>
/// <param name="in_rate">Input sampling rate rounded to the nearest integer (in Hz).</param>
/// <param name="out_rate">Output sampling rate rounded to the nearest integer (in Hz).</param>
[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);
/// <summary>
/// Get the current resampling ratio. This will be reduced to the least common denominator.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="ratio_num">Numerator of the sampling rate ratio copied</param>
/// <param name="ratio_den">Denominator of the sampling rate ratio copied</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_get_ratio(IntPtr st, ref uint ratio_num, ref uint ratio_den);
/// <summary>
/// Set (change) the conversion quality.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="quality">Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern RESAMPLER_ERR speex_resampler_set_quality(IntPtr st, Quality quality);
/// <summary>
/// Get the conversion quality.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="quality">Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_get_quality(IntPtr st, ref Quality quality);
/// <summary>
/// Set (change) the input stride.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="stride">Input stride</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_set_input_stride(IntPtr st, uint stride);
/// <summary>
/// Get the input stride.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="stride">Input stride copied</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_get_input_stride(IntPtr st, ref uint stride);
/// <summary>
/// Set (change) the output stride.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="stride">Output stride</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void speex_resampler_set_output_stride(IntPtr st, uint stride);
/// <summary>
/// Get the output stride.
/// </summary>
/// <param name="st">Resampler state</param>
/// <param name="stride">Output stride copied</param>
[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>
/// <param name="st">Resampler state</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int speex_resampler_get_input_latency(IntPtr st);
/// <summary>
/// Get the latency in output samples introduced by the resampler.
/// </summary>
/// <param name="st">Resampler state</param>
[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 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).
/// </summary>
/// <param name="st">Resampler state</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern RESAMPLER_ERR speex_resampler_skip_zeroes(IntPtr st);
/// <summary>
/// Reset a resampler so a new (unrelated) stream can be processed.
/// </summary>
/// <param name="st">Resampler state</param>
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern RESAMPLER_ERR speex_resampler_reset_mem(IntPtr st);
/// <summary>
/// Returns the English meaning for an error code
/// </summary>
/// <param name="err">Error code</param>
/// <returns>English string</returns>
[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;
/// <summary>
/// throw an exception based on error state
/// </summary>
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");
}
}
/// <param name="quality">0 to 10</param>
/// <param name="rationum">numerator of sample rate change ratio (inrate / outrate)</param>
/// <param name="ratioden">denominator of sample rate 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. if null, act as an <seealso cref="ISoundProvider"/></param>
/// <param name="input">source to take input from when output is requested. if null, no auto-fetching</param>
/// <exception cref="ArgumentException"><paramref name="drainer"/> and <paramref name="input"/> are both non-null</exception>
/// <exception cref="Exception">unmanaged call failed</exception>
public SpeexResampler(Quality quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action<short[], int> 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];
}
/// <summary>change sampling rate on the fly</summary>
/// <param name="rationum">numerator of sample rate change ratio (inrate / outrate)</param>
/// <param name="ratioden">denominator of sample rate 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>
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];
}
/// <summary>
/// add a sample to the queue
/// </summary>
public void EnqueueSample(short left, short right)
{
_inbuf[_inbufpos++] = left;
_inbuf[_inbufpos++] = right;
if (_inbufpos == _inbuf.Length)
{
Flush();
}
}
/// <summary>
/// add multiple samples to the queue
/// </summary>
/// <param name="userbuf">interleaved stereo samples</param>
/// <param name="nsamp">number of sample pairs</param>
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();
}
}
}
/// <summary>flush as many input samples as possible, generating output samples right now</summary>
/// <exception cref="Exception">unmanaged call failed</exception>
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;
/// <exception cref="InvalidOperationException">always</exception>
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
/// <exception cref="NotSupportedException"><paramref name="mode"/> is <see cref="SyncSoundMode.Async"/></exception>
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
}
}

View File

@ -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
}
}
}