aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Audio/Renderer/Common/EffectType.cs7
-rw-r--r--Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs3
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs4
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs160
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs179
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs46
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs2
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs2
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs2
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs6
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs155
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs48
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs6
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs6
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs (renamed from Ryujinx.Audio/Renderer/Parameter/EffectInParameter.cs)21
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs114
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs40
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs (renamed from Ryujinx.Audio/Renderer/Parameter/EffectOutStatus.cs)29
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs43
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/EffectState.cs35
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs70
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs30
-rw-r--r--Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs7
-rw-r--r--Ryujinx.Audio/Renderer/Server/BehaviourContext.cs19
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandBuffer.cs43
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandGenerator.cs25
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs10
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs10
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs123
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs12
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs46
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs12
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs12
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs12
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs60
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs112
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs12
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs12
-rw-r--r--Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs2
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs1
-rw-r--r--Ryujinx.Audio/Renderer/Server/StateUpdater.cs83
-rw-r--r--Ryujinx.Tests/Audio/Renderer/EffectInfoParameterTests.cs3
-rw-r--r--Ryujinx.Tests/Audio/Renderer/EffectOutStatusTests.cs3
-rw-r--r--Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs16
-rw-r--r--Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs16
45 files changed, 1591 insertions, 68 deletions
diff --git a/Ryujinx.Audio/Renderer/Common/EffectType.cs b/Ryujinx.Audio/Renderer/Common/EffectType.cs
index 082f94f8..daa22036 100644
--- a/Ryujinx.Audio/Renderer/Common/EffectType.cs
+++ b/Ryujinx.Audio/Renderer/Common/EffectType.cs
@@ -55,6 +55,11 @@ namespace Ryujinx.Audio.Renderer.Common
/// <summary>
/// Effect applying a biquad filter.
/// </summary>
- BiquadFilter
+ BiquadFilter,
+
+ /// <summary>
+ /// Effect applying a limiter (DRC).
+ /// </summary>
+ Limiter,
}
}
diff --git a/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs b/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs
index a92bd0cc..e9e946ce 100644
--- a/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs
+++ b/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs
@@ -29,6 +29,7 @@ namespace Ryujinx.Audio.Renderer.Common
Aux,
Reverb,
Reverb3d,
- PcmFloat
+ PcmFloat,
+ Limiter
}
}
diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs
index 8ff1c581..997a080e 100644
--- a/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs
@@ -44,6 +44,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
Reverb3d,
Performance,
ClearMixBuffer,
- CopyMixBuffer
+ CopyMixBuffer,
+ LimiterVersion1,
+ LimiterVersion2
}
}
diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs b/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs
new file mode 100644
index 00000000..975e61f9
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs
@@ -0,0 +1,160 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using Ryujinx.Audio.Renderer.Dsp.State;
+using Ryujinx.Audio.Renderer.Parameter.Effect;
+using System;
+using System.Diagnostics;
+
+namespace Ryujinx.Audio.Renderer.Dsp.Command
+{
+ public class LimiterCommandVersion1 : ICommand
+ {
+ public bool Enabled { get; set; }
+
+ public int NodeId { get; }
+
+ public CommandType CommandType => CommandType.LimiterVersion1;
+
+ public ulong EstimatedProcessingTime { get; set; }
+
+ public LimiterParameter Parameter => _parameter;
+ public Memory<LimiterState> State { get; }
+ public ulong WorkBuffer { get; }
+ public ushort[] OutputBufferIndices { get; }
+ public ushort[] InputBufferIndices { get; }
+ public bool IsEffectEnabled { get; }
+
+ private LimiterParameter _parameter;
+
+ public LimiterCommandVersion1(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, bool isEnabled, ulong workBuffer, int nodeId)
+ {
+ Enabled = true;
+ NodeId = nodeId;
+ _parameter = parameter;
+ State = state;
+ WorkBuffer = workBuffer;
+
+ IsEffectEnabled = isEnabled;
+
+ InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
+ OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
+
+ for (int i = 0; i < Parameter.ChannelCount; i++)
+ {
+ InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
+ OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
+ }
+ }
+
+ public void Process(CommandList context)
+ {
+ ref LimiterState state = ref State.Span[0];
+
+ if (IsEffectEnabled)
+ {
+ if (Parameter.Status == Server.Effect.UsageState.Invalid)
+ {
+ state = new LimiterState(ref _parameter, WorkBuffer);
+ }
+ else if (Parameter.Status == Server.Effect.UsageState.New)
+ {
+ state.UpdateParameter(ref _parameter);
+ }
+ }
+
+ ProcessLimiter(context);
+ }
+
+ private void ProcessLimiter(CommandList context)
+ {
+ Debug.Assert(Parameter.IsChannelCountValid());
+
+ if (IsEffectEnabled && Parameter.IsChannelCountValid())
+ {
+ ref LimiterState state = ref State.Span[0];
+
+ ReadOnlyMemory<float>[] inputBuffers = new ReadOnlyMemory<float>[Parameter.ChannelCount];
+ Memory<float>[] outputBuffers = new Memory<float>[Parameter.ChannelCount];
+
+ for (int i = 0; i < Parameter.ChannelCount; i++)
+ {
+ inputBuffers[i] = context.GetBufferMemory(InputBufferIndices[i]);
+ outputBuffers[i] = context.GetBufferMemory(OutputBufferIndices[i]);
+ }
+
+ for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
+ {
+ for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
+ {
+ float inputSample = inputBuffers[channelIndex].Span[sampleIndex];
+
+ float sampleInputMax = Math.Abs(inputSample * Parameter.InputGain);
+
+ float inputCoefficient = Parameter.ReleaseCoefficient;
+
+ if (sampleInputMax > state.DectectorAverage[channelIndex])
+ {
+ inputCoefficient = Parameter.AttackCoefficient;
+ }
+
+ state.DectectorAverage[channelIndex] += inputCoefficient * (sampleInputMax - state.DectectorAverage[channelIndex]);
+
+ float attenuation = 1.0f;
+
+ if (state.DectectorAverage[channelIndex] > Parameter.Threshold)
+ {
+ attenuation = Parameter.Threshold / state.DectectorAverage[channelIndex];
+ }
+
+ float outputCoefficient = Parameter.ReleaseCoefficient;
+
+ if (state.CompressionGain[channelIndex] > attenuation)
+ {
+ outputCoefficient = Parameter.AttackCoefficient;
+ }
+
+ state.CompressionGain[channelIndex] += outputCoefficient * (attenuation - state.CompressionGain[channelIndex]);
+
+ ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
+
+ outputBuffers[channelIndex].Span[sampleIndex] = delayedSample * state.CompressionGain[channelIndex] * Parameter.OutputGain;
+
+ delayedSample = inputSample;
+
+ state.DelayedSampleBufferPosition[channelIndex]++;
+
+ while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
+ {
+ state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < Parameter.ChannelCount; i++)
+ {
+ if (InputBufferIndices[i] != OutputBufferIndices[i])
+ {
+ context.GetBufferMemory(InputBufferIndices[i]).CopyTo(context.GetBufferMemory(OutputBufferIndices[i]));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs b/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs
new file mode 100644
index 00000000..0a4b14b7
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs
@@ -0,0 +1,179 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using Ryujinx.Audio.Renderer.Dsp.State;
+using Ryujinx.Audio.Renderer.Parameter;
+using Ryujinx.Audio.Renderer.Parameter.Effect;
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Dsp.Command
+{
+ public class LimiterCommandVersion2 : ICommand
+ {
+ public bool Enabled { get; set; }
+
+ public int NodeId { get; }
+
+ public CommandType CommandType => CommandType.LimiterVersion2;
+
+ public ulong EstimatedProcessingTime { get; set; }
+
+ public LimiterParameter Parameter => _parameter;
+ public Memory<LimiterState> State { get; }
+ public Memory<EffectResultState> ResultState { get; }
+ public ulong WorkBuffer { get; }
+ public ushort[] OutputBufferIndices { get; }
+ public ushort[] InputBufferIndices { get; }
+ public bool IsEffectEnabled { get; }
+
+ private LimiterParameter _parameter;
+
+ public LimiterCommandVersion2(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, Memory<EffectResultState> resultState, bool isEnabled, ulong workBuffer, int nodeId)
+ {
+ Enabled = true;
+ NodeId = nodeId;
+ _parameter = parameter;
+ State = state;
+ ResultState = resultState;
+ WorkBuffer = workBuffer;
+
+ IsEffectEnabled = isEnabled;
+
+ InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
+ OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
+
+ for (int i = 0; i < Parameter.ChannelCount; i++)
+ {
+ InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
+ OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
+ }
+ }
+
+ public void Process(CommandList context)
+ {
+ ref LimiterState state = ref State.Span[0];
+
+ if (IsEffectEnabled)
+ {
+ if (Parameter.Status == Server.Effect.UsageState.Invalid)
+ {
+ state = new LimiterState(ref _parameter, WorkBuffer);
+ }
+ else if (Parameter.Status == Server.Effect.UsageState.New)
+ {
+ state.UpdateParameter(ref _parameter);
+ }
+ }
+
+ ProcessLimiter(context);
+ }
+
+ private void ProcessLimiter(CommandList context)
+ {
+ Debug.Assert(Parameter.IsChannelCountValid());
+
+ if (IsEffectEnabled && Parameter.IsChannelCountValid())
+ {
+ ref LimiterState state = ref State.Span[0];
+
+ if (!ResultState.IsEmpty && Parameter.StatisticsReset)
+ {
+ ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
+
+ statistics.Reset();
+ }
+
+ ReadOnlyMemory<float>[] inputBuffers = new ReadOnlyMemory<float>[Parameter.ChannelCount];
+ Memory<float>[] outputBuffers = new Memory<float>[Parameter.ChannelCount];
+
+ for (int i = 0; i < Parameter.ChannelCount; i++)
+ {
+ inputBuffers[i] = context.GetBufferMemory(InputBufferIndices[i]);
+ outputBuffers[i] = context.GetBufferMemory(OutputBufferIndices[i]);
+ }
+
+ for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
+ {
+ for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
+ {
+ float inputSample = inputBuffers[channelIndex].Span[sampleIndex];
+
+ float sampleInputMax = Math.Abs(inputSample * Parameter.InputGain);
+
+ float inputCoefficient = Parameter.ReleaseCoefficient;
+
+ if (sampleInputMax > state.DectectorAverage[channelIndex])
+ {
+ inputCoefficient = Parameter.AttackCoefficient;
+ }
+
+ state.DectectorAverage[channelIndex] += inputCoefficient * (sampleInputMax - state.DectectorAverage[channelIndex]);
+
+ float attenuation = 1.0f;
+
+ if (state.DectectorAverage[channelIndex] > Parameter.Threshold)
+ {
+ attenuation = Parameter.Threshold / state.DectectorAverage[channelIndex];
+ }
+
+ float outputCoefficient = Parameter.ReleaseCoefficient;
+
+ if (state.CompressionGain[channelIndex] > attenuation)
+ {
+ outputCoefficient = Parameter.AttackCoefficient;
+ }
+
+ state.CompressionGain[channelIndex] += outputCoefficient * (attenuation - state.CompressionGain[channelIndex]);
+
+ ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
+
+ outputBuffers[channelIndex].Span[sampleIndex] = delayedSample * state.CompressionGain[channelIndex] * Parameter.OutputGain;
+
+ delayedSample = inputSample;
+
+ state.DelayedSampleBufferPosition[channelIndex]++;
+
+ while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
+ {
+ state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
+ }
+
+ if (!ResultState.IsEmpty)
+ {
+ ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
+
+ statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax);
+ statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], state.CompressionGain[channelIndex]);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < Parameter.ChannelCount; i++)
+ {
+ if (InputBufferIndices[i] != OutputBufferIndices[i])
+ {
+ context.GetBufferMemory(InputBufferIndices[i]).CopyTo(context.GetBufferMemory(OutputBufferIndices[i]));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs b/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs
new file mode 100644
index 00000000..53913bad
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs
@@ -0,0 +1,46 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using Ryujinx.Audio.Renderer.Parameter.Effect;
+using System;
+
+namespace Ryujinx.Audio.Renderer.Dsp.State
+{
+ public class LimiterState
+ {
+ public float[] DectectorAverage;
+ public float[] CompressionGain;
+ public float[] DelayedSampleBuffer;
+ public int[] DelayedSampleBufferPosition;
+
+ public LimiterState(ref LimiterParameter parameter, ulong workBuffer)
+ {
+ DectectorAverage = new float[parameter.ChannelCount];
+ CompressionGain = new float[parameter.ChannelCount];
+ DelayedSampleBuffer = new float[parameter.ChannelCount * parameter.DelayBufferSampleCountMax];
+ DelayedSampleBufferPosition = new int[parameter.ChannelCount];
+
+ DectectorAverage.AsSpan().Fill(0.0f);
+ CompressionGain.AsSpan().Fill(1.0f);
+ DelayedSampleBufferPosition.AsSpan().Fill(0);
+
+ UpdateParameter(ref parameter);
+ }
+
+ public void UpdateParameter(ref LimiterParameter parameter) {}
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs
index 5a747922..c30c4013 100644
--- a/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs
@@ -21,7 +21,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter.Effect
{
/// <summary>
- /// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.AuxiliaryBuffer"/>.
+ /// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.AuxiliaryBuffer"/>.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct AuxiliaryBufferParameter
diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs
index 7d58d4be..39d75b69 100644
--- a/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs
@@ -22,7 +22,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter.Effect
{
/// <summary>
- /// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BiquadFilter"/>.
+ /// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BiquadFilter"/>.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BiquadFilterEffectParameter
diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs
index b0c0c337..ef298e3d 100644
--- a/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs
@@ -21,7 +21,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter.Effect
{
/// <summary>
- /// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BufferMix"/>.
+ /// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BufferMix"/>.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BufferMixParameter
diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs
index e0dbeb7c..80e5df25 100644
--- a/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs
@@ -22,7 +22,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter.Effect
{
/// <summary>
- /// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Delay"/>.
+ /// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Delay"/>.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DelayParameter
@@ -103,7 +103,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
/// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
public bool IsChannelCountValid()
{
- return EffectInParameter.IsChannelCountValid(ChannelCount);
+ return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
}
/// <summary>
@@ -112,7 +112,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
/// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
public bool IsChannelCountMaxValid()
{
- return EffectInParameter.IsChannelCountValid(ChannelCountMax);
+ return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
}
}
}
diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs
new file mode 100644
index 00000000..2caf23f9
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs
@@ -0,0 +1,155 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using Ryujinx.Audio.Renderer.Server.Effect;
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Parameter.Effect
+{
+ /// <summary>
+ /// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Limiter"/>.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct LimiterParameter
+ {
+ /// <summary>
+ /// The input channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
+ /// </summary>
+ public Array6<byte> Input;
+
+ /// <summary>
+ /// The output channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
+ /// </summary>
+ public Array6<byte> Output;
+
+ /// <summary>
+ /// The maximum number of channels supported.
+ /// </summary>
+ public ushort ChannelCountMax;
+
+ /// <summary>
+ /// The total channel count used.
+ /// </summary>
+ public ushort ChannelCount;
+
+ /// <summary>
+ /// The target sample rate.
+ /// </summary>
+ /// <remarks>This is in kHz.</remarks>
+ public int SampleRate;
+
+ /// <summary>
+ /// The look ahead max time.
+ /// <remarks>This is in microseconds.</remarks>
+ /// </summary>
+ public int LookAheadTimeMax;
+
+ /// <summary>
+ /// The attack time.
+ /// <remarks>This is in microseconds.</remarks>
+ /// </summary>
+ public int AttackTime;
+
+ /// <summary>
+ /// The release time.
+ /// <remarks>This is in microseconds.</remarks>
+ /// </summary>
+ public int ReleaseTime;
+
+ /// <summary>
+ /// The look ahead time.
+ /// <remarks>This is in microseconds.</remarks>
+ /// </summary>
+ public int LookAheadTime;
+
+ /// <summary>
+ /// The attack coefficient.
+ /// </summary>
+ public float AttackCoefficient;
+
+ /// <summary>
+ /// The release coefficient.
+ /// </summary>
+ public float ReleaseCoefficient;
+
+ /// <summary>
+ /// The threshold.
+ /// </summary>
+ public float Threshold;
+
+ /// <summary>
+ /// The input gain.
+ /// </summary>
+ public float InputGain;
+
+ /// <summary>
+ /// The output gain.
+ /// </summary>
+ public float OutputGain;
+
+ /// <summary>
+ /// The minimum samples stored in the delay buffer.
+ /// </summary>
+ public int DelayBufferSampleCountMin;
+
+ /// <summary>
+ /// The maximum samples stored in the delay buffer.
+ /// </summary>
+ public int DelayBufferSampleCountMax;
+
+ /// <summary>
+ /// The current usage status of the effect on the client side.
+ /// </summary>
+ public UsageState Status;
+
+ /// <summary>
+ /// Indicate if the limiter effect should output statistics.
+ /// </summary>
+ [MarshalAs(UnmanagedType.I1)]
+ public bool StatisticsEnabled;
+
+ /// <summary>
+ /// Indicate to the DSP that the user did a statistics reset.
+ /// </summary>
+ [MarshalAs(UnmanagedType.I1)]
+ public bool StatisticsReset;
+
+ /// <summary>
+ /// Reserved/padding.
+ /// </summary>
+ private byte _reserved;
+
+ /// <summary>
+ /// Check if the <see cref="ChannelCount"/> is valid.
+ /// </summary>
+ /// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
+ public bool IsChannelCountValid()
+ {
+ return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
+ }
+
+ /// <summary>
+ /// Check if the <see cref="ChannelCountMax"/> is valid.
+ /// </summary>
+ /// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
+ public bool IsChannelCountMaxValid()
+ {
+ return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs
new file mode 100644
index 00000000..faf12f2a
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs
@@ -0,0 +1,48 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Parameter.Effect
+{
+ /// <summary>
+ /// Effect result state for <seealso cref="Common.EffectType.Limiter"/>.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct LimiterStatistics
+ {
+ /// <summary>
+ /// The max input sample value recorded by the limiter.
+ /// </summary>
+ public Array6<float> InputMax;
+
+ /// <summary>
+ /// Compression gain min value.
+ /// </summary>
+ public Array6<float> CompressionGainMin;
+
+ /// <summary>
+ /// Reset the statistics.
+ /// </summary>
+ public void Reset()
+ {
+ InputMax.ToSpan().Fill(0.0f);
+ CompressionGainMin.ToSpan().Fill(1.0f);
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs
index 0dbecfb8..9b775fff 100644
--- a/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs
@@ -22,7 +22,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter.Effect
{
/// <summary>
- /// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Reverb3d"/>.
+ /// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Reverb3d"/>.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Reverb3dParameter
@@ -129,7 +129,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
/// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
public bool IsChannelCountValid()
{
- return EffectInParameter.IsChannelCountValid(ChannelCount);
+ return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
}
/// <summary>
@@ -138,7 +138,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
/// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
public bool IsChannelCountMaxValid()
{
- return EffectInParameter.IsChannelCountValid(ChannelCountMax);
+ return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
}
}
}
diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs
index daa81c51..dea54407 100644
--- a/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs
@@ -23,7 +23,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter.Effect
{
/// <summary>
- /// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Reverb"/>.
+ /// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Reverb"/>.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ReverbParameter
@@ -121,7 +121,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
/// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
public bool IsChannelCountValid()
{
- return EffectInParameter.IsChannelCountValid(ChannelCount);
+ return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
}
/// <summary>
@@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
/// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
public bool IsChannelCountMaxValid()
{
- return EffectInParameter.IsChannelCountValid(ChannelCountMax);
+ return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
}
}
}
diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs
index af8edeff..ec2d801e 100644
--- a/Ryujinx.Audio/Renderer/Parameter/EffectInParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs
@@ -23,10 +23,10 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter
{
/// <summary>
- /// Input information for an effect.
+ /// Input information for an effect version 1.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
- public struct EffectInParameter
+ public struct EffectInParameterVersion1 : IEffectInParameter
{
/// <summary>
/// Type of the effect.
@@ -85,11 +85,22 @@ namespace Ryujinx.Audio.Renderer.Parameter
[StructLayout(LayoutKind.Sequential, Size = 0xA0, Pack = 1)]
private struct SpecificDataStruct { }
- /// <summary>
- /// Specific data changing depending of the <see cref="Type"/>. See also the <see cref="Effect"/> namespace.
- /// </summary>
public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart);
+ EffectType IEffectInParameter.Type => Type;
+
+ bool IEffectInParameter.IsNew => IsNew;
+
+ bool IEffectInParameter.IsEnabled => IsEnabled;
+
+ int IEffectInParameter.MixId => MixId;
+
+ ulong IEffectInParameter.BufferBase => BufferBase;
+
+ ulong IEffectInParameter.BufferSize => BufferSize;
+
+ uint IEffectInParameter.ProcessingOrder => ProcessingOrder;
+
/// <summary>
/// Check if the given channel count is valid.
/// </summary>
diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs b/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs
new file mode 100644
index 00000000..ea74f1e6
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs
@@ -0,0 +1,114 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using Ryujinx.Audio.Renderer.Common;
+using Ryujinx.Common.Utilities;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Parameter
+{
+ /// <summary>
+ /// Input information for an effect version 2. (added with REV9)
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct EffectInParameterVersion2 : IEffectInParameter
+ {
+ /// <summary>
+ /// Type of the effect.
+ /// </summary>
+ public EffectType Type;
+
+ /// <summary>
+ /// Set to true if the effect is new.
+ /// </summary>
+ [MarshalAs(UnmanagedType.I1)]
+ public bool IsNew;
+
+ /// <summary>
+ /// Set to true if the effect must be active.
+ /// </summary>
+ [MarshalAs(UnmanagedType.I1)]
+ public bool IsEnabled;
+
+ /// <summary>
+ /// Reserved/padding.
+ /// </summary>
+ private byte _reserved1;
+
+ /// <summary>
+ /// The target mix id of the effect.
+ /// </summary>
+ public int MixId;
+
+ /// <summary>
+ /// Address of the processing workbuffer.
+ /// </summary>
+ /// <remarks>This is additional data that could be required by the effect processing.</remarks>
+ public ulong BufferBase;
+
+ /// <summary>
+ /// Size of the processing workbuffer.
+ /// </summary>
+ /// <remarks>This is additional data that could be required by the effect processing.</remarks>
+ public ulong BufferSize;
+
+ /// <summary>
+ /// Position of the effect while processing effects.
+ /// </summary>
+ public uint ProcessingOrder;
+
+ /// <summary>
+ /// Reserved/padding.
+ /// </summary>
+ private uint _reserved2;
+
+ /// <summary>
+ /// Specific data storage.
+ /// </summary>
+ private SpecificDataStruct _specificDataStart;
+
+ [StructLayout(LayoutKind.Sequential, Size = 0xA0, Pack = 1)]
+ private struct SpecificDataStruct { }
+
+ public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart);
+
+ EffectType IEffectInParameter.Type => Type;
+
+ bool IEffectInParameter.IsNew => IsNew;
+
+ bool IEffectInParameter.IsEnabled => IsEnabled;
+
+ int IEffectInParameter.MixId => MixId;
+
+ ulong IEffectInParameter.BufferBase => BufferBase;
+
+ ulong IEffectInParameter.BufferSize => BufferSize;
+
+ uint IEffectInParameter.ProcessingOrder => ProcessingOrder;
+
+ /// <summary>
+ /// Check if the given channel count is valid.
+ /// </summary>
+ /// <param name="channelCount">The channel count to check</param>
+ /// <returns>Returns true if the channel count is valid.</returns>
+ public static bool IsChannelCountValid(int channelCount)
+ {
+ return channelCount == 1 || channelCount == 2 || channelCount == 4 || channelCount == 6;
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs b/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs
new file mode 100644
index 00000000..0c26d2b9
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs
@@ -0,0 +1,40 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Parameter
+{
+ /// <summary>
+ /// Output information for an effect version 1.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct EffectOutStatusVersion1 : IEffectOutStatus
+ {
+ /// <summary>
+ /// Current effect state.
+ /// </summary>
+ public EffectState State;
+
+ /// <summary>
+ /// Unused/Reserved.
+ /// </summary>
+ private unsafe fixed byte _reserved[15];
+
+ EffectState IEffectOutStatus.State { get => State; set => State = value; }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectOutStatus.cs b/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs
index c6178165..59eeae9b 100644
--- a/Ryujinx.Audio/Renderer/Parameter/EffectOutStatus.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) 2019-2021 Ryujinx
//
// This program is free software: you can redistribute it and/or modify
@@ -20,28 +20,12 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter
{
/// <summary>
- /// Output information for an effect.
+ /// Output information for an effect version 2. (added with REV9)
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
- public struct EffectOutStatus
+ public struct EffectOutStatusVersion2 : IEffectOutStatus
{
/// <summary>
- /// The state of an effect.
- /// </summary>
- public enum EffectState : byte
- {
- /// <summary>
- /// The effect is enabled.
- /// </summary>
- Enabled = 3,
-
- /// <summary>
- /// The effect is disabled.
- /// </summary>
- Disabled = 4
- }
-
- /// <summary>
/// Current effect state.
/// </summary>
public EffectState State;
@@ -50,5 +34,12 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// Unused/Reserved.
/// </summary>
private unsafe fixed byte _reserved[15];
+
+ /// <summary>
+ /// Current result state.
+ /// </summary>
+ public EffectResultState ResultState;
+
+ EffectState IEffectOutStatus.State { get => State; set => State = value; }
}
}
diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs b/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs
new file mode 100644
index 00000000..64a6f50a
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs
@@ -0,0 +1,43 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using Ryujinx.Common.Utilities;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Parameter
+{
+ /// <summary>
+ /// Effect result state (added in REV9).
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct EffectResultState
+ {
+ /// <summary>
+ /// Specific data storage.
+ /// </summary>
+ private SpecificDataStruct _specificDataStart;
+
+ [StructLayout(LayoutKind.Sequential, Size = 0x80, Pack = 1)]
+ private struct SpecificDataStruct { }
+
+ /// <summary>
+ /// Specific data changing depending of the type of effect. See also the <see cref="Effect"/> namespace.
+ /// </summary>
+ public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart);
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectState.cs b/Ryujinx.Audio/Renderer/Parameter/EffectState.cs
new file mode 100644
index 00000000..685d0cdb
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Parameter/EffectState.cs
@@ -0,0 +1,35 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+namespace Ryujinx.Audio.Renderer.Parameter
+{
+ /// <summary>
+ /// The state of an effect.
+ /// </summary>
+ public enum EffectState : byte
+ {
+ /// <summary>
+ /// The effect is enabled.
+ /// </summary>
+ Enabled = 3,
+
+ /// <summary>
+ /// The effect is disabled.
+ /// </summary>
+ Disabled = 4
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs
new file mode 100644
index 00000000..779d8298
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs
@@ -0,0 +1,70 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using Ryujinx.Audio.Renderer.Common;
+using System;
+
+namespace Ryujinx.Audio.Renderer.Parameter
+{
+ /// <summary>
+ /// Generic interface to represent input information for an effect.
+ /// </summary>
+ public interface IEffectInParameter
+ {
+ /// <summary>
+ /// Type of the effect.
+ /// </summary>
+ EffectType Type { get; }
+
+ /// <summary>
+ /// Set to true if the effect is new.
+ /// </summary>
+ bool IsNew { get; }
+
+ /// <summary>
+ /// Set to true if the effect must be active.
+ /// </summary>
+ bool IsEnabled { get; }
+
+ /// <summary>
+ /// The target mix id of the effect.
+ /// </summary>
+ int MixId { get; }
+
+ /// <summary>
+ /// Address of the processing workbuffer.
+ /// </summary>
+ /// <remarks>This is additional data that could be required by the effect processing.</remarks>
+ ulong BufferBase { get; }
+
+ /// <summary>
+ /// Size of the processing workbuffer.
+ /// </summary>
+ /// <remarks>This is additional data that could be required by the effect processing.</remarks>
+ ulong BufferSize { get; }
+
+ /// <summary>
+ /// Position of the effect while processing effects.
+ /// </summary>
+ uint ProcessingOrder { get; }
+
+ /// <summary>
+ /// Specific data changing depending of the <see cref="Type"/>. See also the <see cref="Effect"/> namespace.
+ /// </summary>
+ Span<byte> SpecificData { get; }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs b/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs
new file mode 100644
index 00000000..29220a9d
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs
@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+namespace Ryujinx.Audio.Renderer.Parameter
+{
+ /// <summary>
+ /// Generic interface to represent output information for an effect.
+ /// </summary>
+ public interface IEffectOutStatus
+ {
+ /// <summary>
+ /// Current effect state.
+ /// </summary>
+ EffectState State { get; set; }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
index 112b0e44..943a2d78 100644
--- a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
+++ b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
@@ -307,7 +307,7 @@ namespace Ryujinx.Audio.Renderer.Server
_upsamplerManager = new UpsamplerManager(upSamplerWorkBuffer, _upsamplerCount);
- _effectContext.Initialize(parameter.EffectCount);
+ _effectContext.Initialize(parameter.EffectCount, _behaviourContext.IsEffectInfoVersion2Supported() ? parameter.EffectCount : 0);
_sinkContext.Initialize(parameter.SinkCount);
Memory<VoiceUpdateState> voiceUpdateStatesDsp = workBufferAllocator.Allocate<VoiceUpdateState>(parameter.VoiceCount, VoiceUpdateState.Align);
@@ -636,6 +636,11 @@ namespace Ryujinx.Audio.Renderer.Server
_voiceContext.UpdateForCommandGeneration();
+ if (_behaviourContext.IsEffectInfoVersion2Supported())
+ {
+ _effectContext.UpdateResultStateForCommandGeneration();
+ }
+
ulong endTicks = GetSystemTicks();
_totalElapsedTicks = endTicks - startTicks;
diff --git a/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
index b31f9e9f..ed1f402e 100644
--- a/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
@@ -90,9 +90,17 @@ namespace Ryujinx.Audio.Renderer.Server
public const int Revision8 = 8 << 24;
/// <summary>
+ /// REV9:
+ /// EffectInfo parameters were revisited with a new revision (version 2) allowing more data control between the client and server.
+ /// A new effect was added: Limiter. This effect is effectively implemented with a DRC while providing statistics on the processing on <see cref="Parameter.EffectOutStatusVersion2"/>.
+ /// </summary>
+ /// <remarks>This was added in system update 12.0.0</remarks>
+ public const int Revision9 = 9 << 24;
+
+ /// <summary>
/// Last revision supported by the implementation.
/// </summary>
- public const int LastRevision = Revision8;
+ public const int LastRevision = Revision9;
/// <summary>
/// Target revision magic supported by the implementation.
@@ -331,6 +339,15 @@ namespace Ryujinx.Audio.Renderer.Server
}
/// <summary>
+ /// Check if the audio renderer should use the new effect info format.
+ /// </summary>
+ /// <returns>True if the audio renderer should use the new effect info format.</returns>
+ public bool IsEffectInfoVersion2Supported()
+ {
+ return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision9);
+ }
+
+ /// <summary>
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
/// </summary>
/// <returns>The version of the <see cref="ICommandProcessingTimeEstimator"/>.</returns>
diff --git a/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs
index ca37e090..24cc93af 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs
@@ -372,6 +372,49 @@ namespace Ryujinx.Audio.Renderer.Server
}
/// <summary>
+ /// Generate a new <see cref="LimiterCommandVersion1"/>.
+ /// </summary>
+ /// <param name="bufferOffset">The target buffer offset.</param>
+ /// <param name="parameter">The limiter parameter.</param>
+ /// <param name="state">The limiter state.</param>
+ /// <param name="isEnabled">Set to true if the effect should be active.</param>
+ /// <param name="workBuffer">The work buffer to use for processing.</param>
+ /// <param name="nodeId">The node id associated to this command.</param>
+ public void GenerateLimiterEffectVersion1(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, bool isEnabled, ulong workBuffer, int nodeId)
+ {
+ if (parameter.IsChannelCountValid())
+ {
+ LimiterCommandVersion1 command = new LimiterCommandVersion1(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId);
+
+ command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
+
+ AddCommand(command);
+ }
+ }
+
+ /// <summary>
+ /// Generate a new <see cref="LimiterCommandVersion2"/>.
+ /// </summary>
+ /// <param name="bufferOffset">The target buffer offset.</param>
+ /// <param name="parameter">The limiter parameter.</param>
+ /// <param name="state">The limiter state.</param>
+ /// <param name="effectResultState">The DSP effect result state.</param>
+ /// <param name="isEnabled">Set to true if the effect should be active.</param>
+ /// <param name="workBuffer">The work buffer to use for processing.</param>
+ /// <param name="nodeId">The node id associated to this command.</param>
+ public void GenerateLimiterEffectVersion2(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, Memory<EffectResultState> effectResultState, bool isEnabled, ulong workBuffer, int nodeId)
+ {
+ if (parameter.IsChannelCountValid())
+ {
+ LimiterCommandVersion2 command = new LimiterCommandVersion2(bufferOffset, parameter, state, effectResultState, isEnabled, workBuffer, nodeId);
+
+ command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
+
+ AddCommand(command);
+ }
+ }
+
+ /// <summary>
/// Generate a new <see cref="AuxiliaryBufferCommand"/>.
/// </summary>
/// <param name="bufferOffset">The target buffer offset.</param>
diff --git a/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs
index d2499c1d..01e7c927 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs
@@ -538,7 +538,25 @@ namespace Ryujinx.Audio.Renderer.Server
}
}
- private void GenerateEffect(ref MixState mix, BaseEffect effect)
+ private void GenerateLimiterEffect(uint bufferOffset, LimiterEffect effect, int nodeId, int effectId)
+ {
+ Debug.Assert(effect.Type == EffectType.Limiter);
+
+ ulong workBuffer = effect.GetWorkBuffer(-1);
+
+ if (_rendererContext.BehaviourContext.IsEffectInfoVersion2Supported())
+ {
+ Memory<EffectResultState> dspResultState = _effectContext.GetDspStateMemory(effectId);
+
+ _commandBuffer.GenerateLimiterEffectVersion2(bufferOffset, effect.Parameter, effect.State, dspResultState, effect.IsEnabled, workBuffer, nodeId);
+ }
+ else
+ {
+ _commandBuffer.GenerateLimiterEffectVersion1(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId);
+ }
+ }
+
+ private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
{
int nodeId = mix.NodeId;
@@ -576,6 +594,9 @@ namespace Ryujinx.Audio.Renderer.Server
case EffectType.BiquadFilter:
GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId);
break;
+ case EffectType.Limiter:
+ GenerateLimiterEffect(mix.BufferOffset, (LimiterEffect)effect, nodeId, effectId);
+ break;
default:
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
}
@@ -611,7 +632,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (!effect.ShouldSkip())
{
- GenerateEffect(ref mix, effect);
+ GenerateEffect(ref mix, effectOrder, effect);
}
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs
index 81d3b57b..feb3706f 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs
@@ -176,5 +176,15 @@ namespace Ryujinx.Audio.Renderer.Server
{
return 0;
}
+
+ public uint Estimate(LimiterCommandVersion1 command)
+ {
+ return 0;
+ }
+
+ public uint Estimate(LimiterCommandVersion2 command)
+ {
+ return 0;
+ }
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs
index 0f4fe3c2..227f3c81 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs
@@ -540,5 +540,15 @@ namespace Ryujinx.Audio.Renderer.Server
{
return 0;
}
+
+ public uint Estimate(LimiterCommandVersion1 command)
+ {
+ return 0;
+ }
+
+ public uint Estimate(LimiterCommandVersion2 command)
+ {
+ return 0;
+ }
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs
index b48ff8b5..e00fcf7b 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs
@@ -18,6 +18,7 @@
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp.Command;
+using Ryujinx.Audio.Renderer.Parameter.Effect;
using System;
using System.Diagnostics;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter;
@@ -632,5 +633,127 @@ namespace Ryujinx.Audio.Renderer.Server
throw new NotImplementedException($"{format}");
}
}
+
+ private uint EstimateLimiterCommandCommon(LimiterParameter parameter, bool enabled)
+ {
+ Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
+
+ if (_sampleCount == 160)
+ {
+ if (enabled)
+ {
+ switch (parameter.ChannelCount)
+ {
+ case 1:
+ return (uint)21392.0f;
+ case 2:
+ return (uint)26829.0f;
+ case 4:
+ return (uint)32405.0f;
+ case 6:
+ return (uint)52219.0f;
+ default:
+ throw new NotImplementedException($"{parameter.ChannelCount}");
+ }
+ }
+ else
+ {
+ switch (parameter.ChannelCount)
+ {
+ case 1:
+ return (uint)897.0f;
+ case 2:
+ return (uint)931.55f;
+ case 4:
+ return (uint)975.39f;
+ case 6:
+ return (uint)1016.8f;
+ default:
+ throw new NotImplementedException($"{parameter.ChannelCount}");
+ }
+ }
+ }
+
+ if (enabled)
+ {
+ switch (parameter.ChannelCount)
+ {
+ case 1:
+ return (uint)30556.0f;
+ case 2:
+ return (uint)39011.0f;
+ case 4:
+ return (uint)48270.0f;
+ case 6:
+ return (uint)76712.0f;
+ default:
+ throw new NotImplementedException($"{parameter.ChannelCount}");
+ }
+ }
+ else
+ {
+ switch (parameter.ChannelCount)
+ {
+ case 1:
+ return (uint)874.43f;
+ case 2:
+ return (uint)921.55f;
+ case 4:
+ return (uint)945.26f;
+ case 6:
+ return (uint)992.26f;
+ default:
+ throw new NotImplementedException($"{parameter.ChannelCount}");
+ }
+ }
+ }
+
+ public uint Estimate(LimiterCommandVersion1 command)
+ {
+ Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
+
+ return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled);
+ }
+
+ public uint Estimate(LimiterCommandVersion2 command)
+ {
+ Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
+
+ if (!command.Parameter.StatisticsEnabled || !command.IsEffectEnabled)
+ {
+ return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled);
+ }
+
+ if (_sampleCount == 160)
+ {
+ switch (command.Parameter.ChannelCount)
+ {
+ case 1:
+ return (uint)23309.0f;
+ case 2:
+ return (uint)29954.0f;
+ case 4:
+ return (uint)35807.0f;
+ case 6:
+ return (uint)58340.0f;
+ default:
+ throw new NotImplementedException($"{command.Parameter.ChannelCount}");
+ }
+ }
+
+ switch (command.Parameter.ChannelCount)
+ {
+ case 1:
+ return (uint)33526.0f;
+ case 2:
+ return (uint)43549.0f;
+ case 4:
+ return (uint)52190.0f;
+ case 6:
+ return (uint)85527.0f;
+ default:
+ throw new NotImplementedException($"{command.Parameter.ChannelCount}");
+ }
+ }
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
index df82945b..eac1708e 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
@@ -50,7 +50,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return WorkBuffers[index].GetReference(true);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
Debug.Assert(IsTypeValid(ref parameter));
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
index 7529f894..c7b06e7a 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
@@ -98,7 +98,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// </summary>
/// <param name="parameter">The user parameter.</param>
/// <returns>Returns true if the <see cref="EffectType"/> sent by the user matches the internal <see cref="EffectType"/>.</returns>
- public bool IsTypeValid(ref EffectInParameter parameter)
+ public bool IsTypeValid<T>(ref T parameter) where T: unmanaged, IEffectInParameter
{
return parameter.Type == TargetEffectType;
}
@@ -115,7 +115,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// Update the internal common parameters from a user parameter.
/// </summary>
/// <param name="parameter">The user parameter.</param>
- protected void UpdateParameterBase(ref EffectInParameter parameter)
+ protected void UpdateParameterBase<T>(ref T parameter) where T : unmanaged, IEffectInParameter
{
MixId = parameter.MixId;
ProcessingOrder = parameter.ProcessingOrder;
@@ -154,12 +154,38 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
}
/// <summary>
- /// Update the internal state from a user parameter.
+ /// Initialize the given <paramref name="state"/> result state.
+ /// </summary>
+ /// <param name="state">The state to initalize</param>
+ public virtual void InitializeResultState(ref EffectResultState state) {}
+
+ /// <summary>
+ /// Update the <paramref name="destState"/> result state with <paramref name="srcState"/>.
+ /// </summary>
+ /// <param name="destState">The destination result state</param>
+ /// <param name="srcState">The source result state</param>
+ public virtual void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState) {}
+
+ /// <summary>
+ /// Update the internal state from a user version 1 parameter.
+ /// </summary>
+ /// <param name="updateErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param>
+ /// <param name="parameter">The user parameter.</param>
+ /// <param name="mapper">The mapper to use.</param>
+ public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ {
+ Debug.Assert(IsTypeValid(ref parameter));
+
+ updateErrorInfo = new ErrorInfo();
+ }
+
+ /// <summary>
+ /// Update the internal state from a user version 2 parameter.
/// </summary>
/// <param name="updateErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param>
/// <param name="parameter">The user parameter.</param>
/// <param name="mapper">The mapper to use.</param>
- public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
+ public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
{
Debug.Assert(IsTypeValid(ref parameter));
@@ -206,26 +232,26 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// </summary>
/// <param name="outStatus">The given user output.</param>
/// <param name="isAudioRendererActive">If set to true, the <see cref="AudioRenderSystem"/> is active.</param>
- public void StoreStatus(ref EffectOutStatus outStatus, bool isAudioRendererActive)
+ public void StoreStatus<T>(ref T outStatus, bool isAudioRendererActive) where T: unmanaged, IEffectOutStatus
{
if (isAudioRendererActive)
{
if (UsageState == UsageState.Disabled)
{
- outStatus.State = EffectOutStatus.EffectState.Disabled;
+ outStatus.State = EffectState.Disabled;
}
else
{
- outStatus.State = EffectOutStatus.EffectState.Enabled;
+ outStatus.State = EffectState.Enabled;
}
}
else if (UsageState == UsageState.New)
{
- outStatus.State = EffectOutStatus.EffectState.Enabled;
+ outStatus.State = EffectState.Enabled;
}
else
{
- outStatus.State = EffectOutStatus.EffectState.Disabled;
+ outStatus.State = EffectState.Disabled;
}
}
@@ -249,6 +275,8 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return PerformanceDetailType.Reverb3d;
case EffectType.BufferMix:
return PerformanceDetailType.Mix;
+ case EffectType.Limiter:
+ return PerformanceDetailType.Limiter;
default:
throw new NotImplementedException($"{Type}");
}
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs
index 35ba8a0d..6c91b607 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs
@@ -52,7 +52,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
public override EffectType TargetEffectType => EffectType.BiquadFilter;
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
Debug.Assert(IsTypeValid(ref parameter));
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs
index 14ed3950..05cccad9 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs
@@ -36,7 +36,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
public override EffectType TargetEffectType => EffectType.BufferMix;
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
Debug.Assert(IsTypeValid(ref parameter));
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs
index df3e5ee7..a5b3dbc5 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs
@@ -54,7 +54,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return GetSingleBuffer();
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
Debug.Assert(IsTypeValid(ref parameter));
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs b/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs
index ff6051ae..074f4375 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs
@@ -15,6 +15,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+using Ryujinx.Audio.Renderer.Parameter;
+using Ryujinx.Audio.Renderer.Utils;
+using System;
using System.Diagnostics;
namespace Ryujinx.Audio.Renderer.Server.Effect
@@ -34,6 +37,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// </summary>
private uint _effectCount;
+ private EffectResultState[] _resultStatesCpu;
+ private EffectResultState[] _resultStatesDsp;
+
/// <summary>
/// Create a new <see cref="EffectContext"/>.
/// </summary>
@@ -47,7 +53,8 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// Initialize the <see cref="EffectContext"/>.
/// </summary>
/// <param name="effectCount">The total effect count.</param>
- public void Initialize(uint effectCount)
+ /// <param name="resultStateCount">The total result state count.</param>
+ public void Initialize(uint effectCount, uint resultStateCount)
{
_effectCount = effectCount;
_effects = new BaseEffect[effectCount];
@@ -56,6 +63,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
{
_effects[i] = new BaseEffect();
}
+
+ _resultStatesCpu = new EffectResultState[resultStateCount];
+ _resultStatesDsp = new EffectResultState[resultStateCount];
}
/// <summary>
@@ -78,5 +88,53 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return ref _effects[index];
}
+
+ /// <summary>
+ /// Get a reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.
+ /// </summary>
+ /// <param name="index">The index to use.</param>
+ /// <returns>A reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.</returns>
+ /// <remarks>The returned <see cref="EffectResultState"/> should only be used when updating the server state.</remarks>
+ public ref EffectResultState GetState(int index)
+ {
+ Debug.Assert(index >= 0 && index < _resultStatesCpu.Length);
+
+ return ref _resultStatesCpu[index];
+ }
+
+ /// <summary>
+ /// Get a reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.
+ /// </summary>
+ /// <param name="index">The index to use.</param>
+ /// <returns>A reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.</returns>
+ /// <remarks>The returned <see cref="EffectResultState"/> should only be used in the context of processing on the <see cref="Dsp.AudioProcessor"/>.</remarks>
+ public ref EffectResultState GetDspState(int index)
+ {
+ Debug.Assert(index >= 0 && index < _resultStatesDsp.Length);
+
+ return ref _resultStatesDsp[index];
+ }
+
+ /// <summary>
+ /// Get a memory instance to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.
+ /// </summary>
+ /// <param name="index">The index to use.</param>
+ /// <returns>A memory instance to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.</returns>
+ /// <remarks>The returned <see cref="Memory{EffectResultState}"/> should only be used in the context of processing on the <see cref="Dsp.AudioProcessor"/>.</remarks>
+ public Memory<EffectResultState> GetDspStateMemory(int index)
+ {
+ return SpanIOHelper.GetMemory(_resultStatesDsp.AsMemory(), index, (uint)_resultStatesDsp.Length);
+ }
+
+ /// <summary>
+ /// Update internal state during command generation.
+ /// </summary>
+ public void UpdateResultStateForCommandGeneration()
+ {
+ for (int index = 0; index < _resultStatesCpu.Length; index++)
+ {
+ _effects[index].UpdateResultState(ref _resultStatesCpu[index], ref _resultStatesDsp[index]);
+ }
+ }
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs
new file mode 100644
index 00000000..fa04c027
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs
@@ -0,0 +1,112 @@
+//
+// Copyright (c) 2019-2021 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using Ryujinx.Audio.Renderer.Common;
+using Ryujinx.Audio.Renderer.Dsp.State;
+using Ryujinx.Audio.Renderer.Parameter;
+using Ryujinx.Audio.Renderer.Parameter.Effect;
+using Ryujinx.Audio.Renderer.Server.MemoryPool;
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Server.Effect
+{
+ /// <summary>
+ /// Server state for a limiter effect.
+ /// </summary>
+ public class LimiterEffect : BaseEffect
+ {
+ /// <summary>
+ /// The limiter parameter.
+ /// </summary>
+ public LimiterParameter Parameter;
+
+ /// <summary>
+ /// The limiter state.
+ /// </summary>
+ public Memory<LimiterState> State { get; }
+
+ /// <summary>
+ /// Create a new <see cref="LimiterEffect"/>.
+ /// </summary>
+ public LimiterEffect()
+ {
+ State = new LimiterState[1];
+ }
+
+ public override EffectType TargetEffectType => EffectType.Limiter;
+
+ public override ulong GetWorkBuffer(int index)
+ {
+ return GetSingleBuffer();
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ {
+ Debug.Assert(IsTypeValid(ref parameter));
+
+ ref LimiterParameter limiterParameter = ref MemoryMarshal.Cast<byte, LimiterParameter>(parameter.SpecificData)[0];
+
+ updateErrorInfo = new BehaviourParameter.ErrorInfo();
+
+ UpdateParameterBase(ref parameter);
+
+ Parameter = limiterParameter;
+
+ IsEnabled = parameter.IsEnabled;
+
+ if (BufferUnmapped || parameter.IsNew)
+ {
+ UsageState = UsageState.New;
+ Parameter.Status = UsageState.Invalid;
+
+ BufferUnmapped = !mapper.TryAttachBuffer(out updateErrorInfo, ref WorkBuffers[0], parameter.BufferBase, parameter.BufferSize);
+ }
+ }
+
+ public override void UpdateForCommandGeneration()
+ {
+ UpdateUsageStateForCommandGeneration();
+
+ Parameter.Status = UsageState.Enabled;
+ Parameter.StatisticsReset = false;
+ }
+
+ public override void InitializeResultState(ref EffectResultState state)
+ {
+ ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(state.SpecificData)[0];
+
+ statistics.Reset();
+ }
+
+ public override void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState)
+ {
+ destState = srcState;
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs
index d9f23799..7b8fd6c4 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs
@@ -53,7 +53,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return GetSingleBuffer();
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T: unmanaged, IEffectInParameter
{
Debug.Assert(IsTypeValid(ref parameter));
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs
index 4c81f729..30908396 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs
@@ -56,7 +56,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return GetSingleBuffer();
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
Debug.Assert(IsTypeValid(ref parameter));
diff --git a/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs b/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs
index 119ec907..eae48be6 100644
--- a/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs
+++ b/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs
@@ -48,5 +48,7 @@ namespace Ryujinx.Audio.Renderer.Server
uint Estimate(DeviceSinkCommand command);
uint Estimate(DownMixSurroundToStereoCommand command);
uint Estimate(UpsampleCommand command);
+ uint Estimate(LimiterCommandVersion1 command);
+ uint Estimate(LimiterCommandVersion2 command);
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs
index 3a336af3..fd7c93b1 100644
--- a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs
@@ -18,7 +18,6 @@
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Parameter;
using System;
-using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Server.Performance
{
diff --git a/Ryujinx.Audio/Renderer/Server/StateUpdater.cs b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
index 77935b75..e7a982c4 100644
--- a/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
+++ b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
@@ -224,7 +224,7 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.Success;
}
- private static void ResetEffect(ref BaseEffect effect, ref EffectInParameter parameter, PoolMapper mapper)
+ private static void ResetEffect<T>(ref BaseEffect effect, ref T parameter, PoolMapper mapper) where T: unmanaged, IEffectInParameter
{
effect.ForceUnmapBuffers(mapper);
@@ -251,6 +251,9 @@ namespace Ryujinx.Audio.Renderer.Server
case EffectType.BiquadFilter:
effect = new BiquadFilterEffect();
break;
+ case EffectType.Limiter:
+ effect = new LimiterEffect();
+ break;
default:
throw new NotImplementedException($"EffectType {parameter.Type} not implemented!");
}
@@ -258,14 +261,82 @@ namespace Ryujinx.Audio.Renderer.Server
public ResultCode UpdateEffects(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools)
{
- if (context.GetCount() * Unsafe.SizeOf<EffectInParameter>() != _inputHeader.EffectsSize)
+ if (_behaviourContext.IsEffectInfoVersion2Supported())
+ {
+ return UpdateEffectsVersion2(context, isAudioRendererActive, memoryPools);
+ }
+ else
+ {
+ return UpdateEffectsVersion1(context, isAudioRendererActive, memoryPools);
+ }
+ }
+
+ public ResultCode UpdateEffectsVersion2(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools)
+ {
+ if (context.GetCount() * Unsafe.SizeOf<EffectInParameterVersion2>() != _inputHeader.EffectsSize)
+ {
+ return ResultCode.InvalidUpdateInfo;
+ }
+
+ int initialOutputSize = _output.Length;
+
+ ReadOnlySpan<EffectInParameterVersion2> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion2>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span);
+
+ _input = _input.Slice((int)_inputHeader.EffectsSize);
+
+ PoolMapper mapper = new PoolMapper(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
+
+ for (int i = 0; i < context.GetCount(); i++)
+ {
+ EffectInParameterVersion2 parameter = parameters[i];
+
+ ref EffectOutStatusVersion2 outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatusVersion2>(ref _output)[0];
+
+ ref BaseEffect effect = ref context.GetEffect(i);
+
+ if (!effect.IsTypeValid(ref parameter))
+ {
+ ResetEffect(ref effect, ref parameter, mapper);
+ }
+
+ effect.Update(out ErrorInfo updateErrorInfo, ref parameter, mapper);
+
+ if (updateErrorInfo.ErrorCode != ResultCode.Success)
+ {
+ _behaviourContext.AppendError(ref updateErrorInfo);
+ }
+
+ effect.StoreStatus(ref outStatus, isAudioRendererActive);
+
+ if (parameter.IsNew)
+ {
+ effect.InitializeResultState(ref context.GetDspState(i));
+ effect.InitializeResultState(ref context.GetState(i));
+ }
+
+ effect.UpdateResultState(ref outStatus.ResultState, ref context.GetState(i));
+ }
+
+ int currentOutputSize = _output.Length;
+
+ OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatusVersion2>() * context.GetCount());
+ OutputHeader.TotalSize += OutputHeader.EffectsSize;
+
+ Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize);
+
+ return ResultCode.Success;
+ }
+
+ public ResultCode UpdateEffectsVersion1(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools)
+ {
+ if (context.GetCount() * Unsafe.SizeOf<EffectInParameterVersion1>() != _inputHeader.EffectsSize)
{
return ResultCode.InvalidUpdateInfo;
}
int initialOutputSize = _output.Length;
- ReadOnlySpan<EffectInParameter> parameters = MemoryMarshal.Cast<byte, EffectInParameter>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span);
+ ReadOnlySpan<EffectInParameterVersion1> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion1>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span);
_input = _input.Slice((int)_inputHeader.EffectsSize);
@@ -273,9 +344,9 @@ namespace Ryujinx.Audio.Renderer.Server
for (int i = 0; i < context.GetCount(); i++)
{
- EffectInParameter parameter = parameters[i];
+ EffectInParameterVersion1 parameter = parameters[i];
- ref EffectOutStatus outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatus>(ref _output)[0];
+ ref EffectOutStatusVersion1 outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatusVersion1>(ref _output)[0];
ref BaseEffect effect = ref context.GetEffect(i);
@@ -296,7 +367,7 @@ namespace Ryujinx.Audio.Renderer.Server
int currentOutputSize = _output.Length;
- OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatus>() * context.GetCount());
+ OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatusVersion1>() * context.GetCount());
OutputHeader.TotalSize += OutputHeader.EffectsSize;
Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize);
diff --git a/Ryujinx.Tests/Audio/Renderer/EffectInfoParameterTests.cs b/Ryujinx.Tests/Audio/Renderer/EffectInfoParameterTests.cs
index 2b482cdb..c4ac82f0 100644
--- a/Ryujinx.Tests/Audio/Renderer/EffectInfoParameterTests.cs
+++ b/Ryujinx.Tests/Audio/Renderer/EffectInfoParameterTests.cs
@@ -9,7 +9,8 @@ namespace Ryujinx.Tests.Audio.Renderer
[Test]
public void EnsureTypeSize()
{
- Assert.AreEqual(0xC0, Unsafe.SizeOf<EffectInParameter>());
+ Assert.AreEqual(0xC0, Unsafe.SizeOf<EffectInParameterVersion1>());
+ Assert.AreEqual(0xC0, Unsafe.SizeOf<EffectInParameterVersion2>());
}
}
}
diff --git a/Ryujinx.Tests/Audio/Renderer/EffectOutStatusTests.cs b/Ryujinx.Tests/Audio/Renderer/EffectOutStatusTests.cs
index 199bcf8a..8cb57da3 100644
--- a/Ryujinx.Tests/Audio/Renderer/EffectOutStatusTests.cs
+++ b/Ryujinx.Tests/Audio/Renderer/EffectOutStatusTests.cs
@@ -9,7 +9,8 @@ namespace Ryujinx.Tests.Audio.Renderer
[Test]
public void EnsureTypeSize()
{
- Assert.AreEqual(0x10, Unsafe.SizeOf<EffectOutStatus>());
+ Assert.AreEqual(0x10, Unsafe.SizeOf<EffectOutStatusVersion1>());
+ Assert.AreEqual(0x90, Unsafe.SizeOf<EffectOutStatusVersion2>());
}
}
}
diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs b/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs
new file mode 100644
index 00000000..8512ebd4
--- /dev/null
+++ b/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs
@@ -0,0 +1,16 @@
+using NUnit.Framework;
+using Ryujinx.Audio.Renderer.Parameter.Effect;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Tests.Audio.Renderer.Parameter.Effect
+{
+ class LimiterParameterTests
+ {
+ [Test]
+ public void EnsureTypeSize()
+ {
+ Assert.AreEqual(0x44, Unsafe.SizeOf<LimiterParameter>());
+ }
+ }
+}
+
diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs b/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs
new file mode 100644
index 00000000..43645ae4
--- /dev/null
+++ b/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs
@@ -0,0 +1,16 @@
+using NUnit.Framework;
+using Ryujinx.Audio.Renderer.Parameter.Effect;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Tests.Audio.Renderer.Parameter.Effect
+{
+ class LimiterStatisticsTests
+ {
+ [Test]
+ public void EnsureTypeSize()
+ {
+ Assert.AreEqual(0x30, Unsafe.SizeOf<LimiterStatistics>());
+ }
+ }
+}
+