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
This commit is contained in:
Mary-nyan 2022-11-28 08:28:45 +01:00 committed by GitHub
parent 472119c8da
commit dff138229c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 140 additions and 58 deletions

View file

@ -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 coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 0);
short coefficient1 = coefficients[coefficientIndex * 2 + 1]; 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]; coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2);
coefficient1 = coefficients[coefficientIndex * 2 + 1]; coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1);
nibbles += 2; nibbles += 2;

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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)
{ {

View file

@ -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; }

View file

@ -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; }

View file

@ -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 DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices);
// TODO: Update delay processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping. DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, OutputBufferIndices);
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices);
DataSourceHelper.RemapChannelResourceMappingToLegacy(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, Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f,
0.0f, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain,
delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f,
0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f,
delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, feedbackGain); 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);

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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. // 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. // As such we currently only implement a generic path for simplicity for double biquad.
// TODO: Implement double biquad filters fast path.
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);

View file

@ -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);

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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; }

View file

@ -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;
}
} }
} }

View file

@ -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>

View file

@ -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"/>.

View file

@ -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;
} }

View file

@ -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,
} }
} }

View file

@ -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();
}
} }
} }

View file

@ -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)

View file

@ -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();
} }
} }