diff options
author | Mary <me@thog.eu> | 2021-05-25 19:01:09 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-25 19:01:09 +0200 |
commit | f3b0b4831c323a20393aa0388f947317354372b7 (patch) | |
tree | 478412fd3a8c2de6eab5e54dd47944f59bf1a836 /Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs | |
parent | 54ea2285f05ef6f59a6f1c63df4a7bdd77d7b883 (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.cs | 179 |
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])); + } + } + } + } + } +} |