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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
CopyMixBuffer,
|
||||
LimiterVersion1,
|
||||
LimiterVersion2,
|
||||
GroupedBiquadFilter,
|
||||
MultiTapBiquadFilter,
|
||||
CaptureBuffer,
|
||||
Compressor,
|
||||
BiquadFilterAndMix,
|
||||
MultiTapBiquadFilterAndMix,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user