diff options
| author | Logan Stromberg <loganstromberg@gmail.com> | 2023-02-21 02:44:57 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-02-21 11:44:57 +0100 |
| commit | edfd4d70c0f38d41c6ebb31508127b14727017bd (patch) | |
| tree | cf379010089a72cf1b33387e4ce664f51c8557d1 /Ryujinx.Tests/Audio | |
| parent | fc43aecbbd37a83ebd03f8cfe8fbc033ce2bda7d (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.cs | 93 | ||||
| -rw-r--r-- | Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs | 64 |
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); + } + } +} |
