diff options
author | Mary <mary@mary.zone> | 2022-04-08 10:52:18 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-08 10:52:18 +0200 |
commit | d04ba51bb0456ee8f778fe86fc224665b0fb20c8 (patch) | |
tree | 7dd94a8c321cb64df7016065eeb30a345d3aa4b7 /Ryujinx.Audio/Renderer/Dsp | |
parent | 55ee26136363338d1cfffabd4cca07c9ca9b7847 (diff) |
amadeus: Improve and fix delay effect processing (#3205)1.1.94
* amadeus: Improve and fix delay effect processing
This rework the delay effect processing by representing calculation with the appropriate matrix and by unrolling some loop in the code.
This allows better optimization by the JIT while making it more readeable.
Also fix a bug in the Surround code path found while looking back at my notes.
* Remove useless GetHashCode
* Address gdkchan's comments
Diffstat (limited to 'Ryujinx.Audio/Renderer/Dsp')
-rw-r--r-- | Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs | 157 | ||||
-rw-r--r-- | Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs | 13 |
2 files changed, 99 insertions, 71 deletions
diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs index 8d416cc3..186efb6a 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs @@ -18,8 +18,10 @@ using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Server.Effect; +using Ryujinx.Audio.Renderer.Utils.Math; using System; using System.Diagnostics; +using System.Numerics; using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Renderer.Dsp.Command @@ -70,7 +72,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void ProcessDelayMono(ref DelayState state, float* outputBuffer, float* inputBuffer, uint sampleCount) { float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision); @@ -83,133 +85,148 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command float input = inputBuffer[i] * 64; float delayLineValue = state.DelayLines[0].Read(); - float lowPassResult = (input * inGain + delayLineValue * feedbackGain) * state.LowPassBaseGain + state.LowPassZ[0] * state.LowPassFeedbackGain; + float temp = input * inGain + delayLineValue * feedbackGain; - state.LowPassZ[0] = lowPassResult; - - state.DelayLines[0].Update(lowPassResult); + state.UpdateLowPassFilter(ref temp, 1); outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64; } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void ProcessDelayStereo(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { const ushort channelCount = 2; - Span<float> channelInput = stackalloc float[channelCount]; - Span<float> delayLineValues = stackalloc float[channelCount]; - Span<float> temp = stackalloc float[channelCount]; - float delayFeedbackBaseGain = state.DelayFeedbackBaseGain; float delayFeedbackCrossGain = state.DelayFeedbackCrossGain; float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision); float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision); float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision); + Matrix2x2 delayFeedback = new Matrix2x2(delayFeedbackBaseGain , delayFeedbackCrossGain, + delayFeedbackCrossGain, delayFeedbackBaseGain); + for (int i = 0; i < sampleCount; i++) { - for (int j = 0; j < channelCount; j++) + Vector2 channelInput = new Vector2 { - channelInput[j] = *((float*)inputBuffers[j] + i) * 64; - delayLineValues[j] = state.DelayLines[j].Read(); - } - - temp[0] = channelInput[0] * inGain + delayLineValues[1] * delayFeedbackCrossGain + delayLineValues[0] * delayFeedbackBaseGain; - temp[1] = channelInput[1] * inGain + delayLineValues[0] * delayFeedbackCrossGain + delayLineValues[1] * delayFeedbackBaseGain; + X = *((float*)inputBuffers[0] + i) * 64, + Y = *((float*)inputBuffers[1] + i) * 64, + }; - for (int j = 0; j < channelCount; j++) + Vector2 delayLineValues = new Vector2() { - float lowPassResult = state.LowPassFeedbackGain * state.LowPassZ[j] + temp[j] * state.LowPassBaseGain; + X = state.DelayLines[0].Read(), + Y = state.DelayLines[1].Read(), + }; - state.LowPassZ[j] = lowPassResult; - state.DelayLines[j].Update(lowPassResult); + Vector2 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain; - *((float*)outputBuffers[j] + i) = (channelInput[j] * dryGain + delayLineValues[j] * outGain) / 64; - } + state.UpdateLowPassFilter(ref Unsafe.As<Vector2, float>(ref temp), channelCount); + + *((float*)outputBuffers[0] + i) = (channelInput.X * dryGain + delayLineValues.X * outGain) / 64; + *((float*)outputBuffers[1] + i) = (channelInput.Y * dryGain + delayLineValues.Y * outGain) / 64; } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void ProcessDelayQuadraphonic(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { const ushort channelCount = 4; - Span<float> channelInput = stackalloc float[channelCount]; - Span<float> delayLineValues = stackalloc float[channelCount]; - Span<float> temp = stackalloc float[channelCount]; - float delayFeedbackBaseGain = state.DelayFeedbackBaseGain; float delayFeedbackCrossGain = state.DelayFeedbackCrossGain; float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision); float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision); float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision); + Matrix4x4 delayFeedback = new Matrix4x4(delayFeedbackBaseGain , delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, + delayFeedbackCrossGain, delayFeedbackBaseGain , 0.0f , delayFeedbackCrossGain, + delayFeedbackCrossGain, 0.0f , delayFeedbackBaseGain , delayFeedbackCrossGain, + 0.0f , delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain); + + for (int i = 0; i < sampleCount; i++) { - for (int j = 0; j < channelCount; j++) + Vector4 channelInput = new Vector4 { - channelInput[j] = *((float*)inputBuffers[j] + i) * 64; - delayLineValues[j] = state.DelayLines[j].Read(); - } - - temp[0] = channelInput[0] * inGain + (delayLineValues[2] + delayLineValues[1]) * delayFeedbackCrossGain + delayLineValues[0] * delayFeedbackBaseGain; - temp[1] = channelInput[1] * inGain + (delayLineValues[0] + delayLineValues[3]) * delayFeedbackCrossGain + delayLineValues[1] * delayFeedbackBaseGain; - temp[2] = channelInput[2] * inGain + (delayLineValues[3] + delayLineValues[0]) * delayFeedbackCrossGain + delayLineValues[2] * delayFeedbackBaseGain; - temp[3] = channelInput[3] * inGain + (delayLineValues[1] + delayLineValues[2]) * delayFeedbackCrossGain + delayLineValues[3] * delayFeedbackBaseGain; + X = *((float*)inputBuffers[0] + i) * 64, + Y = *((float*)inputBuffers[1] + i) * 64, + Z = *((float*)inputBuffers[2] + i) * 64, + W = *((float*)inputBuffers[3] + i) * 64 + }; - for (int j = 0; j < channelCount; j++) + Vector4 delayLineValues = new Vector4() { - float lowPassResult = state.LowPassFeedbackGain * state.LowPassZ[j] + temp[j] * state.LowPassBaseGain; - - state.LowPassZ[j] = lowPassResult; - state.DelayLines[j].Update(lowPassResult); - - *((float*)outputBuffers[j] + i) = (channelInput[j] * dryGain + delayLineValues[j] * outGain) / 64; - } + X = state.DelayLines[0].Read(), + Y = state.DelayLines[1].Read(), + Z = state.DelayLines[2].Read(), + W = state.DelayLines[3].Read() + }; + + Vector4 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain; + + state.UpdateLowPassFilter(ref Unsafe.As<Vector4, float>(ref temp), channelCount); + + *((float*)outputBuffers[0] + i) = (channelInput.X * dryGain + delayLineValues.X * outGain) / 64; + *((float*)outputBuffers[1] + i) = (channelInput.Y * dryGain + delayLineValues.Y * outGain) / 64; + *((float*)outputBuffers[2] + i) = (channelInput.Z * dryGain + delayLineValues.Z * outGain) / 64; + *((float*)outputBuffers[3] + i) = (channelInput.W * dryGain + delayLineValues.W * outGain) / 64; } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void ProcessDelaySurround(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { const ushort channelCount = 6; - Span<float> channelInput = stackalloc float[channelCount]; - Span<float> delayLineValues = stackalloc float[channelCount]; - Span<float> temp = stackalloc float[channelCount]; - + float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision); float delayFeedbackBaseGain = state.DelayFeedbackBaseGain; float delayFeedbackCrossGain = state.DelayFeedbackCrossGain; float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision); float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision); float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision); + Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain , 0.0f , 0.0f , 0.0f , delayFeedbackCrossGain, delayFeedbackCrossGain, + 0.0f , delayFeedbackBaseGain , 0.0f , delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f , + delayFeedbackCrossGain, 0.0f , delayFeedbackBaseGain , delayFeedbackCrossGain, 0.0f , 0.0f , + 0.0f , delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain , 0.0f , 0.0f , + delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f , 0.0f , delayFeedbackBaseGain , 0.0f , + 0.0f , 0.0f , 0.0f , 0.0f , 0.0f , feedbackGain); + for (int i = 0; i < sampleCount; i++) { - for (int j = 0; j < channelCount; j++) + Vector6 channelInput = new Vector6 { - channelInput[j] = *((float*)inputBuffers[j] + i) * 64; - delayLineValues[j] = state.DelayLines[j].Read(); - } - - temp[0] = channelInput[0] * inGain + (delayLineValues[2] + delayLineValues[4]) * delayFeedbackCrossGain + delayLineValues[0] * delayFeedbackBaseGain; - temp[1] = channelInput[1] * inGain + (delayLineValues[4] + delayLineValues[3]) * delayFeedbackCrossGain + delayLineValues[1] * delayFeedbackBaseGain; - temp[2] = channelInput[2] * inGain + (delayLineValues[3] + delayLineValues[0]) * delayFeedbackCrossGain + delayLineValues[2] * delayFeedbackBaseGain; - temp[3] = channelInput[3] * inGain + (delayLineValues[1] + delayLineValues[2]) * delayFeedbackCrossGain + delayLineValues[3] * delayFeedbackBaseGain; - temp[4] = channelInput[4] * inGain + (delayLineValues[0] + delayLineValues[1]) * delayFeedbackCrossGain + delayLineValues[4] * delayFeedbackBaseGain; - temp[5] = channelInput[5] * inGain + delayLineValues[5] * delayFeedbackBaseGain; - - for (int j = 0; j < channelCount; j++) + X = *((float*)inputBuffers[0] + i) * 64, + Y = *((float*)inputBuffers[1] + i) * 64, + Z = *((float*)inputBuffers[2] + i) * 64, + W = *((float*)inputBuffers[3] + i) * 64, + V = *((float*)inputBuffers[4] + i) * 64, + U = *((float*)inputBuffers[5] + i) * 64 + }; + + Vector6 delayLineValues = new Vector6 { - float lowPassResult = state.LowPassFeedbackGain * state.LowPassZ[j] + temp[j] * state.LowPassBaseGain; - - state.LowPassZ[j] = lowPassResult; - state.DelayLines[j].Update(lowPassResult); - - *((float*)outputBuffers[j] + i) = (channelInput[j] * dryGain + delayLineValues[j] * outGain) / 64; - } + X = state.DelayLines[0].Read(), + Y = state.DelayLines[1].Read(), + Z = state.DelayLines[2].Read(), + W = state.DelayLines[3].Read(), + V = state.DelayLines[4].Read(), + U = state.DelayLines[5].Read() + }; + + Vector6 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain; + + state.UpdateLowPassFilter(ref Unsafe.As<Vector6, float>(ref temp), channelCount); + + *((float*)outputBuffers[0] + i) = (channelInput.X * dryGain + delayLineValues.X * outGain) / 64; + *((float*)outputBuffers[1] + i) = (channelInput.Y * dryGain + delayLineValues.Y * outGain) / 64; + *((float*)outputBuffers[2] + i) = (channelInput.Z * dryGain + delayLineValues.Z * outGain) / 64; + *((float*)outputBuffers[3] + i) = (channelInput.W * dryGain + delayLineValues.W * outGain) / 64; + *((float*)outputBuffers[4] + i) = (channelInput.V * dryGain + delayLineValues.V * outGain) / 64; + *((float*)outputBuffers[5] + i) = (channelInput.U * dryGain + delayLineValues.U * outGain) / 64; } } diff --git a/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs b/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs index 7b694fb0..21ffbebd 100644 --- a/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs +++ b/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs @@ -17,6 +17,7 @@ using Ryujinx.Audio.Renderer.Dsp.Effect; using Ryujinx.Audio.Renderer.Parameter.Effect; +using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Renderer.Dsp.State { @@ -43,7 +44,6 @@ namespace Ryujinx.Audio.Renderer.Dsp.State { DelayLines[i] = new DelayLine(sampleRate, parameter.DelayTimeMax); DelayLines[i].SetDelay(parameter.DelayTime); - LowPassZ[0] = 0; } UpdateParameter(ref parameter); @@ -69,5 +69,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.State LowPassFeedbackGain = 0.95f * FixedPointHelper.ToFloat(parameter.LowPassAmount, FixedPointPrecision); LowPassBaseGain = 1.0f - LowPassFeedbackGain; } + + public void UpdateLowPassFilter(ref float tempRawRef, uint channelCount) + { + for (int i = 0; i < channelCount; i++) + { + float lowPassResult = LowPassFeedbackGain * LowPassZ[i] + Unsafe.Add(ref tempRawRef, i) * LowPassBaseGain; + + LowPassZ[i] = lowPassResult; + DelayLines[i].Update(lowPassResult); + } + } } } |