aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Tests/Audio
diff options
context:
space:
mode:
authorLogan Stromberg <loganstromberg@gmail.com>2023-02-21 02:44:57 -0800
committerGitHub <noreply@github.com>2023-02-21 11:44:57 +0100
commitedfd4d70c0f38d41c6ebb31508127b14727017bd (patch)
treecf379010089a72cf1b33387e4ce664f51c8557d1 /Ryujinx.Tests/Audio
parentfc43aecbbd37a83ebd03f8cfe8fbc033ce2bda7d (diff)
Use SIMD acceleration for audio upsampler (#4410)
* Use SIMD acceleration for audio upsampler filter kernel for a moderate speedup * Address formatting. Implement AVX2 fast path for high quality resampling in ResamplerHelper * now really, are we really getting the benefit of inlining 50+ line methods? * adding unit tests for resampler + upsampler. The upsampler ones fail for some reason * Fixing upsampler test. Apparently this algo only works at specific ratios --------- Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
Diffstat (limited to 'Ryujinx.Tests/Audio')
-rw-r--r--Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs93
-rw-r--r--Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs64
2 files changed, 157 insertions, 0 deletions
diff --git a/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs b/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs
new file mode 100644
index 00000000..364837ee
--- /dev/null
+++ b/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs
@@ -0,0 +1,93 @@
+using NUnit.Framework;
+using Ryujinx.Audio.Renderer.Dsp;
+using Ryujinx.Audio.Renderer.Parameter;
+using Ryujinx.Audio.Renderer.Server.Upsampler;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ryujinx.Tests.Audio.Renderer.Dsp
+{
+ class ResamplerTests
+ {
+ [Test]
+ [TestCase(VoiceInParameter.SampleRateConversionQuality.Low)]
+ [TestCase(VoiceInParameter.SampleRateConversionQuality.Default)]
+ [TestCase(VoiceInParameter.SampleRateConversionQuality.High)]
+ public void TestResamplerConsistencyUpsampling(VoiceInParameter.SampleRateConversionQuality quality)
+ {
+ DoResamplingTest(44100, 48000, quality);
+ }
+
+ [Test]
+ [TestCase(VoiceInParameter.SampleRateConversionQuality.Low)]
+ [TestCase(VoiceInParameter.SampleRateConversionQuality.Default)]
+ [TestCase(VoiceInParameter.SampleRateConversionQuality.High)]
+ public void TestResamplerConsistencyDownsampling(VoiceInParameter.SampleRateConversionQuality quality)
+ {
+ DoResamplingTest(48000, 44100, quality);
+ }
+
+ /// <summary>
+ /// Generates a 1-second sine wave sample at input rate, resamples it to output rate, and
+ /// ensures that it resampled at the expected rate with no discontinuities
+ /// </summary>
+ /// <param name="inputRate">The input sample rate to test</param>
+ /// <param name="outputRate">The output sample rate to test</param>
+ /// <param name="quality">The resampler quality to use</param>
+ private static void DoResamplingTest(int inputRate, int outputRate, VoiceInParameter.SampleRateConversionQuality quality)
+ {
+ float inputSampleRate = (float)inputRate;
+ float outputSampleRate = (float)outputRate;
+ int inputSampleCount = inputRate;
+ int outputSampleCount = outputRate;
+ short[] inputBuffer = new short[inputSampleCount + 100]; // add some safety buffer at the end
+ float[] outputBuffer = new float[outputSampleCount + 100];
+ for (int sample = 0; sample < inputBuffer.Length; sample++)
+ {
+ // 440 hz sine wave with amplitude = 0.5f at input sample rate
+ inputBuffer[sample] = (short)(32767 * MathF.Sin((440 / inputSampleRate) * (float)sample * MathF.PI * 2f) * 0.5f);
+ }
+
+ float fraction = 0;
+
+ ResamplerHelper.Resample(
+ outputBuffer.AsSpan(),
+ inputBuffer.AsSpan(),
+ inputSampleRate / outputSampleRate,
+ ref fraction,
+ outputSampleCount,
+ quality,
+ false);
+
+ float[] expectedOutput = new float[outputSampleCount];
+ float sumDifference = 0;
+ int delay = quality switch
+ {
+ VoiceInParameter.SampleRateConversionQuality.High => 3,
+ VoiceInParameter.SampleRateConversionQuality.Default => 1,
+ _ => 0
+ };
+
+ for (int sample = 0; sample < outputSampleCount; sample++)
+ {
+ outputBuffer[sample] /= 32767;
+ // 440 hz sine wave with amplitude = 0.5f at output sample rate
+ expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (float)(sample + delay) * MathF.PI * 2f) * 0.5f;
+ float thisDelta = Math.Abs(expectedOutput[sample] - outputBuffer[sample]);
+
+ // Ensure no discontinuities
+ Assert.IsTrue(thisDelta < 0.1f);
+ sumDifference += thisDelta;
+ }
+
+ sumDifference = sumDifference / (float)outputSampleCount;
+ // Expect the output to be 99% similar to the expected resampled sine wave
+ Assert.IsTrue(sumDifference < 0.01f);
+ }
+ }
+}
diff --git a/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs b/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs
new file mode 100644
index 00000000..2018752b
--- /dev/null
+++ b/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs
@@ -0,0 +1,64 @@
+using NUnit.Framework;
+using Ryujinx.Audio.Renderer.Dsp;
+using Ryujinx.Audio.Renderer.Parameter;
+using Ryujinx.Audio.Renderer.Server.Upsampler;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ryujinx.Tests.Audio.Renderer.Dsp
+{
+ class UpsamplerTests
+ {
+ [Test]
+ public void TestUpsamplerConsistency()
+ {
+ UpsamplerBufferState bufferState = new UpsamplerBufferState();
+ int inputBlockSize = 160;
+ int numInputSamples = 32000;
+ int numOutputSamples = 48000;
+ float inputSampleRate = numInputSamples;
+ float outputSampleRate = numOutputSamples;
+ float[] inputBuffer = new float[numInputSamples + 100];
+ float[] outputBuffer = new float[numOutputSamples + 100];
+ for (int sample = 0; sample < inputBuffer.Length; sample++)
+ {
+ // 440 hz sine wave with amplitude = 0.5f at input sample rate
+ inputBuffer[sample] = MathF.Sin((440 / inputSampleRate) * (float)sample * MathF.PI * 2f) * 0.5f;
+ }
+
+ int inputIdx = 0;
+ int outputIdx = 0;
+ while (inputIdx + inputBlockSize < numInputSamples)
+ {
+ int outputBufLength = (int)Math.Round((float)(inputIdx + inputBlockSize) * outputSampleRate / inputSampleRate) - outputIdx;
+ UpsamplerHelper.Upsample(
+ outputBuffer.AsSpan(outputIdx),
+ inputBuffer.AsSpan(inputIdx),
+ outputBufLength,
+ inputBlockSize,
+ ref bufferState);
+
+ inputIdx += inputBlockSize;
+ outputIdx += outputBufLength;
+ }
+
+ float[] expectedOutput = new float[numOutputSamples];
+ float sumDifference = 0;
+ for (int sample = 0; sample < numOutputSamples; sample++)
+ {
+ // 440 hz sine wave with amplitude = 0.5f at output sample rate with an offset of 15
+ expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (float)(sample - 15) * MathF.PI * 2f) * 0.5f;
+ sumDifference += Math.Abs(expectedOutput[sample] - outputBuffer[sample]);
+ }
+
+ sumDifference = sumDifference / (float)expectedOutput.Length;
+ // Expect the output to be 98% similar to the expected resampled sine wave
+ Assert.IsTrue(sumDifference < 0.02f);
+ }
+ }
+}