2012-09-07 18:49:18 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
// ReSharper disable StyleCop.SA1300
|
|
|
|
|
// ReSharper disable InconsistentNaming
|
2013-11-14 19:33:13 +00:00
|
|
|
|
namespace BizHawk.Emulation.Common
|
2012-09-07 18:49:18 +00:00
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// junk wrapper around LibSpeexDSP. quite inefficient. will be replaced
|
|
|
|
|
/// </summary>
|
2016-12-11 17:14:42 +00:00
|
|
|
|
public class SpeexResampler : IDisposable, ISoundProvider
|
2012-09-07 18:49:18 +00:00
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
// to accept an ISyncSoundProvder 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];
|
|
|
|
|
|
2017-05-02 22:15:33 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// quality of the resampler. values other than those listed are valid, provided they are between MIN and MAX
|
|
|
|
|
/// </summary>
|
|
|
|
|
public enum Quality : int
|
2012-09-07 18:49:18 +00:00
|
|
|
|
{
|
2017-05-02 22:15:33 +00:00
|
|
|
|
QUALITY_MAX = 10,
|
|
|
|
|
QUALITY_MIN = 0,
|
|
|
|
|
QUALITY_DEFAULT = 4,
|
|
|
|
|
QUALITY_VOIP = 3,
|
|
|
|
|
QUALITY_DESKTOP = 5,
|
|
|
|
|
}
|
2017-05-02 22:04:17 +00:00
|
|
|
|
|
2017-05-02 22:15:33 +00:00
|
|
|
|
private static class LibSpeexDSP
|
|
|
|
|
{
|
2012-09-07 18:49:18 +00:00
|
|
|
|
public enum RESAMPLER_ERR
|
|
|
|
|
{
|
|
|
|
|
SUCCESS = 0,
|
|
|
|
|
ALLOC_FAILED = 1,
|
|
|
|
|
BAD_STATE = 2,
|
|
|
|
|
INVALID_ARG = 3,
|
|
|
|
|
PTR_OVERLAP = 4,
|
|
|
|
|
MAX_ERROR
|
2017-04-24 12:41:55 +00:00
|
|
|
|
}
|
2012-09-07 18:49:18 +00:00
|
|
|
|
|
|
|
|
|
/// <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>
|
2017-04-27 16:56:33 +00:00
|
|
|
|
/// <param name="err">The error state</param>
|
2012-09-07 18:49:18 +00:00
|
|
|
|
/// <returns>Newly created resampler state</returns>
|
|
|
|
|
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
|
2017-05-02 22:15:33 +00:00
|
|
|
|
public static extern IntPtr speex_resampler_init(uint nb_channels, uint in_rate, uint out_rate, Quality quality, ref RESAMPLER_ERR err);
|
2012-09-07 18:49:18 +00:00
|
|
|
|
|
|
|
|
|
/// <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>
|
2017-04-27 17:07:34 +00:00
|
|
|
|
/// <param name="err">The error state</param>
|
2012-09-07 18:49:18 +00:00
|
|
|
|
/// <returns>Newly created resampler state</returns>
|
|
|
|
|
[DllImport("libspeexdsp.dll", CallingConvention = CallingConvention.Cdecl)]
|
2017-05-02 22:15:33 +00:00
|
|
|
|
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);
|
2012-09-07 18:49:18 +00:00
|
|
|
|
|
|
|
|
|
/// <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>
|
2017-05-02 13:52:42 +00:00
|
|
|
|
/// <param name="st">resampler state</param>
|
2012-09-07 18:49:18 +00:00
|
|
|
|
/// <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)]
|
2017-05-02 22:15:33 +00:00
|
|
|
|
public static extern RESAMPLER_ERR speex_resampler_set_quality(IntPtr st, Quality quality);
|
2012-09-07 18:49:18 +00:00
|
|
|
|
|
|
|
|
|
/// <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)]
|
2017-05-02 22:15:33 +00:00
|
|
|
|
public static extern void speex_resampler_get_quality(IntPtr st, ref Quality quality);
|
2012-09-07 18:49:18 +00:00
|
|
|
|
|
|
|
|
|
/// <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);
|
|
|
|
|
|
2012-09-07 20:12:47 +00:00
|
|
|
|
/*these two functions don't exist in our version of the dll
|
|
|
|
|
|
2012-09-07 18:49:18 +00:00
|
|
|
|
/// <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);
|
|
|
|
|
|
2012-09-07 20:12:47 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2012-09-07 18:49:18 +00:00
|
|
|
|
/// <summary>
|
2017-05-02 13:52:42 +00:00
|
|
|
|
/// Make sure that the first samples to go out of the resampler don't have
|
2012-09-07 18:49:18 +00:00
|
|
|
|
/// 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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
// opaque pointer to state
|
|
|
|
|
private IntPtr _st = IntPtr.Zero;
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
private short[] _outbuf;
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
2016-12-11 17:14:42 +00:00
|
|
|
|
// for sync
|
2017-04-27 16:27:26 +00:00
|
|
|
|
private short[] _outbuf2 = new short[16];
|
2017-04-27 17:25:12 +00:00
|
|
|
|
private int _outbuf2pos;
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
// in buffer position in samples (not sample pairs)
|
|
|
|
|
private int _inbufpos;
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// throw an exception based on error state
|
|
|
|
|
/// </summary>
|
2017-04-24 12:41:55 +00:00
|
|
|
|
private static void CheckError(LibSpeexDSP.RESAMPLER_ERR e)
|
2012-09-07 20:12:47 +00:00
|
|
|
|
{
|
|
|
|
|
switch (e)
|
|
|
|
|
{
|
|
|
|
|
case LibSpeexDSP.RESAMPLER_ERR.SUCCESS:
|
|
|
|
|
return;
|
|
|
|
|
case LibSpeexDSP.RESAMPLER_ERR.ALLOC_FAILED:
|
2019-03-28 03:17:14 +00:00
|
|
|
|
throw new InsufficientMemoryException($"{nameof(LibSpeexDSP)}: Alloc failed");
|
2012-09-07 20:12:47 +00:00
|
|
|
|
case LibSpeexDSP.RESAMPLER_ERR.BAD_STATE:
|
2019-03-28 03:17:14 +00:00
|
|
|
|
throw new Exception($"{nameof(LibSpeexDSP)}: Bad state");
|
2012-09-07 20:12:47 +00:00
|
|
|
|
case LibSpeexDSP.RESAMPLER_ERR.INVALID_ARG:
|
2019-03-28 03:17:14 +00:00
|
|
|
|
throw new ArgumentException($"{nameof(LibSpeexDSP)}: Bad Argument");
|
2012-09-07 20:12:47 +00:00
|
|
|
|
case LibSpeexDSP.RESAMPLER_ERR.PTR_OVERLAP:
|
2019-03-28 03:17:14 +00:00
|
|
|
|
throw new Exception($"{nameof(LibSpeexDSP)}: Buffers cannot overlap");
|
2012-09-07 20:12:47 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2017-04-27 16:27:26 +00:00
|
|
|
|
/// Initializes a new instance of the <see cref="SpeexResampler"/> class
|
2012-09-07 20:12:47 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="quality">0 to 10</param>
|
2017-04-27 16:27:26 +00:00
|
|
|
|
/// <param name="rationum">numerator of sample rate change ratio (inrate / outrate)</param>
|
|
|
|
|
/// <param name="ratioden">denominator of sample rate change ratio (inrate / outrate)</param>
|
2012-09-07 20:12:47 +00:00
|
|
|
|
/// <param name="sratein">sampling rate in, rounded to nearest hz</param>
|
|
|
|
|
/// <param name="srateout">sampling rate out, rounded to nearest hz</param>
|
2017-04-27 16:27:26 +00:00
|
|
|
|
/// <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>
|
2017-05-02 22:15:33 +00:00
|
|
|
|
public SpeexResampler(Quality quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action<short[], int> drainer = null, ISoundProvider input = null)
|
2012-09-07 20:12:47 +00:00
|
|
|
|
{
|
2013-12-11 04:01:33 +00:00
|
|
|
|
if (drainer != null && input != null)
|
2017-04-24 12:41:55 +00:00
|
|
|
|
{
|
2019-03-28 03:17:14 +00:00
|
|
|
|
throw new ArgumentException($"Can't autofetch without being an {nameof(ISoundProvider)}?");
|
2017-04-24 12:41:55 +00:00
|
|
|
|
}
|
2013-12-11 04:01:33 +00:00
|
|
|
|
|
2012-09-07 20:12:47 +00:00
|
|
|
|
LibSpeexDSP.RESAMPLER_ERR err = LibSpeexDSP.RESAMPLER_ERR.SUCCESS;
|
2017-04-27 16:27:26 +00:00
|
|
|
|
_st = LibSpeexDSP.speex_resampler_init_frac(2, rationum, ratioden, sratein, srateout, quality, ref err);
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
if (_st == IntPtr.Zero)
|
2017-04-24 12:41:55 +00:00
|
|
|
|
{
|
2019-03-28 03:17:14 +00:00
|
|
|
|
throw new Exception($"{nameof(LibSpeexDSP)} returned null!");
|
2017-04-24 12:41:55 +00:00
|
|
|
|
}
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
|
|
|
|
CheckError(err);
|
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
_drainer = drainer ?? InternalDrain;
|
|
|
|
|
_input = input;
|
2012-09-07 18:49:18 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
_outbuf = new short[(_inbuf.Length * ratioden / rationum / 2 * 2) + 128];
|
2012-09-07 20:12:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-01 22:26:08 +00:00
|
|
|
|
/// <summary>change sampling rate on the fly</summary>
|
2017-04-27 16:27:26 +00:00
|
|
|
|
/// <param name="rationum">numerator of sample rate change ratio (inrate / outrate)</param>
|
|
|
|
|
/// <param name="ratioden">denominator of sample rate change ratio (inrate / outrate)</param>
|
2013-05-01 22:26:08 +00:00
|
|
|
|
/// <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)
|
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
CheckError(LibSpeexDSP.speex_resampler_set_rate_frac(_st, rationum, ratioden, sratein, srateout));
|
|
|
|
|
_outbuf = new short[(_inbuf.Length * ratioden / rationum / 2 * 2) + 128];
|
2013-05-01 22:26:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-07 20:12:47 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// add a sample to the queue
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void EnqueueSample(short left, short right)
|
2012-09-07 18:49:18 +00:00
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
_inbuf[_inbufpos++] = left;
|
|
|
|
|
_inbuf[_inbufpos++] = right;
|
2012-09-07 18:49:18 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
if (_inbufpos == _inbuf.Length)
|
2017-04-24 12:41:55 +00:00
|
|
|
|
{
|
2012-09-07 20:12:47 +00:00
|
|
|
|
Flush();
|
2017-04-24 12:41:55 +00:00
|
|
|
|
}
|
2012-09-07 18:49:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-09 00:41:11 +00:00
|
|
|
|
/// <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)
|
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
int shortstocopy = Math.Min(_inbuf.Length - _inbufpos, (nsamp - numused) * 2);
|
2012-09-09 00:41:11 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
Buffer.BlockCopy(userbuf, numused * 2 * sizeof(short), _inbuf, _inbufpos * sizeof(short), shortstocopy * sizeof(short));
|
|
|
|
|
_inbufpos += shortstocopy;
|
2012-09-09 00:41:11 +00:00
|
|
|
|
numused += shortstocopy / 2;
|
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
if (_inbufpos == _inbuf.Length)
|
2017-04-24 12:41:55 +00:00
|
|
|
|
{
|
2012-09-09 00:41:11 +00:00
|
|
|
|
Flush();
|
2017-04-24 12:41:55 +00:00
|
|
|
|
}
|
2012-09-09 00:41:11 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-07 20:12:47 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// flush as many input samples as possible, generating output samples right now
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void Flush()
|
2012-09-07 18:49:18 +00:00
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
uint inal = (uint)_inbufpos / 2;
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
uint outal = (uint)_outbuf.Length / 2;
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
LibSpeexDSP.speex_resampler_process_interleaved_int(_st, _inbuf, ref inal, _outbuf, ref outal);
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
|
|
|
|
// reset inbuf
|
2017-04-27 16:27:26 +00:00
|
|
|
|
if (inal != _inbufpos / 2)
|
2017-04-24 12:41:55 +00:00
|
|
|
|
{
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
throw new Exception("Speexresampler didn't eat the whole array?");
|
2017-04-24 12:41:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
_inbufpos = 0;
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
////Buffer.BlockCopy(inbuf, (int)inal * 2 * sizeof(short), inbuf, 0, inbufpos - (int)inal * 2);
|
|
|
|
|
////inbufpos -= (int)inal * 2;
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
|
|
|
|
// dispatch outbuf
|
2017-04-27 16:27:26 +00:00
|
|
|
|
_drainer(_outbuf, (int)outal);
|
2012-09-07 18:49:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
if (_st != IntPtr.Zero)
|
2016-12-12 01:02:16 +00:00
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
LibSpeexDSP.speex_resampler_destroy(_st);
|
|
|
|
|
_st = IntPtr.Zero;
|
2016-12-12 01:02:16 +00:00
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~SpeexResampler()
|
|
|
|
|
{
|
|
|
|
|
Dispose();
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
}
|
2012-09-07 18:49:18 +00:00
|
|
|
|
|
2017-04-24 12:41:55 +00:00
|
|
|
|
private void InternalDrain(short[] buf, int nsamp)
|
2012-09-07 18:49:18 +00:00
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
if (_outbuf2pos + (nsamp * 2) > _outbuf2.Length)
|
2012-09-07 18:49:18 +00:00
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
short[] newbuf = new short[_outbuf2pos + (nsamp * 2)];
|
|
|
|
|
Buffer.BlockCopy(_outbuf2, 0, newbuf, 0, _outbuf2pos * sizeof(short));
|
|
|
|
|
_outbuf2 = newbuf;
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
}
|
2017-04-24 12:41:55 +00:00
|
|
|
|
|
2017-04-27 16:27:26 +00:00
|
|
|
|
Buffer.BlockCopy(buf, 0, _outbuf2, _outbuf2pos * sizeof(short), nsamp * 2 * sizeof(short));
|
|
|
|
|
_outbuf2pos += nsamp * 2;
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
}
|
2012-09-07 18:49:18 +00:00
|
|
|
|
|
2016-12-11 17:14:42 +00:00
|
|
|
|
public void GetSamplesSync(out short[] samples, out int nsamp)
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
if (_input != null)
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
{
|
|
|
|
|
short[] sampin;
|
|
|
|
|
int nsampin;
|
2017-04-27 16:27:26 +00:00
|
|
|
|
_input.GetSamplesSync(out sampin, out nsampin);
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
EnqueueSamples(sampin, nsampin);
|
2012-09-07 18:49:18 +00:00
|
|
|
|
}
|
2017-04-24 12:41:55 +00:00
|
|
|
|
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
Flush();
|
2017-04-27 16:27:26 +00:00
|
|
|
|
nsamp = _outbuf2pos / 2;
|
|
|
|
|
samples = _outbuf2;
|
|
|
|
|
_outbuf2pos = 0;
|
2012-09-07 18:49:18 +00:00
|
|
|
|
}
|
2012-09-07 20:12:47 +00:00
|
|
|
|
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
public void DiscardSamples()
|
2012-09-07 20:12:47 +00:00
|
|
|
|
{
|
2017-04-27 16:27:26 +00:00
|
|
|
|
_outbuf2pos = 0;
|
2012-09-07 20:12:47 +00:00
|
|
|
|
}
|
2016-12-11 17:14:42 +00:00
|
|
|
|
|
2017-04-24 12:41:55 +00:00
|
|
|
|
public bool CanProvideAsync => false;
|
2016-12-11 17:14:42 +00:00
|
|
|
|
|
2017-04-24 12:41:55 +00:00
|
|
|
|
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
2016-12-11 17:14:42 +00:00
|
|
|
|
|
|
|
|
|
public void GetSamplesAsync(short[] samples)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Async mode is not supported.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetSyncMode(SyncSoundMode mode)
|
|
|
|
|
{
|
|
|
|
|
if (mode == SyncSoundMode.Async)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException("Async mode is not supported.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-09-07 18:49:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|