aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs
diff options
context:
space:
mode:
authorMary <me@thog.eu>2021-05-25 19:01:09 +0200
committerGitHub <noreply@github.com>2021-05-25 19:01:09 +0200
commitf3b0b4831c323a20393aa0388f947317354372b7 (patch)
tree478412fd3a8c2de6eab5e54dd47944f59bf1a836 /Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs
parent54ea2285f05ef6f59a6f1c63df4a7bdd77d7b883 (diff)
amadeus: Update to REV9 (#2309)
* amadeus: Update to REV9 This implements all the changes made with REV9 on 12.0.0. * Address Ac_k's comments
Diffstat (limited to 'Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs')
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs179
1 files changed, 179 insertions, 0 deletions
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]));
+ }
+ }
+ }
+ }
+ }
+}