From d9a93c474a3d95a9044049b13e52d3dd54756cd1 Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:01:51 -0700 Subject: [PATCH] throw out the speex resampler, place in SDL's resampler to replace it no more 2010 vc++ runtime requirement --- Assets/dll/libspeexdsp.dll | Bin 96256 -> 0 bytes src/BizHawk.Client.EmuHawk/Program.cs | 25 +- .../BizHawk.Emulation.Common.csproj | 1 + .../Sound/LibSpeexDSP.cs | 234 ----------------- .../Sound/SDLResampler.cs | 191 ++++++++++++++ .../Sound/SpeexResampler.cs | 245 ------------------ .../Consoles/Nintendo/N64/N64Audio.cs | 28 +- .../N64/NativeApi/mupen64plusAudioApi.cs | 4 +- 8 files changed, 222 insertions(+), 506 deletions(-) delete mode 100644 Assets/dll/libspeexdsp.dll delete mode 100644 src/BizHawk.Emulation.Common/Sound/LibSpeexDSP.cs create mode 100644 src/BizHawk.Emulation.Common/Sound/SDLResampler.cs delete mode 100644 src/BizHawk.Emulation.Common/Sound/SpeexResampler.cs diff --git a/Assets/dll/libspeexdsp.dll b/Assets/dll/libspeexdsp.dll deleted file mode 100644 index 02ebc1835148892a2cc93f123c4514abf3bff39c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96256 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 z 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 + diff --git a/src/BizHawk.Emulation.Common/Sound/LibSpeexDSP.cs b/src/BizHawk.Emulation.Common/Sound/LibSpeexDSP.cs deleted file mode 100644 index ec7193c87b..0000000000 --- a/src/BizHawk.Emulation.Common/Sound/LibSpeexDSP.cs +++ /dev/null @@ -1,234 +0,0 @@ -#nullable disable - -using System; -using System.Runtime.InteropServices; -using BizHawk.BizInvoke; -using static BizHawk.Emulation.Common.SpeexResampler; - -namespace BizHawk.Emulation.Common -{ - public abstract class LibSpeexDSP - { - public enum RESAMPLER_ERR - { - SUCCESS = 0, - ALLOC_FAILED = 1, - BAD_STATE = 2, - INVALID_ARG = 3, - PTR_OVERLAP = 4, - MAX_ERROR - } - - /// - /// Create a new resampler with integer input and output rates. - /// - /// Number of channels to be processed - /// Input sampling rate (integer number of Hz). - /// Output sampling rate (integer number of Hz). - /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality. - /// The error state - /// Newly created resampler state - [BizImport(CallingConvention.Cdecl)] - public abstract IntPtr speex_resampler_init(uint nb_channels, uint in_rate, uint out_rate, Quality quality, ref RESAMPLER_ERR err); - - /// - /// Create a new resampler with fractional input/output rates. The sampling - /// rate ratio is an arbitrary rational number with both the numerator and - /// denominator being 32-bit integers. - /// - /// Number of channels to be processed - /// Numerator of the sampling rate ratio - /// Denominator of the sampling rate ratio - /// Input sampling rate rounded to the nearest integer (in Hz). - /// Output sampling rate rounded to the nearest integer (in Hz). - /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality. - /// The error state - /// Newly created resampler state - [BizImport(CallingConvention.Cdecl)] - public abstract IntPtr speex_resampler_init_frac(uint nb_channels, uint ratio_num, uint ratio_den, uint in_rate, uint out_rate, Quality quality, ref RESAMPLER_ERR err); - - /// - /// Destroy a resampler state. - /// - /// Resampler state - [BizImport(CallingConvention.Cdecl)] - public abstract void speex_resampler_destroy(IntPtr st); - - /// - /// Resample a float array. The input and output buffers must *not* overlap. - /// - /// Resampler state - /// Index of the channel to process for the multi-channel base (0 otherwise) - /// Input buffer - /// Number of input samples in the input buffer. Returns the number of samples processed - /// Output buffer - /// Size of the output buffer. Returns the number of samples written - [BizImport(CallingConvention.Cdecl)] - public abstract RESAMPLER_ERR speex_resampler_process_float(IntPtr st, uint channel_index, float[] inp, ref uint in_len, float[] outp, ref uint out_len); - - /// - /// Resample an int array. The input and output buffers must *not* overlap. - /// - /// Resampler state - /// Index of the channel to process for the multi-channel base (0 otherwise) - /// Input buffer - /// Number of input samples in the input buffer. Returns the number of samples processed - /// Output buffer - /// Size of the output buffer. Returns the number of samples written - [BizImport(CallingConvention.Cdecl)] - public abstract RESAMPLER_ERR speex_resampler_process_int(IntPtr st, uint channel_index, short[] inp, ref uint in_len, short[] outp, ref uint out_len); - - /// - /// Resample an interleaved float array. The input and output buffers must *not* overlap. - /// - /// Resampler state - /// Input buffer - /// Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel. - /// Output buffer - /// Size of the output buffer. Returns the number of samples written. This is all per-channel. - [BizImport(CallingConvention.Cdecl)] - public abstract RESAMPLER_ERR speex_resampler_process_interleaved_float(IntPtr st, float[] inp, ref uint in_len, float[] outp, ref uint out_len); - - /// - /// Resample an interleaved int array. The input and output buffers must *not* overlap. - /// - /// Resampler state - /// Input buffer - /// Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel. - /// Output buffer - /// Size of the output buffer. Returns the number of samples written. This is all per-channel. - [BizImport(CallingConvention.Cdecl)] - public abstract RESAMPLER_ERR speex_resampler_process_interleaved_int(IntPtr st, short[] inp, ref uint in_len, short[] outp, ref uint out_len); - - /// - /// Set (change) the input/output sampling rates (integer value). - /// - /// Resampler state - /// Input sampling rate (integer number of Hz). - /// Output sampling rate (integer number of Hz). - [BizImport(CallingConvention.Cdecl)] - public abstract RESAMPLER_ERR speex_resampler_set_rate(IntPtr st, uint in_rate, uint out_rate); - - /// - /// Get the current input/output sampling rates (integer value). - /// - /// Resampler state - /// Input sampling rate (integer number of Hz) copied. - /// Output sampling rate (integer number of Hz) copied. - [BizImport(CallingConvention.Cdecl)] - public abstract void speex_resampler_get_rate(IntPtr st, ref uint in_rate, ref uint out_rate); - - /// - /// Set (change) the input/output sampling rates and resampling ratio (fractional values in Hz supported). - /// - /// resampler state - /// Numerator of the sampling rate ratio - /// Denominator of the sampling rate ratio - /// Input sampling rate rounded to the nearest integer (in Hz). - /// Output sampling rate rounded to the nearest integer (in Hz). - [BizImport(CallingConvention.Cdecl)] - public abstract RESAMPLER_ERR speex_resampler_set_rate_frac(IntPtr st, uint ratio_num, uint ratio_den, uint in_rate, uint out_rate); - - /// - /// Get the current resampling ratio. This will be reduced to the least common denominator. - /// - /// Resampler state - /// Numerator of the sampling rate ratio copied - /// Denominator of the sampling rate ratio copied - [BizImport(CallingConvention.Cdecl)] - public abstract void speex_resampler_get_ratio(IntPtr st, ref uint ratio_num, ref uint ratio_den); - - /// - /// Set (change) the conversion quality. - /// - /// Resampler state - /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality. - [BizImport(CallingConvention.Cdecl)] - public abstract RESAMPLER_ERR speex_resampler_set_quality(IntPtr st, Quality quality); - - /// - /// Get the conversion quality. - /// - /// Resampler state - /// Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality. - [BizImport(CallingConvention.Cdecl)] - public abstract void speex_resampler_get_quality(IntPtr st, ref Quality quality); - - /// - /// Set (change) the input stride. - /// - /// Resampler state - /// Input stride - [BizImport(CallingConvention.Cdecl)] - public abstract void speex_resampler_set_input_stride(IntPtr st, uint stride); - - /// - /// Get the input stride. - /// - /// Resampler state - /// Input stride copied - [BizImport(CallingConvention.Cdecl)] - public abstract void speex_resampler_get_input_stride(IntPtr st, ref uint stride); - - /// - /// Set (change) the output stride. - /// - /// Resampler state - /// Output stride - [BizImport(CallingConvention.Cdecl)] - public abstract void speex_resampler_set_output_stride(IntPtr st, uint stride); - - /// - /// Get the output stride. - /// - /// Resampler state - /// Output stride copied - [BizImport(CallingConvention.Cdecl)] - public abstract void speex_resampler_get_output_stride(IntPtr st, ref uint stride); - - /*these two functions don't exist in our version of the dll - - /// - /// Get the latency in input samples introduced by the resampler. - /// - /// Resampler state - [BizImport(CallingConvention.Cdecl)] - public abstract int speex_resampler_get_input_latency(IntPtr st); - - /// - /// Get the latency in output samples introduced by the resampler. - /// - /// Resampler state - [BizImport(CallingConvention.Cdecl)] - public abstract int speex_resampler_get_output_latency(IntPtr st); - - */ - - /// - /// Make sure that the first samples to go out of the resampler don't have - /// leading zeros. This is only useful before starting to use a newly created - /// resampler. It is recommended to use that when resampling an audio file, as - /// it will generate a file with the same length. For real-time processing, - /// it is probably easier not to use this call (so that the output duration - /// is the same for the first frame). - /// - /// Resampler state - [BizImport(CallingConvention.Cdecl)] - public abstract RESAMPLER_ERR speex_resampler_skip_zeros(IntPtr st); - - /// - /// Reset a resampler so a new (unrelated) stream can be processed. - /// - /// Resampler state - [BizImport(CallingConvention.Cdecl)] - public abstract RESAMPLER_ERR speex_resampler_reset_mem(IntPtr st); - - /// - /// Returns the English meaning for an error code - /// - /// Error code - /// English string - [BizImport(CallingConvention.Cdecl, Compatibility = true)] - public abstract string speex_resampler_strerror(RESAMPLER_ERR err); - } -} diff --git a/src/BizHawk.Emulation.Common/Sound/SDLResampler.cs b/src/BizHawk.Emulation.Common/Sound/SDLResampler.cs new file mode 100644 index 0000000000..f2d4575119 --- /dev/null +++ b/src/BizHawk.Emulation.Common/Sound/SDLResampler.cs @@ -0,0 +1,191 @@ +#nullable disable + +using System; +using System.Runtime.InteropServices; + +using static SDL2.SDL; + +namespace BizHawk.Emulation.Common +{ + /// + /// Wrapper against SDL's resampler + /// + public class SDLResampler : IDisposable, ISoundProvider + { + // to accept an ISyncSoundProvider input + private readonly ISoundProvider _input; + + // function to call to dispatch output + private readonly Action _drainer; + + private short[] _outBuf = Array.Empty(); + + // for ISoundProvider output use + private short[] _outSamples = Array.Empty(); + private int _outNumSamps; + + // opaque pointer to SDL_AudioStream + private IntPtr _stream; + + /// sampling rate in by hz + /// sampling rate out by hz + /// function which accepts output as produced. if null, act as an + /// source to take input from when output is requested. if null, no auto-fetching + /// and are both non-null + /// unmanaged call failed + public SDLResampler(int src_rate, int dst_rate, Action drainer = null, ISoundProvider input = null) + { + if (drainer is not null && input is not null) throw new ArgumentException(message: $"Can't autofetch without being an {nameof(ISoundProvider)}?", paramName: nameof(input)); + + _stream = SDL_NewAudioStream(AUDIO_S16SYS, 2, src_rate, AUDIO_S16SYS, 2, dst_rate); + + if (_stream == IntPtr.Zero) + { + throw new($"{nameof(SDL_NewAudioStream)} returned null! SDL error: {SDL_GetError()}"); + } + + _drainer = drainer ?? InternalDrain; + _input = input; + } + + // SDL bindings don't have this for some reason :( + [DllImport("SDL2", CallingConvention = CallingConvention.Cdecl)] + private static extern int SDL_AudioStreamFlush(IntPtr stream); + + /// change sampling rate on the fly + /// sampling rate in by hz + /// sampling rate out by hz + public void ChangeRate(int src_rate, int dst_rate) + { + // force flush the stream, as we'll be destroying it to change the sample rate... + if (SDL_AudioStreamFlush(_stream) != 0) + { + throw new($"{nameof(SDL_AudioStreamFlush)} failed! SDL error: {SDL_GetError()}"); + } + + Flush(); + + SDL_FreeAudioStream(_stream); + _stream = SDL_NewAudioStream(AUDIO_S16SYS, 2, src_rate, AUDIO_S16SYS, 2, dst_rate); + + if (_stream == IntPtr.Zero) + { + throw new($"{nameof(SDL_NewAudioStream)} returned null! SDL error: {SDL_GetError()}"); + } + } + + /// + /// add multiple samples to the queue + /// + /// interleaved stereo samples + /// number of sample pairs + public unsafe void EnqueueSamples(short[] userbuf, int nsamp) + { + fixed (short* ub = userbuf) + { + if (SDL_AudioStreamPut(_stream, (IntPtr)ub, nsamp * 4) != 0) + { + throw new($"{nameof(SDL_AudioStreamPut)} failed! SDL error: {SDL_GetError()}"); + } + } + } + + /// flush as many input samples as possible, generating output samples right now + /// unmanaged call failed + public unsafe void Flush() + { + var streamAvail = SDL_AudioStreamAvailable(_stream); + if (streamAvail == 0) + { + return; + } + + // stereo s16 audio always has 4 bytes per interleaved sample + if (streamAvail % 4 != 0) + { + throw new("SDL audio stream contained partial sample frames?"); + } + + if (streamAvail / 2 > _outBuf.Length) + { + _outBuf = new short[streamAvail / 2]; + } + + fixed (short* outBuf = _outBuf) + { + var numRead = SDL_AudioStreamGet(_stream, (IntPtr)outBuf, streamAvail); + if (numRead == -1) + { + throw new($"{nameof(SDL_AudioStreamGet)} failed! SDL error: {SDL_GetError()}"); + } + + if (numRead != streamAvail) + { + throw new($"{nameof(SDL_AudioStreamGet)} didn't eat the whole array?"); + } + } + + _drainer(_outBuf, streamAvail / 4); + } + + public void Dispose() + { + if (_stream != IntPtr.Zero) + { + SDL_FreeAudioStream(_stream); + _stream = IntPtr.Zero; + } + } + + private void InternalDrain(short[] buf, int nsamp) + { + if (_outNumSamps + nsamp * 2 > _outBuf.Length) + { + var newbuf = new short[_outNumSamps + nsamp * 2]; + Buffer.BlockCopy(_outSamples, 0, newbuf, 0, _outNumSamps * sizeof(short)); + _outSamples = newbuf; + } + + Buffer.BlockCopy(buf, 0, _outSamples, _outNumSamps * sizeof(short), nsamp * 2 * sizeof(short)); + _outNumSamps += nsamp * 2; + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + if (_input != null) + { + _input.GetSamplesSync(out var sampin, out var nsampin); + EnqueueSamples(sampin, nsampin); + } + + Flush(); + nsamp = _outNumSamps / 2; + samples = _outSamples; + _outNumSamps = 0; + } + + public void DiscardSamples() + { + _outNumSamps = 0; + } + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + /// always + public void GetSamplesAsync(short[] samples) + { + throw new InvalidOperationException("Async mode is not supported."); + } + + /// is + public void SetSyncMode(SyncSoundMode mode) + { + if (mode == SyncSoundMode.Async) + { + throw new NotSupportedException("Async mode is not supported."); + } + } + } +} diff --git a/src/BizHawk.Emulation.Common/Sound/SpeexResampler.cs b/src/BizHawk.Emulation.Common/Sound/SpeexResampler.cs deleted file mode 100644 index 3d15421fb4..0000000000 --- a/src/BizHawk.Emulation.Common/Sound/SpeexResampler.cs +++ /dev/null @@ -1,245 +0,0 @@ -#nullable disable - -using System; -using BizHawk.BizInvoke; -using BizHawk.Common; - -// ReSharper disable UnusedMember.Local -// ReSharper disable UnusedMember.Global -// ReSharper disable IdentifierTypo -// ReSharper disable StyleCop.SA1300 -// ReSharper disable InconsistentNaming -namespace BizHawk.Emulation.Common -{ - /// - /// junk wrapper around LibSpeexDSP. quite inefficient. will be replaced - /// - public class SpeexResampler : IDisposable, ISoundProvider - { - private static readonly LibSpeexDSP NativeDSP; - static SpeexResampler() - { - var resolver = new DynamicLibraryImportResolver( - OSTailoredCode.IsUnixHost ? "libspeexdsp.so.1" : "libspeexdsp.dll", hasLimitedLifetime: false); - NativeDSP = BizInvoker.GetInvoker(resolver, CallingConventionAdapters.Native); - } - - // to accept an ISyncSoundProvider input - private readonly ISoundProvider _input; - - // function to call to dispatch output - private readonly Action _drainer; - - // TODO: this size is roughly based on how big you can make the buffer before the snes resampling (32040.5 -> 44100) gets screwed up - private readonly short[] _inbuf = new short[512]; // [8192]; // [512]; - - /// - /// quality of the resampler. values other than those listed are valid, provided they are between MIN and MAX - /// - public enum Quality - { - QUALITY_MAX = 10, - QUALITY_MIN = 0, - QUALITY_DEFAULT = 4, - QUALITY_VOIP = 3, - QUALITY_DESKTOP = 5 - } - - // opaque pointer to state - private IntPtr _st = IntPtr.Zero; - - private short[] _outbuf; - - // for sync - private short[] _outbuf2 = new short[16]; - private int _outbuf2pos; - - // in buffer position in samples (not sample pairs) - private int _inbufpos; - - /// - /// throw an exception based on error state - /// - private static void CheckError(LibSpeexDSP.RESAMPLER_ERR e) - { - switch (e) - { - case LibSpeexDSP.RESAMPLER_ERR.SUCCESS: - return; -#pragma warning disable MA0012 // this will crash the .NET host, as it should - case LibSpeexDSP.RESAMPLER_ERR.ALLOC_FAILED: - throw new InsufficientMemoryException($"{nameof(LibSpeexDSP)}: Alloc failed"); -#pragma warning restore MA0012 - case LibSpeexDSP.RESAMPLER_ERR.BAD_STATE: - throw new Exception($"{nameof(LibSpeexDSP)}: Bad state"); - case LibSpeexDSP.RESAMPLER_ERR.INVALID_ARG: - throw new Exception($"{nameof(LibSpeexDSP)}: Bad Argument"); - case LibSpeexDSP.RESAMPLER_ERR.PTR_OVERLAP: - throw new Exception($"{nameof(LibSpeexDSP)}: Buffers cannot overlap"); - } - } - - /// 0 to 10 - /// numerator of sample rate change ratio (inrate / outrate) - /// denominator of sample rate change ratio (inrate / outrate) - /// sampling rate in, rounded to nearest hz - /// sampling rate out, rounded to nearest hz - /// function which accepts output as produced. if null, act as an - /// source to take input from when output is requested. if null, no auto-fetching - /// and are both non-null - /// unmanaged call failed - public SpeexResampler(Quality quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action drainer = null, ISoundProvider input = null) - { - if (drainer is not null && input is not null) throw new ArgumentException(message: $"Can't autofetch without being an {nameof(ISoundProvider)}?", paramName: nameof(input)); - - var err = LibSpeexDSP.RESAMPLER_ERR.SUCCESS; - _st = NativeDSP.speex_resampler_init_frac(2, rationum, ratioden, sratein, srateout, quality, ref err); - - if (_st == IntPtr.Zero) - { - throw new Exception($"{nameof(LibSpeexDSP)} returned null!"); - } - - CheckError(err); - - _drainer = drainer ?? InternalDrain; - _input = input; - - _outbuf = new short[(_inbuf.Length * ratioden / rationum / 2 * 2) + 128]; - } - - /// change sampling rate on the fly - /// numerator of sample rate change ratio (inrate / outrate) - /// denominator of sample rate change ratio (inrate / outrate) - /// sampling rate in, rounded to nearest hz - /// sampling rate out, rounded to nearest hz - public void ChangeRate(uint rationum, uint ratioden, uint sratein, uint srateout) - { - CheckError(NativeDSP.speex_resampler_set_rate_frac(_st, rationum, ratioden, sratein, srateout)); - _outbuf = new short[(_inbuf.Length * ratioden / rationum / 2 * 2) + 128]; - } - - /// - /// add a sample to the queue - /// - public void EnqueueSample(short left, short right) - { - _inbuf[_inbufpos++] = left; - _inbuf[_inbufpos++] = right; - - if (_inbufpos == _inbuf.Length) - { - Flush(); - } - } - - /// - /// add multiple samples to the queue - /// - /// interleaved stereo samples - /// number of sample pairs - public void EnqueueSamples(short[] userbuf, int nsamp) - { - int numused = 0; - while (numused < nsamp) - { - int shortstocopy = Math.Min(_inbuf.Length - _inbufpos, (nsamp - numused) * 2); - - Buffer.BlockCopy(userbuf, numused * 2 * sizeof(short), _inbuf, _inbufpos * sizeof(short), shortstocopy * sizeof(short)); - _inbufpos += shortstocopy; - numused += shortstocopy / 2; - - if (_inbufpos == _inbuf.Length) - { - Flush(); - } - } - } - - /// flush as many input samples as possible, generating output samples right now - /// unmanaged call failed - public void Flush() - { - uint inal = (uint)_inbufpos / 2; - - uint outal = (uint)_outbuf.Length / 2; - - NativeDSP.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) - { - NativeDSP.speex_resampler_destroy(_st); - _st = IntPtr.Zero; - GC.SuppressFinalize(this); - } - } - - ~SpeexResampler() - { - Dispose(); - } - - private void InternalDrain(short[] buf, int nsamp) - { - if (_outbuf2pos + (nsamp * 2) > _outbuf2.Length) - { - short[] newbuf = new short[_outbuf2pos + (nsamp * 2)]; - Buffer.BlockCopy(_outbuf2, 0, newbuf, 0, _outbuf2pos * sizeof(short)); - _outbuf2 = newbuf; - } - - Buffer.BlockCopy(buf, 0, _outbuf2, _outbuf2pos * sizeof(short), nsamp * 2 * sizeof(short)); - _outbuf2pos += nsamp * 2; - } - - public void GetSamplesSync(out short[] samples, out int nsamp) - { - if (_input != null) - { - _input.GetSamplesSync(out var sampin, out int nsampin); - EnqueueSamples(sampin, nsampin); - } - - Flush(); - nsamp = _outbuf2pos / 2; - samples = _outbuf2; - _outbuf2pos = 0; - } - - public void DiscardSamples() - { - _outbuf2pos = 0; - } - - public bool CanProvideAsync => false; - - public SyncSoundMode SyncMode => SyncSoundMode.Sync; - - /// always - public void GetSamplesAsync(short[] samples) - { - throw new InvalidOperationException("Async mode is not supported."); - } - - /// is - public void SetSyncMode(SyncSoundMode mode) - { - if (mode == SyncSoundMode.Async) - { - throw new NotSupportedException("Async mode is not supported."); - } - } - } -} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64Audio.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64Audio.cs index c16c0e44ff..587b334bd6 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64Audio.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64Audio.cs @@ -16,24 +16,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64 /// /// Buffer for audio data /// - private short[] audioBuffer = new short[0]; - private uint _samplingRate = 0; + private short[] audioBuffer = Array.Empty(); + + private int _samplingRate; + /// /// Currently active sampling rate /// - public uint SamplingRate + public int SamplingRate { get => _samplingRate; private set { _samplingRate = value; - Resampler.ChangeRate(_samplingRate, 44100, _samplingRate, 44100); + Resampler.ChangeRate(_samplingRate, 44100); } } + /// /// Resampler for audio output /// - public SpeexResampler Resampler { get; private set; } + public SDLResampler Resampler { get; private set; } + public bool RenderSound { get; set; } /// @@ -44,8 +48,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64 this.api = new mupen64plusAudioApi(core); _samplingRate = api.GetSamplingRate(); - Resampler = new SpeexResampler((SpeexResampler.Quality)6, SamplingRate, 44100, - SamplingRate, 44100); + Resampler = new(SamplingRate, 44100); coreAPI = core; coreAPI.VInterrupt += DoAudioFrame; @@ -57,19 +60,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64 /// public void DoAudioFrame() { - uint m64pSamplingRate = api.GetSamplingRate(); + var m64pSamplingRate = api.GetSamplingRate(); if (m64pSamplingRate != SamplingRate) + { SamplingRate = m64pSamplingRate; + } - int audioBufferSize = api.GetAudioBufferSize(); + var audioBufferSize = api.GetAudioBufferSize(); if (audioBuffer.Length < audioBufferSize) + { audioBuffer = new short[audioBufferSize]; + } if (audioBufferSize > 0) { api.GetAudioBuffer(audioBuffer); if (RenderSound) + { Resampler.EnqueueSamples(audioBuffer, audioBufferSize / 2); + Resampler.Flush(); + } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/NativeApi/mupen64plusAudioApi.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/NativeApi/mupen64plusAudioApi.cs index 9e2caaee4b..bd02093057 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/NativeApi/mupen64plusAudioApi.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/NativeApi/mupen64plusAudioApi.cs @@ -57,9 +57,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64.NativeApi /// /// Returns currently used sampling rate /// - public uint GetSamplingRate() + public int GetSamplingRate() { - return (uint)dllGetAudioRate(); + return dllGetAudioRate(); } ///