a bit of reorg - put Emulation.Common sound files in the sound folder not unecessarily nested
This commit is contained in:
parent
0632560899
commit
0154222737
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue