aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Audio/Renderer/Dsp
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2024-05-17 16:46:43 -0300
committerGitHub <noreply@github.com>2024-05-17 16:46:43 -0300
commit4d84df94873a070f6f5c199438f957b24d8cf8a9 (patch)
tree3e4b4cc9585526c63f3a9fdb3a150bfd721a5030 /src/Ryujinx.Audio/Renderer/Dsp
parent9ec8b2c01a0b00f3a33d9a23b9f5ff3758520484 (diff)
Update audio renderer to REV12: Add support for splitter biquad filter (#6813)
* Update audio renderer to REV12: Add support for splitter biquad filter * Formatting * Official names * Update BiquadFilterState size + other fixes * Update tests * Update comment for version 2 * Size test for SplitterDestinationVersion2 * Should use Volume1 if no ramp
Diffstat (limited to 'src/Ryujinx.Audio/Renderer/Dsp')
-rw-r--r--src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs234
-rw-r--r--src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs123
-rw-r--r--src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs4
-rw-r--r--src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs16
-rw-r--r--src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs145
-rw-r--r--src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs (renamed from src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs)6
-rw-r--r--src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs6
7 files changed, 522 insertions, 12 deletions
diff --git a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs
index 1a51a1fb..31f614d6 100644
--- a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs
+++ b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs
@@ -16,10 +16,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// <param name="parameter">The biquad filter parameter</param>
/// <param name="state">The biquad filter state</param>
/// <param name="outputBuffer">The output buffer to write the result</param>
- /// <param name="inputBuffer">The input buffer to write the result</param>
+ /// <param name="inputBuffer">The input buffer to read the samples from</param>
/// <param name="sampleCount">The count of samples to process</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ProcessBiquadFilter(ref BiquadFilterParameter parameter, ref BiquadFilterState state, Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, uint sampleCount)
+ public static void ProcessBiquadFilter(
+ ref BiquadFilterParameter parameter,
+ ref BiquadFilterState state,
+ Span<float> outputBuffer,
+ ReadOnlySpan<float> inputBuffer,
+ uint sampleCount)
{
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
@@ -41,16 +46,111 @@ namespace Ryujinx.Audio.Renderer.Dsp
}
/// <summary>
+ /// Apply a single biquad filter and mix the result into the output buffer.
+ /// </summary>
+ /// <remarks>This is implemented with a direct form 1.</remarks>
+ /// <param name="parameter">The biquad filter parameter</param>
+ /// <param name="state">The biquad filter state</param>
+ /// <param name="outputBuffer">The output buffer to write the result</param>
+ /// <param name="inputBuffer">The input buffer to read the samples from</param>
+ /// <param name="sampleCount">The count of samples to process</param>
+ /// <param name="volume">Mix volume</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ProcessBiquadFilterAndMix(
+ ref BiquadFilterParameter parameter,
+ ref BiquadFilterState state,
+ Span<float> outputBuffer,
+ ReadOnlySpan<float> inputBuffer,
+ uint sampleCount,
+ float volume)
+ {
+ float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
+ float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
+ float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
+
+ float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
+ float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
+
+ for (int i = 0; i < sampleCount; i++)
+ {
+ float input = inputBuffer[i];
+ float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2;
+
+ state.State1 = state.State0;
+ state.State0 = input;
+ state.State3 = state.State2;
+ state.State2 = output;
+
+ outputBuffer[i] += FloatingPointHelper.MultiplyRoundUp(output, volume);
+ }
+ }
+
+ /// <summary>
+ /// Apply a single biquad filter and mix the result into the output buffer with volume ramp.
+ /// </summary>
+ /// <remarks>This is implemented with a direct form 1.</remarks>
+ /// <param name="parameter">The biquad filter parameter</param>
+ /// <param name="state">The biquad filter state</param>
+ /// <param name="outputBuffer">The output buffer to write the result</param>
+ /// <param name="inputBuffer">The input buffer to read the samples from</param>
+ /// <param name="sampleCount">The count of samples to process</param>
+ /// <param name="volume">Initial mix volume</param>
+ /// <param name="ramp">Volume increment step</param>
+ /// <returns>Last filtered sample value</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float ProcessBiquadFilterAndMixRamp(
+ ref BiquadFilterParameter parameter,
+ ref BiquadFilterState state,
+ Span<float> outputBuffer,
+ ReadOnlySpan<float> inputBuffer,
+ uint sampleCount,
+ float volume,
+ float ramp)
+ {
+ float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
+ float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
+ float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
+
+ float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
+ float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
+
+ float mixState = 0f;
+
+ for (int i = 0; i < sampleCount; i++)
+ {
+ float input = inputBuffer[i];
+ float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2;
+
+ state.State1 = state.State0;
+ state.State0 = input;
+ state.State3 = state.State2;
+ state.State2 = output;
+
+ mixState = FloatingPointHelper.MultiplyRoundUp(output, volume);
+
+ outputBuffer[i] += mixState;
+ volume += ramp;
+ }
+
+ return mixState;
+ }
+
+ /// <summary>
/// Apply multiple biquad filter.
/// </summary>
/// <remarks>This is implemented with a direct form 1.</remarks>
/// <param name="parameters">The biquad filter parameter</param>
/// <param name="states">The biquad filter state</param>
/// <param name="outputBuffer">The output buffer to write the result</param>
- /// <param name="inputBuffer">The input buffer to write the result</param>
+ /// <param name="inputBuffer">The input buffer to read the samples from</param>
/// <param name="sampleCount">The count of samples to process</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ProcessBiquadFilter(ReadOnlySpan<BiquadFilterParameter> parameters, Span<BiquadFilterState> states, Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, uint sampleCount)
+ public static void ProcessBiquadFilter(
+ ReadOnlySpan<BiquadFilterParameter> parameters,
+ Span<BiquadFilterState> states,
+ Span<float> outputBuffer,
+ ReadOnlySpan<float> inputBuffer,
+ uint sampleCount)
{
for (int stageIndex = 0; stageIndex < parameters.Length; stageIndex++)
{
@@ -67,7 +167,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
for (int i = 0; i < sampleCount; i++)
{
- float input = inputBuffer[i];
+ float input = stageIndex != 0 ? outputBuffer[i] : inputBuffer[i];
float output = input * a0 + state.State0 * a1 + state.State1 * a2 + state.State2 * b1 + state.State3 * b2;
state.State1 = state.State0;
@@ -79,5 +179,129 @@ namespace Ryujinx.Audio.Renderer.Dsp
}
}
}
+
+ /// <summary>
+ /// Apply double biquad filter and mix the result into the output buffer.
+ /// </summary>
+ /// <remarks>This is implemented with a direct form 1.</remarks>
+ /// <param name="parameters">The biquad filter parameter</param>
+ /// <param name="states">The biquad filter state</param>
+ /// <param name="outputBuffer">The output buffer to write the result</param>
+ /// <param name="inputBuffer">The input buffer to read the samples from</param>
+ /// <param name="sampleCount">The count of samples to process</param>
+ /// <param name="volume">Mix volume</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ProcessDoubleBiquadFilterAndMix(
+ ref BiquadFilterParameter parameter0,
+ ref BiquadFilterParameter parameter1,
+ ref BiquadFilterState state0,
+ ref BiquadFilterState state1,
+ Span<float> outputBuffer,
+ ReadOnlySpan<float> inputBuffer,
+ uint sampleCount,
+ float volume)
+ {
+ float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter);
+ float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter);
+ float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter);
+
+ float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter);
+ float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter);
+
+ float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter);
+ float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter);
+ float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter);
+
+ float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter);
+ float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter);
+
+ for (int i = 0; i < sampleCount; i++)
+ {
+ float input = inputBuffer[i];
+ float output = input * a00 + state0.State0 * a10 + state0.State1 * a20 + state0.State2 * b10 + state0.State3 * b20;
+
+ state0.State1 = state0.State0;
+ state0.State0 = input;
+ state0.State3 = state0.State2;
+ state0.State2 = output;
+
+ input = output;
+ output = input * a01 + state1.State0 * a11 + state1.State1 * a21 + state1.State2 * b11 + state1.State3 * b21;
+
+ state1.State1 = state1.State0;
+ state1.State0 = input;
+ state1.State3 = state1.State2;
+ state1.State2 = output;
+
+ outputBuffer[i] += FloatingPointHelper.MultiplyRoundUp(output, volume);
+ }
+ }
+
+ /// <summary>
+ /// Apply double biquad filter and mix the result into the output buffer with volume ramp.
+ /// </summary>
+ /// <remarks>This is implemented with a direct form 1.</remarks>
+ /// <param name="parameters">The biquad filter parameter</param>
+ /// <param name="states">The biquad filter state</param>
+ /// <param name="outputBuffer">The output buffer to write the result</param>
+ /// <param name="inputBuffer">The input buffer to read the samples from</param>
+ /// <param name="sampleCount">The count of samples to process</param>
+ /// <param name="volume">Initial mix volume</param>
+ /// <param name="ramp">Volume increment step</param>
+ /// <returns>Last filtered sample value</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float ProcessDoubleBiquadFilterAndMixRamp(
+ ref BiquadFilterParameter parameter0,
+ ref BiquadFilterParameter parameter1,
+ ref BiquadFilterState state0,
+ ref BiquadFilterState state1,
+ Span<float> outputBuffer,
+ ReadOnlySpan<float> inputBuffer,
+ uint sampleCount,
+ float volume,
+ float ramp)
+ {
+ float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter);
+ float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter);
+ float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter);
+
+ float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter);
+ float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter);
+
+ float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter);
+ float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter);
+ float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter);
+
+ float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter);
+ float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter);
+
+ float mixState = 0f;
+
+ for (int i = 0; i < sampleCount; i++)
+ {
+ float input = inputBuffer[i];
+ float output = input * a00 + state0.State0 * a10 + state0.State1 * a20 + state0.State2 * b10 + state0.State3 * b20;
+
+ state0.State1 = state0.State0;
+ state0.State0 = input;
+ state0.State3 = state0.State2;
+ state0.State2 = output;
+
+ input = output;
+ output = input * a01 + state1.State0 * a11 + state1.State1 * a21 + state1.State2 * b11 + state1.State3 * b21;
+
+ state1.State1 = state1.State0;
+ state1.State0 = input;
+ state1.State3 = state1.State2;
+ state1.State2 = output;
+
+ mixState = FloatingPointHelper.MultiplyRoundUp(output, volume);
+
+ outputBuffer[i] += mixState;
+ volume += ramp;
+ }
+
+ return mixState;
+ }
}
}
diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs
new file mode 100644
index 00000000..106fc035
--- /dev/null
+++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs
@@ -0,0 +1,123 @@
+using Ryujinx.Audio.Renderer.Common;
+using Ryujinx.Audio.Renderer.Dsp.State;
+using Ryujinx.Audio.Renderer.Parameter;
+using System;
+
+namespace Ryujinx.Audio.Renderer.Dsp.Command
+{
+ public class BiquadFilterAndMixCommand : ICommand
+ {
+ public bool Enabled { get; set; }
+
+ public int NodeId { get; }
+
+ public CommandType CommandType => CommandType.BiquadFilterAndMix;
+
+ public uint EstimatedProcessingTime { get; set; }
+
+ public ushort InputBufferIndex { get; }
+ public ushort OutputBufferIndex { get; }
+
+ private BiquadFilterParameter _parameter;
+
+ public Memory<BiquadFilterState> BiquadFilterState { get; }
+ public Memory<BiquadFilterState> PreviousBiquadFilterState { get; }
+
+ public Memory<VoiceUpdateState> State { get; }
+
+ public int LastSampleIndex { get; }
+
+ public float Volume0 { get; }
+ public float Volume1 { get; }
+
+ public bool NeedInitialization { get; }
+ public bool HasVolumeRamp { get; }
+ public bool IsFirstMixBuffer { get; }
+
+ public BiquadFilterAndMixCommand(
+ float volume0,
+ float volume1,
+ uint inputBufferIndex,
+ uint outputBufferIndex,
+ int lastSampleIndex,
+ Memory<VoiceUpdateState> state,
+ ref BiquadFilterParameter filter,
+ Memory<BiquadFilterState> biquadFilterState,
+ Memory<BiquadFilterState> previousBiquadFilterState,
+ bool needInitialization,
+ bool hasVolumeRamp,
+ bool isFirstMixBuffer,
+ int nodeId)
+ {
+ Enabled = true;
+ NodeId = nodeId;
+
+ InputBufferIndex = (ushort)inputBufferIndex;
+ OutputBufferIndex = (ushort)outputBufferIndex;
+
+ _parameter = filter;
+ BiquadFilterState = biquadFilterState;
+ PreviousBiquadFilterState = previousBiquadFilterState;
+
+ State = state;
+ LastSampleIndex = lastSampleIndex;
+
+ Volume0 = volume0;
+ Volume1 = volume1;
+
+ NeedInitialization = needInitialization;
+ HasVolumeRamp = hasVolumeRamp;
+ IsFirstMixBuffer = isFirstMixBuffer;
+ }
+
+ public void Process(CommandList context)
+ {
+ ReadOnlySpan<float> inputBuffer = context.GetBuffer(InputBufferIndex);
+ Span<float> outputBuffer = context.GetBuffer(OutputBufferIndex);
+
+ if (NeedInitialization)
+ {
+ // If there is no previous state, initialize to zero.
+
+ BiquadFilterState.Span[0] = new BiquadFilterState();
+ }
+ else if (IsFirstMixBuffer)
+ {
+ // This is the first buffer, set previous state to current state.
+
+ PreviousBiquadFilterState.Span[0] = BiquadFilterState.Span[0];
+ }
+ else
+ {
+ // Rewind the current state by copying back the previous state.
+
+ BiquadFilterState.Span[0] = PreviousBiquadFilterState.Span[0];
+ }
+
+ if (HasVolumeRamp)
+ {
+ float volume = Volume0;
+ float ramp = (Volume1 - Volume0) / (int)context.SampleCount;
+
+ State.Span[0].LastSamples[LastSampleIndex] = BiquadFilterHelper.ProcessBiquadFilterAndMixRamp(
+ ref _parameter,
+ ref BiquadFilterState.Span[0],
+ outputBuffer,
+ inputBuffer,
+ context.SampleCount,
+ volume,
+ ramp);
+ }
+ else
+ {
+ BiquadFilterHelper.ProcessBiquadFilterAndMix(
+ ref _parameter,
+ ref BiquadFilterState.Span[0],
+ outputBuffer,
+ inputBuffer,
+ context.SampleCount,
+ Volume1);
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs
index 098a04a0..de5c0ea2 100644
--- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs
+++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs
@@ -30,8 +30,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
CopyMixBuffer,
LimiterVersion1,
LimiterVersion2,
- GroupedBiquadFilter,
+ MultiTapBiquadFilter,
CaptureBuffer,
Compressor,
+ BiquadFilterAndMix,
+ MultiTapBiquadFilterAndMix,
}
}
diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs
index 3c7dd63b..41ac84c1 100644
--- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs
+++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs
@@ -24,7 +24,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public Memory<VoiceUpdateState> State { get; }
- public MixRampGroupedCommand(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, Span<float> volume0, Span<float> volume1, Memory<VoiceUpdateState> state, int nodeId)
+ public MixRampGroupedCommand(
+ uint mixBufferCount,
+ uint inputBufferIndex,
+ uint outputBufferIndex,
+ ReadOnlySpan<float> volume0,
+ ReadOnlySpan<float> volume1,
+ Memory<VoiceUpdateState> state,
+ int nodeId)
{
Enabled = true;
MixBufferCount = mixBufferCount;
@@ -48,7 +55,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static float ProcessMixRampGrouped(Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, float volume0, float volume1, int sampleCount)
+ private static float ProcessMixRampGrouped(
+ Span<float> outputBuffer,
+ ReadOnlySpan<float> inputBuffer,
+ float volume0,
+ float volume1,
+ int sampleCount)
{
float ramp = (volume1 - volume0) / sampleCount;
float volume = volume0;
diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs
new file mode 100644
index 00000000..e359371b
--- /dev/null
+++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs
@@ -0,0 +1,145 @@
+using Ryujinx.Audio.Renderer.Common;
+using Ryujinx.Audio.Renderer.Dsp.State;
+using Ryujinx.Audio.Renderer.Parameter;
+using System;
+
+namespace Ryujinx.Audio.Renderer.Dsp.Command
+{
+ public class MultiTapBiquadFilterAndMixCommand : ICommand
+ {
+ public bool Enabled { get; set; }
+
+ public int NodeId { get; }
+
+ public CommandType CommandType => CommandType.MultiTapBiquadFilterAndMix;
+
+ public uint EstimatedProcessingTime { get; set; }
+
+ public ushort InputBufferIndex { get; }
+ public ushort OutputBufferIndex { get; }
+
+ private BiquadFilterParameter _parameter0;
+ private BiquadFilterParameter _parameter1;
+
+ public Memory<BiquadFilterState> BiquadFilterState0 { get; }
+ public Memory<BiquadFilterState> BiquadFilterState1 { get; }
+ public Memory<BiquadFilterState> PreviousBiquadFilterState0 { get; }
+ public Memory<BiquadFilterState> PreviousBiquadFilterState1 { get; }
+
+ public Memory<VoiceUpdateState> State { get; }
+
+ public int LastSampleIndex { get; }
+
+ public float Volume0 { get; }
+ public float Volume1 { get; }
+
+ public bool NeedInitialization0 { get; }
+ public bool NeedInitialization1 { get; }
+ public bool HasVolumeRamp { get; }
+ public bool IsFirstMixBuffer { get; }
+
+ public MultiTapBiquadFilterAndMixCommand(
+ float volume0,
+ float volume1,
+ uint inputBufferIndex,
+ uint outputBufferIndex,
+ int lastSampleIndex,
+ Memory<VoiceUpdateState> state,
+ ref BiquadFilterParameter filter0,
+ ref BiquadFilterParameter filter1,
+ Memory<BiquadFilterState> biquadFilterState0,
+ Memory<BiquadFilterState> biquadFilterState1,
+ Memory<BiquadFilterState> previousBiquadFilterState0,
+ Memory<BiquadFilterState> previousBiquadFilterState1,
+ bool needInitialization0,
+ bool needInitialization1,
+ bool hasVolumeRamp,
+ bool isFirstMixBuffer,
+ int nodeId)
+ {
+ Enabled = true;
+ NodeId = nodeId;
+
+ InputBufferIndex = (ushort)inputBufferIndex;
+ OutputBufferIndex = (ushort)outputBufferIndex;
+
+ _parameter0 = filter0;
+ _parameter1 = filter1;
+ BiquadFilterState0 = biquadFilterState0;
+ BiquadFilterState1 = biquadFilterState1;
+ PreviousBiquadFilterState0 = previousBiquadFilterState0;
+ PreviousBiquadFilterState1 = previousBiquadFilterState1;
+
+ State = state;
+ LastSampleIndex = lastSampleIndex;
+
+ Volume0 = volume0;
+ Volume1 = volume1;
+
+ NeedInitialization0 = needInitialization0;
+ NeedInitialization1 = needInitialization1;
+ HasVolumeRamp = hasVolumeRamp;
+ IsFirstMixBuffer = isFirstMixBuffer;
+ }
+
+ private void UpdateState(Memory<BiquadFilterState> state, Memory<BiquadFilterState> previousState, bool needInitialization)
+ {
+ if (needInitialization)
+ {
+ // If there is no previous state, initialize to zero.
+
+ state.Span[0] = new BiquadFilterState();
+ }
+ else if (IsFirstMixBuffer)
+ {
+ // This is the first buffer, set previous state to current state.
+
+ previousState.Span[0] = state.Span[0];
+ }
+ else
+ {
+ // Rewind the current state by copying back the previous state.
+
+ state.Span[0] = previousState.Span[0];
+ }
+ }
+
+ public void Process(CommandList context)
+ {
+ ReadOnlySpan<float> inputBuffer = context.GetBuffer(InputBufferIndex);
+ Span<float> outputBuffer = context.GetBuffer(OutputBufferIndex);
+
+ UpdateState(BiquadFilterState0, PreviousBiquadFilterState0, NeedInitialization0);
+ UpdateState(BiquadFilterState1, PreviousBiquadFilterState1, NeedInitialization1);
+
+ if (HasVolumeRamp)
+ {
+ float volume = Volume0;
+ float ramp = (Volume1 - Volume0) / (int)context.SampleCount;
+
+ State.Span[0].LastSamples[LastSampleIndex] = BiquadFilterHelper.ProcessDoubleBiquadFilterAndMixRamp(
+ ref _parameter0,
+ ref _parameter1,
+ ref BiquadFilterState0.Span[0],
+ ref BiquadFilterState1.Span[0],
+ outputBuffer,
+ inputBuffer,
+ context.SampleCount,
+ volume,
+ ramp);
+ }
+ else
+ {
+ BiquadFilterHelper.ProcessDoubleBiquadFilterAndMix(
+ ref _parameter0,
+ ref _parameter1,
+ ref BiquadFilterState0.Span[0],
+ ref BiquadFilterState1.Span[0],
+ outputBuffer,
+ inputBuffer,
+ context.SampleCount,
+ Volume1);
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs
index 7af851bd..e159f8ef 100644
--- a/src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs
+++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs
@@ -4,13 +4,13 @@ using System;
namespace Ryujinx.Audio.Renderer.Dsp.Command
{
- public class GroupedBiquadFilterCommand : ICommand
+ public class MultiTapBiquadFilterCommand : ICommand
{
public bool Enabled { get; set; }
public int NodeId { get; }
- public CommandType CommandType => CommandType.GroupedBiquadFilter;
+ public CommandType CommandType => CommandType.MultiTapBiquadFilter;
public uint EstimatedProcessingTime { get; set; }
@@ -20,7 +20,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
private readonly int _outputBufferIndex;
private readonly bool[] _isInitialized;
- public GroupedBiquadFilterCommand(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
+ public MultiTapBiquadFilterCommand(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
{
_parameters = filters.ToArray();
_biquadFilterStates = biquadFilterStateMemory;
diff --git a/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs
index f9a32b3f..58a2d9cc 100644
--- a/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs
+++ b/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs
@@ -2,12 +2,16 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Dsp.State
{
- [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
+ [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x20)]
public struct BiquadFilterState
{
public float State0;
public float State1;
public float State2;
public float State3;
+ public float State4;
+ public float State5;
+ public float State6;
+ public float State7;
}
}