Просмотр исходного кода

amadeus: Fixes and initial 15.0.0 support (#3908)

* amadeus: Allow OOB read of GC-ADPCM coefficients

Fixes "Ninja Gaiden Sigma 2" and possibly "NINJA GAIDEN 3: Razor's Edge"

* amadeus: Fix wrong variable usage in delay effect

We should transform the delay line values, not the input.

* amadeus: Update GroupedBiquadFilterCommand documentation

* amadeus: Simplify PoolMapper alignment checks

* amadeus: Update Surround delay effect matrix to REV11

* amadeus: Add drop parameter support and use 32 bits integers for estimate time

Also implement accurate ExecuteAudioRendererRendering stub.

* Address gdkchan's comments

* Address gdkchan's other comments

* Address gdkchan's comment
Mary-nyan 3 лет назад
Родитель
Сommit
dff138229c
37 измененных файлов с 140 добавлено и 58 удалено
  1. 18 4
      Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs
  2. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs
  3. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs
  4. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs
  5. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs
  6. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs
  7. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs
  8. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs
  9. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs
  10. 15 15
      Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs
  11. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs
  12. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs
  13. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs
  14. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs
  15. 3 4
      Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs
  16. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs
  17. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs
  18. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs
  19. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs
  20. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs
  21. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs
  22. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs
  23. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs
  24. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs
  25. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs
  26. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs
  27. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs
  28. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs
  29. 1 1
      Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs
  30. 30 4
      Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
  31. 2 1
      Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
  32. 1 1
      Ryujinx.Audio/Renderer/Server/CommandBuffer.cs
  33. 2 2
      Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs
  34. 1 0
      Ryujinx.Audio/ResultCode.cs
  35. 11 1
      Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs
  36. 29 0
      Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs
  37. 2 0
      Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs

+ 18 - 4
Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs

@@ -1,4 +1,5 @@
 using Ryujinx.Audio.Renderer.Dsp.State;
 using Ryujinx.Audio.Renderer.Dsp.State;
+using Ryujinx.Common.Logging;
 using System;
 using System;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
@@ -71,6 +72,19 @@ namespace Ryujinx.Audio.Renderer.Dsp
             return (short)value;
             return (short)value;
         }
         }
 
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
+        {
+            if ((uint)index > (uint)coefficients.Length)
+            {
+                Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
+
+                return 0;
+            }
+
+            return coefficients[index];
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
         public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
         {
         {
@@ -84,8 +98,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
             byte coefficientIndex = (byte)((predScale >> 4) & 0xF);
             byte coefficientIndex = (byte)((predScale >> 4) & 0xF);
             short history0 = loopContext.History0;
             short history0 = loopContext.History0;
             short history1 = loopContext.History1;
             short history1 = loopContext.History1;
-            short coefficient0 = coefficients[coefficientIndex * 2 + 0];
-            short coefficient1 = coefficients[coefficientIndex * 2 + 1];
+            short coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 0);
+            short coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1);
 
 
             int decodedCount = Math.Min(count, endSampleOffset - startSampleOffset - offset);
             int decodedCount = Math.Min(count, endSampleOffset - startSampleOffset - offset);
             int nibbles = GetNibblesFromSampleCount(offset + startSampleOffset);
             int nibbles = GetNibblesFromSampleCount(offset + startSampleOffset);
@@ -109,8 +123,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
 
 
                     coefficientIndex = (byte)((predScale >> 4) & 0xF);
                     coefficientIndex = (byte)((predScale >> 4) & 0xF);
 
 
-                    coefficient0 = coefficients[coefficientIndex * 2 + 0];
-                    coefficient1 = coefficients[coefficientIndex * 2 + 1];
+                    coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2);
+                    coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1);
 
 
                     nibbles += 2;
                     nibbles += 2;
 
 

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs

