aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs')
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs173
1 files changed, 173 insertions, 0 deletions
diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs
new file mode 100644
index 00000000..8c344293
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Diagnostics;
+using Ryujinx.Audio.Renderer.Dsp.Effect;
+using Ryujinx.Audio.Renderer.Dsp.State;
+using Ryujinx.Audio.Renderer.Parameter.Effect;
+
+namespace Ryujinx.Audio.Renderer.Dsp.Command
+{
+ public class CompressorCommand : ICommand
+ {
+ private const int FixedPointPrecision = 15;
+
+ public bool Enabled { get; set; }
+
+ public int NodeId { get; }
+
+ public CommandType CommandType => CommandType.Compressor;
+
+ public uint EstimatedProcessingTime { get; set; }
+
+ public CompressorParameter Parameter => _parameter;
+ public Memory<CompressorState> State { get; }
+ public ushort[] OutputBufferIndices { get; }
+ public ushort[] InputBufferIndices { get; }
+ public bool IsEffectEnabled { get; }
+
+ private CompressorParameter _parameter;
+
+ public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
+ {
+ Enabled = true;
+ NodeId = nodeId;
+ _parameter = parameter;
+ State = state;
+
+ 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 CompressorState state = ref State.Span[0];
+
+ if (IsEffectEnabled)
+ {
+ if (_parameter.Status == Server.Effect.UsageState.Invalid)
+ {
+ state = new CompressorState(ref _parameter);
+ }
+ else if (_parameter.Status == Server.Effect.UsageState.New)
+ {
+ state.UpdateParameter(ref _parameter);
+ }
+ }
+
+ ProcessCompressor(context, ref state);
+ }
+
+ private unsafe void ProcessCompressor(CommandList context, ref CompressorState state)
+ {
+ Debug.Assert(_parameter.IsChannelCountValid());
+
+ if (IsEffectEnabled && _parameter.IsChannelCountValid())
+ {
+ Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
+ Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
+ Span<float> channelInput = stackalloc float[Parameter.ChannelCount];
+ ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
+ float unknown4 = state.Unknown4;
+ ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
+ float previousCompressionEmaAlpha = state.PreviousCompressionEmaAlpha;
+
+ for (int i = 0; i < _parameter.ChannelCount; i++)
+ {
+ inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
+ outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
+ }
+
+ for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
+ {
+ for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
+ {
+ channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
+ }
+
+ float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
+ float y = FloatingPointHelper.Log10(newMean) * 10.0f;
+ float z = 0.0f;
+
+ bool unknown10OutOfRange = false;
+
+ if (newMean < 1.0e-10f)
+ {
+ z = 1.0f;
+
+ unknown10OutOfRange = state.Unknown10 < -100.0f;
+ }
+
+ if (y >= state.Unknown10 || unknown10OutOfRange)
+ {
+ float tmpGain;
+
+ if (y >= state.Unknown14)
+ {
+ tmpGain = ((1.0f / Parameter.Ratio) - 1.0f) * (y - Parameter.Threshold);
+ }
+ else
+ {
+ tmpGain = (y - state.Unknown10) * ((y - state.Unknown10) * -state.CompressorGainReduction);
+ }
+
+ z = FloatingPointHelper.DecibelToLinearExtended(tmpGain);
+ }
+
+ float unknown4New = z;
+ float compressionEmaAlpha;
+
+ if ((unknown4 - z) <= 0.08f)
+ {
+ compressionEmaAlpha = Parameter.ReleaseCoefficient;
+
+ if ((unknown4 - z) >= -0.08f)
+ {
+ if (MathF.Abs(compressionGainAverage.Read() - z) >= 0.001f)
+ {
+ unknown4New = unknown4;
+ }
+
+ compressionEmaAlpha = previousCompressionEmaAlpha;
+ }
+ }
+ else
+ {
+ compressionEmaAlpha = Parameter.AttackCoefficient;
+ }
+
+ float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha);
+
+ for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
+ {
+ *((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain;
+ }
+
+ unknown4 = unknown4New;
+ previousCompressionEmaAlpha = compressionEmaAlpha;
+ }
+
+ state.InputMovingAverage = inputMovingAverage;
+ state.Unknown4 = unknown4;
+ state.CompressionGainAverage = compressionGainAverage;
+ state.PreviousCompressionEmaAlpha = previousCompressionEmaAlpha;
+ }
+ else
+ {
+ for (int i = 0; i < Parameter.ChannelCount; i++)
+ {
+ if (InputBufferIndices[i] != OutputBufferIndices[i])
+ {
+ context.CopyBuffer(OutputBufferIndices[i], InputBufferIndices[i]);
+ }
+ }
+ }
+ }
+ }
+}