@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.AdpcmDataSourceVersion1;
         public CommandType CommandType => CommandType.AdpcmDataSourceVersion1;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public uint SampleRate { get; }
         public uint SampleRate { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs

@@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.AuxiliaryBuffer;
         public CommandType CommandType => CommandType.AuxiliaryBuffer;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public uint InputBufferIndex { get; }
         public uint InputBufferIndex { get; }
         public uint OutputBufferIndex { get; }
         public uint OutputBufferIndex { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs

@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.BiquadFilter;
         public CommandType CommandType => CommandType.BiquadFilter;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public Memory<BiquadFilterState> BiquadFilterState { get; }
         public Memory<BiquadFilterState> BiquadFilterState { get; }
         public int InputBufferIndex { get; }
         public int InputBufferIndex { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs

@@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.CaptureBuffer;
         public CommandType CommandType => CommandType.CaptureBuffer;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public uint InputBufferIndex { get; }
         public uint InputBufferIndex { get; }
 
 

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs

@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.CircularBufferSink;
         public CommandType CommandType => CommandType.CircularBufferSink;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort[] Input { get; }
         public ushort[] Input { get; }
         public uint InputCount { get; }
         public uint InputCount { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs

@@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.ClearMixBuffer;
         public CommandType CommandType => CommandType.ClearMixBuffer;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ClearMixBufferCommand(int nodeId)
         public ClearMixBufferCommand(int nodeId)
         {
         {

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs

@@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.CopyMixBuffer;
         public CommandType CommandType => CommandType.CopyMixBuffer;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort InputBufferIndex { get; }
         public ushort InputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs

@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType { get; }
         public CommandType CommandType { get; }
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public uint SampleRate { get; }
         public uint SampleRate { get; }

+ 15 - 15
Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs

@@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.Delay;
         public CommandType CommandType => CommandType.Delay;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public DelayParameter Parameter => _parameter;
         public DelayParameter Parameter => _parameter;
         public Memory<DelayState> State { get; }
         public Memory<DelayState> State { get; }
@@ -49,15 +49,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
                 OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
                 OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
             }
             }
 
 
-            // NOTE: We do the opposite as Nintendo here for now to restore previous behaviour
-            // TODO: Update delay processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping.
-            DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices);
-            DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices);
+            DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices);
+            DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, OutputBufferIndices);
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
         [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
         private unsafe void ProcessDelayMono(ref DelayState state, float* outputBuffer, float* inputBuffer, uint sampleCount)
         private unsafe void ProcessDelayMono(ref DelayState state, float* outputBuffer, float* inputBuffer, uint sampleCount)
         {
         {
+            const ushort channelCount = 1;
+
             float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision);
             float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision);
             float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision);
             float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision);
             float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
             float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
@@ -70,7 +70,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
                 float temp = input * inGain + delayLineValue * feedbackGain;
                 float temp = input * inGain + delayLineValue * feedbackGain;
 
 
-                state.UpdateLowPassFilter(ref temp, 1);
+                state.UpdateLowPassFilter(ref temp, channelCount);
 
 
                 outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64;
                 outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64;
             }
             }
@@ -104,7 +104,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
                     Y = state.DelayLines[1].Read(),
                     Y = state.DelayLines[1].Read(),
                 };
                 };
 
 
-                Vector2 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
+                Vector2 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain;
 
 
                 state.UpdateLowPassFilter(ref Unsafe.As<Vector2, float>(ref temp), channelCount);
                 state.UpdateLowPassFilter(ref Unsafe.As<Vector2, float>(ref temp), channelCount);
 
 
@@ -148,7 +148,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
                     W = state.DelayLines[3].Read()
                     W = state.DelayLines[3].Read()
                 };
                 };
 
 
-                Vector4 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
+                Vector4 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain;
 
 
                 state.UpdateLowPassFilter(ref Unsafe.As<Vector4, float>(ref temp), channelCount);
                 state.UpdateLowPassFilter(ref Unsafe.As<Vector4, float>(ref temp), channelCount);
 
 
@@ -171,12 +171,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
             float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
             float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
             float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
             float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
 
 
-            Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain,
-                                                    0.0f, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f,
-                                                    delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f,
-                                                    0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f,
-                                                    delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackBaseGain, 0.0f,
-                                                    0.0f, 0.0f, 0.0f, 0.0f, 0.0f, feedbackGain);
+            Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f,
+                                                    0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain,
+                                                    delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f,
+                                                    0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f,
+                                                    delayFeedbackCrossGain, 0.0f, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
+                                                    0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackBaseGain);
 
 
             for (int i = 0; i < sampleCount; i++)
             for (int i = 0; i < sampleCount; i++)
             {
             {
@@ -200,7 +200,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
                     U = state.DelayLines[5].Read()
                     U = state.DelayLines[5].Read()
                 };
                 };
 
 
-                Vector6 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
+                Vector6 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain;
 
 
                 state.UpdateLowPassFilter(ref Unsafe.As<Vector6, float>(ref temp), channelCount);
                 state.UpdateLowPassFilter(ref Unsafe.As<Vector6, float>(ref temp), channelCount);
 
 

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs

@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.DepopForMixBuffers;
         public CommandType CommandType => CommandType.DepopForMixBuffers;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public uint MixBufferOffset { get; }
         public uint MixBufferOffset { get; }
 
 

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs

@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.DepopPrepare;
         public CommandType CommandType => CommandType.DepopPrepare;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public uint MixBufferCount { get; }
         public uint MixBufferCount { get; }
 
 

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs

@@ -14,7 +14,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.DeviceSink;
         public CommandType CommandType => CommandType.DeviceSink;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public string DeviceName { get; }
         public string DeviceName { get; }
 
 

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs

@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.DownMixSurroundToStereo;
         public CommandType CommandType => CommandType.DownMixSurroundToStereo;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort[] InputBufferIndices { get; }
         public ushort[] InputBufferIndices { get; }
         public ushort[] OutputBufferIndices { get; }
         public ushort[] OutputBufferIndices { get; }

+ 3 - 4
Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs

@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.GroupedBiquadFilter;
         public CommandType CommandType => CommandType.GroupedBiquadFilter;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         private BiquadFilterParameter[] _parameters;
         private BiquadFilterParameter[] _parameters;
         private Memory<BiquadFilterState> _biquadFilterStates;
         private Memory<BiquadFilterState> _biquadFilterStates;
@@ -47,9 +47,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
                 }
                 }
             }
             }
 
 
-            // NOTE: Nintendo also implements a hot path for double biquad filters, but no generic path when the command definition suggests it could be done.
-            // As such we currently only implement a generic path for simplicity.
-            // TODO: Implement double biquad filters fast path.
+            // NOTE: Nintendo only implement single and double biquad filters but no generic path when the command definition suggests it could be done.
+            // As such we currently only implement a generic path for simplicity for double biquad.
             if (_parameters.Length == 1)
             if (_parameters.Length == 1)
             {
             {
                 BiquadFilterHelper.ProcessBiquadFilter(ref _parameters[0], ref states[0], outputBuffer, inputBuffer, context.SampleCount);
                 BiquadFilterHelper.ProcessBiquadFilter(ref _parameters[0], ref states[0], outputBuffer, inputBuffer, context.SampleCount);

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs

@@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType { get; }
         public CommandType CommandType { get; }
 
 
-        public ulong EstimatedProcessingTime { get; }
+        public uint EstimatedProcessingTime { get; }
 
 
         public void Process(CommandList context);
         public void Process(CommandList context);
 
 

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs

@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.LimiterVersion1;
         public CommandType CommandType => CommandType.LimiterVersion1;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public LimiterParameter Parameter => _parameter;
         public LimiterParameter Parameter => _parameter;
         public Memory<LimiterState> State { get; }
         public Memory<LimiterState> State { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs

@@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.LimiterVersion2;
         public CommandType CommandType => CommandType.LimiterVersion2;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public LimiterParameter Parameter => _parameter;
         public LimiterParameter Parameter => _parameter;
         public Memory<LimiterState> State { get; }
         public Memory<LimiterState> State { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs

@@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.Mix;
         public CommandType CommandType => CommandType.Mix;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort InputBufferIndex { get; }
         public ushort InputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs

@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.MixRamp;
         public CommandType CommandType => CommandType.MixRamp;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort InputBufferIndex { get; }
         public ushort InputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs

@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.MixRampGrouped;
         public CommandType CommandType => CommandType.MixRampGrouped;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public uint MixBufferCount { get; }
         public uint MixBufferCount { get; }
 
 

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs

@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.PcmFloatDataSourceVersion1;
         public CommandType CommandType => CommandType.PcmFloatDataSourceVersion1;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public uint SampleRate { get; }
         public uint SampleRate { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs

@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.PcmInt16DataSourceVersion1;
         public CommandType CommandType => CommandType.PcmInt16DataSourceVersion1;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public uint SampleRate { get; }
         public uint SampleRate { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs

@@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.Performance;
         public CommandType CommandType => CommandType.Performance;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public PerformanceEntryAddresses PerformanceEntryAddresses { get; }
         public PerformanceEntryAddresses PerformanceEntryAddresses { get; }
 
 

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs

@@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.Reverb3d;
         public CommandType CommandType => CommandType.Reverb3d;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort InputBufferIndex { get; }
         public ushort InputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs

@@ -34,7 +34,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.Reverb;
         public CommandType CommandType => CommandType.Reverb;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ReverbParameter Parameter => _parameter;
         public ReverbParameter Parameter => _parameter;
         public Memory<ReverbState> State { get; }
         public Memory<ReverbState> State { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs

@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.Upsample;
         public CommandType CommandType => CommandType.Upsample;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public uint BufferCount { get; }
         public uint BufferCount { get; }
         public uint InputBufferIndex { get; }
         public uint InputBufferIndex { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs

@@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.Volume;
         public CommandType CommandType => CommandType.Volume;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort InputBufferIndex { get; }
         public ushort InputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }

+ 1 - 1
Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs

@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
 
         public CommandType CommandType => CommandType.VolumeRamp;
         public CommandType CommandType => CommandType.VolumeRamp;
 
 
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         public ushort InputBufferIndex { get; }
         public ushort InputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }
         public ushort OutputBufferIndex { get; }

+ 30 - 4
Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs

@@ -28,6 +28,7 @@ namespace Ryujinx.Audio.Renderer.Server
     {
     {
         private object _lock = new object();
         private object _lock = new object();
 
 
+        private AudioRendererRenderingDevice _renderingDevice;
         private AudioRendererExecutionMode _executionMode;
         private AudioRendererExecutionMode _executionMode;
         private IWritableEvent _systemEvent;
         private IWritableEvent _systemEvent;
         private ManualResetEvent _terminationEvent;
         private ManualResetEvent _terminationEvent;
@@ -63,6 +64,7 @@ namespace Ryujinx.Audio.Renderer.Server
         private uint _renderingTimeLimitPercent;
         private uint _renderingTimeLimitPercent;
         private bool _voiceDropEnabled;
         private bool _voiceDropEnabled;
         private uint _voiceDropCount;
         private uint _voiceDropCount;
+        private float _voiceDropParameter;
         private bool _isDspRunningBehind;
         private bool _isDspRunningBehind;
 
 
         private ICommandProcessingTimeEstimator _commandProcessingTimeEstimator;
         private ICommandProcessingTimeEstimator _commandProcessingTimeEstimator;
@@ -95,6 +97,7 @@ namespace Ryujinx.Audio.Renderer.Server
 
 
             _totalElapsedTicksUpdating = 0;
             _totalElapsedTicksUpdating = 0;
             _sessionId = 0;
             _sessionId = 0;
+            _voiceDropParameter = 1.0f;
         }
         }
 
 
         public ResultCode Initialize(
         public ResultCode Initialize(
@@ -130,6 +133,7 @@ namespace Ryujinx.Audio.Renderer.Server
             _upsamplerCount = parameter.SinkCount + parameter.SubMixBufferCount;
             _upsamplerCount = parameter.SinkCount + parameter.SubMixBufferCount;
             _appletResourceId = appletResourceId;
             _appletResourceId = appletResourceId;
             _memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount;
             _memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount;
+            _renderingDevice = parameter.RenderingDevice;
             _executionMode = parameter.ExecutionMode;
             _executionMode = parameter.ExecutionMode;
             _sessionId = sessionId;
             _sessionId = sessionId;
             MemoryManager = memoryManager;
             MemoryManager = memoryManager;
@@ -337,6 +341,7 @@ namespace Ryujinx.Audio.Renderer.Server
 
 
             _processHandle = processHandle;
             _processHandle = processHandle;
             _elapsedFrameCount = 0;
             _elapsedFrameCount = 0;
+            _voiceDropParameter = 1.0f;
 
 
             switch (_behaviourContext.GetCommandProcessingTimeEstimatorVersion())
             switch (_behaviourContext.GetCommandProcessingTimeEstimatorVersion())
             {
             {
@@ -515,7 +520,7 @@ namespace Ryujinx.Audio.Renderer.Server
             return (ulong)(_manager.TickSource.ElapsedSeconds * Constants.TargetTimerFrequency);
             return (ulong)(_manager.TickSource.ElapsedSeconds * Constants.TargetTimerFrequency);
         }
         }
 
 
-        private uint ComputeVoiceDrop(CommandBuffer commandBuffer, long voicesEstimatedTime, long deltaTimeDsp)
+        private uint ComputeVoiceDrop(CommandBuffer commandBuffer, uint voicesEstimatedTime, long deltaTimeDsp)
         {
         {
             int i;
             int i;
 
 
@@ -584,7 +589,7 @@ namespace Ryujinx.Audio.Renderer.Server
                     {
                     {
                         command.Enabled = false;
                         command.Enabled = false;
 
 
-                        voicesEstimatedTime -= (long)command.EstimatedProcessingTime;
+                        voicesEstimatedTime -= (uint)(_voiceDropParameter * command.EstimatedProcessingTime);
                     }
                     }
                 }
                 }
             }
             }
@@ -618,13 +623,13 @@ namespace Ryujinx.Audio.Renderer.Server
             _voiceContext.Sort();
             _voiceContext.Sort();
             commandGenerator.GenerateVoices();
             commandGenerator.GenerateVoices();
 
 
-            long voicesEstimatedTime = (long)commandBuffer.EstimatedProcessingTime;
+            uint voicesEstimatedTime = (uint)(_voiceDropParameter * commandBuffer.EstimatedProcessingTime);
 
 
             commandGenerator.GenerateSubMixes();
             commandGenerator.GenerateSubMixes();
             commandGenerator.GenerateFinalMixes();
             commandGenerator.GenerateFinalMixes();
             commandGenerator.GenerateSinks();
             commandGenerator.GenerateSinks();
 
 
-            long totalEstimatedTime = (long)commandBuffer.EstimatedProcessingTime;
+            uint totalEstimatedTime = (uint)(_voiceDropParameter * commandBuffer.EstimatedProcessingTime);
 
 
             if (_voiceDropEnabled)
             if (_voiceDropEnabled)
             {
             {
@@ -856,5 +861,26 @@ namespace Ryujinx.Audio.Renderer.Server
                 }
                 }
             }
             }
         }
         }
+
+        public void SetVoiceDropParameter(float voiceDropParameter)
+        {
+            _voiceDropParameter = Math.Clamp(voiceDropParameter, 0.0f, 2.0f);
+        }
+
+        public float GetVoiceDropParameter()
+        {
+            return _voiceDropParameter;
+        }
+
+        public ResultCode ExecuteAudioRendererRendering()
+        {
+            if (_executionMode == AudioRendererExecutionMode.Manual && _renderingDevice == AudioRendererRenderingDevice.Cpu)
+            {
+                // NOTE: Here Nintendo aborts with this error code, we don't want that.
+                return ResultCode.InvalidExecutionContextOperation;
+            }
+
+            return ResultCode.UnsupportedOperation;
+        }
     }
     }
 }
 }

+ 2 - 1
Ryujinx.Audio/Renderer/Server/BehaviourContext.cs

@@ -94,8 +94,9 @@ namespace Ryujinx.Audio.Renderer.Server
         /// REV11:
         /// REV11:
         /// The "legacy" effects (Delay, Reverb and Reverb 3D) were updated to match the standard channel mapping used by the audio renderer.
         /// The "legacy" effects (Delay, Reverb and Reverb 3D) were updated to match the standard channel mapping used by the audio renderer.
         /// A new version of the command estimator was added to address timing changes caused by the legacy effects changes.
         /// A new version of the command estimator was added to address timing changes caused by the legacy effects changes.
+        /// A voice drop parameter was added in 15.0.0: This allows an application to amplify or attenuate the estimated time of DSP commands.
         /// </summary>
         /// </summary>
-        /// <remarks>This was added in system update 14.0.0</remarks>
+        /// <remarks>This was added in system update 14.0.0 but some changes were made in 15.0.0</remarks>
         public const int Revision11 = 11 << 24;
         public const int Revision11 = 11 << 24;
 
 
         /// <summary>
         /// <summary>

+ 1 - 1
Ryujinx.Audio/Renderer/Server/CommandBuffer.cs

@@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Server
         /// <summary>
         /// <summary>
         /// The estimated total processing time.
         /// The estimated total processing time.
         /// </summary>
         /// </summary>
-        public ulong EstimatedProcessingTime { get; set; }
+        public uint EstimatedProcessingTime { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// The command list that is populated by the <see cref="CommandBuffer"/>.
         /// The command list that is populated by the <see cref="CommandBuffer"/>.

+ 2 - 2
Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs

@@ -263,12 +263,12 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
                 return UpdateResult.Success;
                 return UpdateResult.Success;
             }
             }
 
 
-            if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress & (pageSize - 1)) != 0)
+            if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress % pageSize) != 0)
             {
             {
                 return UpdateResult.InvalidParameter;
                 return UpdateResult.InvalidParameter;
             }
             }
 
 
-            if (inParameter.Size == 0 || (inParameter.Size & (pageSize - 1)) != 0)
+            if (inParameter.Size == 0 || (inParameter.Size % pageSize) != 0)
             {
             {
                 return UpdateResult.InvalidParameter;
                 return UpdateResult.InvalidParameter;
             }
             }

+ 1 - 0
Ryujinx.Audio/ResultCode.cs

@@ -17,5 +17,6 @@ namespace Ryujinx.Audio
         InvalidAddressInfo = (42 << ErrorCodeShift) | ModuleId,
         InvalidAddressInfo = (42 << ErrorCodeShift) | ModuleId,
         InvalidMixSorting = (43 << ErrorCodeShift) | ModuleId,
         InvalidMixSorting = (43 << ErrorCodeShift) | ModuleId,
         UnsupportedOperation = (513 << ErrorCodeShift) | ModuleId,
         UnsupportedOperation = (513 << ErrorCodeShift) | ModuleId,
+        InvalidExecutionContextOperation = (514 << ErrorCodeShift) | ModuleId,
     }
     }
 }
 }

+ 11 - 1
Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs

@@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
 
 
         public ResultCode ExecuteAudioRendererRendering()
         public ResultCode ExecuteAudioRendererRendering()
         {
         {
-            throw new NotImplementedException();
+            return (ResultCode)_impl.ExecuteAudioRendererRendering();
         }
         }
 
 
         public uint GetMixBufferCount()
         public uint GetMixBufferCount()
@@ -108,5 +108,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
                 _impl.Dispose();
                 _impl.Dispose();
             }
             }
         }
         }
+
+        public void SetVoiceDropParameter(float voiceDropParameter)
+        {
+            _impl.SetVoiceDropParameter(voiceDropParameter);
+        }
+
+        public float GetVoiceDropParameter()
+        {
+            return _impl.GetVoiceDropParameter();
+        }
     }
     }
 }
 }

+ 29 - 0
Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs

@@ -172,6 +172,35 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
             return result;
             return result;
         }
         }
 
 
+        [CommandHipc(11)] // 3.0.0+
+        // ExecuteAudioRendererRendering()
+        public ResultCode ExecuteAudioRendererRendering(ServiceCtx context)
+        {
+            return _impl.ExecuteAudioRendererRendering();
+        }
+
+        [CommandHipc(12)] // 15.0.0+
+        // SetVoiceDropParameter(f32 voiceDropParameter)
+        public ResultCode SetVoiceDropParameter(ServiceCtx context)
+        {
+            float voiceDropParameter = context.RequestData.ReadSingle();
+
+            _impl.SetVoiceDropParameter(voiceDropParameter);
+
+            return ResultCode.Success;
+        }
+
+        [CommandHipc(13)] // 15.0.0+
+        // GetVoiceDropParameter() -> f32 voiceDropParameter
+        public ResultCode GetVoiceDropParameter(ServiceCtx context)
+        {
+            float voiceDropParameter = _impl.GetVoiceDropParameter();
+
+            context.ResponseData.Write(voiceDropParameter);
+
+            return ResultCode.Success;
+        }
+
         protected override void Dispose(bool isDisposing)
         protected override void Dispose(bool isDisposing)
         {
         {
             if (isDisposing)
             if (isDisposing)

+ 2 - 0
Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs

@@ -16,5 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
         void SetRenderingTimeLimit(uint percent);
         void SetRenderingTimeLimit(uint percent);
         uint GetRenderingTimeLimit();
         uint GetRenderingTimeLimit();
         ResultCode ExecuteAudioRendererRendering();
         ResultCode ExecuteAudioRendererRendering();
+        void SetVoiceDropParameter(float voiceDropParameter);
+        float GetVoiceDropParameter();
     }
     }
 }
 }