From 1ef09717d232cc3a142b10dc1ea977b37d123c9a Mon Sep 17 00:00:00 2001 From: LotP <22-lotp@users.noreply.git.ryujinx.app> Date: Sat, 11 Oct 2025 02:11:39 -0500 Subject: [PATCH] SDK20 and REV15 support * Fixed an issue where games would boot loop because of an incorrect HID state. * Turns out the SamplingNumber of the atomic input storage doesn't match the SamplingNumber of the input state held by the atomic storage, instead it is exactly double the value in the input state. * Added new Condition struct to the HID Shared memory and populate it with dummy data to fix the no-controller crash (already merged). * The audio renderer has been mostly updated to rev15, allowing rev15 games to launch. * Biquad filters now use floats. * Several structures have been renamed to match the SDK names, making it easier to compare functionality. A few names are still missing and will be changed at a later date. * The new commands from rev15 have been added to the CommandType enum, but they are still missing from the code itself. * Due to changes in the SDK layout, the time estimation functions are either missing or very well hidden (or Ghidra search functionality is useless). We can't fully implement the new commands until the timing data has been located. * A few minor tweaks to the code have been made to more accurately match the SDK. --- .../Renderer/Common/BehaviourParameter.cs | 8 +- .../{VoiceUpdateState.cs => VoiceState.cs} | 5 +- .../Renderer/Dsp/BiquadFilterHelper.cs | 238 +++++++--- .../Command/AdpcmDataSourceCommandVersion1.cs | 18 +- .../Dsp/Command/BiquadFilterAndMixCommand.cs | 8 +- .../Dsp/Command/BiquadFilterCommand.cs | 4 +- .../Renderer/Dsp/Command/CommandList.cs | 2 +- .../Renderer/Dsp/Command/CommandType.cs | 9 + .../Dsp/Command/DataSourceVersion2Command.cs | 24 +- .../Dsp/Command/DepopPrepareCommand.cs | 6 +- .../Renderer/Dsp/Command/DeviceSinkCommand.cs | 4 +- .../Renderer/Dsp/Command/FillBufferCommand.cs | 69 +++ .../Renderer/Dsp/Command/MixRampCommand.cs | 4 +- .../Dsp/Command/MixRampGroupedCommand.cs | 6 +- .../MultiTapBiquadFilterAndMixCommand.cs | 12 +- .../Command/MultiTapBiquadFilterCommand.cs | 4 +- .../PcmFloatDataSourceCommandVersion1.cs | 16 +- .../PcmInt16DataSourceCommandVersion1.cs | 16 +- .../Renderer/Dsp/Command/UpsampleCommand.cs | 4 +- .../Renderer/Dsp/DataSourceHelper.cs | 4 +- .../Renderer/Dsp/ResamplerHelper.cs | 2 +- .../Parameter/AudioRendererConfiguration.cs | 3 +- ...Parameter.cs => BiquadFilterParameter1.cs} | 2 +- .../Parameter/BiquadFilterParameter2.cs | 36 ++ ...ter.cs => BiquadFilterEffectParameter1.cs} | 2 +- .../Effect/BiquadFilterEffectParameter2.cs | 49 +++ .../Parameter/EffectInParameterVersion3.cs | 97 ++++ .../ISplitterDestinationInParameter.cs | 4 +- .../Renderer/Parameter/SinkInParameter.cs | 2 +- .../SplitterDestinationInParameterVersion1.cs | 4 +- ...SplitterDestinationInParameterVersion2a.cs | 100 +++++ ...plitterDestinationInParameterVersion2b.cs} | 6 +- ...iceInParameter.cs => VoiceInParameter1.cs} | 10 +- .../Renderer/Parameter/VoiceInParameter2.cs | 176 ++++++++ .../Renderer/Parameter/VoiceOutStatus.cs | 6 +- .../Renderer/Server/AudioRenderSystem.cs | 131 +++--- .../{BehaviourContext.cs => BehaviourInfo.cs} | 98 +++-- .../Renderer/Server/CommandBuffer.cs | 90 ++-- .../Renderer/Server/CommandGenerator.cs | 183 ++++---- .../CommandProcessingTimeEstimatorVersion1.cs | 5 + .../CommandProcessingTimeEstimatorVersion2.cs | 5 + .../CommandProcessingTimeEstimatorVersion3.cs | 7 +- .../CommandProcessingTimeEstimatorVersion5.cs | 5 + .../Renderer/Server/Effect/BaseEffect.cs | 13 + .../Server/Effect/BiquadFilterEffect.cs | 22 +- .../Server/ICommandProcessingTimeEstimator.cs | 1 + .../Renderer/Server/MemoryPool/AddressInfo.cs | 34 +- .../{MemoryPoolState.cs => MemoryPoolInfo.cs} | 38 +- .../Renderer/Server/MemoryPool/PoolMapper.cs | 76 ++-- .../Renderer/Server/Mix/MixContext.cs | 54 +-- .../Server/Mix/{MixState.cs => MixInfo.cs} | 21 +- .../Server/Performance/PerformanceManager.cs | 12 +- .../Renderer/Server/RendererSystemContext.cs | 9 +- .../Renderer/Server/Sink/DeviceSink.cs | 6 +- .../Server/Splitter/SplitterContext.cs | 72 ++- .../Server/Splitter/SplitterDestination.cs | 92 ++-- .../Splitter/SplitterDestinationVersion2.cs | 11 +- .../Renderer/Server/StateUpdater.cs | 224 ++++++++-- .../Renderer/Server/Types/PlayState.cs | 6 +- .../{UpsamplerState.cs => UpsamplerInfo.cs} | 12 +- .../Server/Upsampler/UpsamplerManager.cs | 16 +- .../Renderer/Server/Voice/VoiceContext.cs | 58 +-- .../Voice/{VoiceState.cs => VoiceInfo.cs} | 312 ++++++++++--- .../SharedMemory/Common/ISampledDataStruct.cs | 2 +- .../Audio/AudioUserIpcServer.cs | 2 + .../Sdk/Audio/Detail/AudioDevice.cs | 6 +- .../Sdk/Audio/Detail/AudioRendererManager.cs | 4 +- .../Sdk/Audio/Detail/AudioSnoopManager.cs | 42 +- .../Sdk/Audio/Detail/IAudioSnoopManager.cs | 6 + .../Renderer/BiquadFilterParameterTests.cs | 3 +- ...UpdateStateTests.cs => VoiceStateTests.cs} | 4 +- .../Audio/Renderer/Dsp/ResamplerTests.cs | 22 +- .../BiquadFilterEffectParameterTests.cs | 3 +- .../Audio/Renderer/Server/AddressInfoTests.cs | 4 +- .../Renderer/Server/BehaviourContextTests.cs | 413 ------------------ .../Renderer/Server/BehaviourInfoTests.cs | 413 ++++++++++++++++++ ...olStateTests.cs => MemoryPoolInfoTests.cs} | 10 +- .../{MixStateTests.cs => MixInfoTests.cs} | 4 +- .../Audio/Renderer/Server/PoolMapperTests.cs | 16 +- .../Server/SplitterDestinationTests.cs | 2 +- .../{VoiceStateTests.cs => VoiceInfoTests.cs} | 4 +- .../Audio/Renderer/VoiceInParameterTests.cs | 3 +- src/Ryujinx.Tests/Memory/PartialUnmaps.cs | 2 +- 83 files changed, 2343 insertions(+), 1195 deletions(-) rename src/Ryujinx.Audio/Renderer/Common/{VoiceUpdateState.cs => VoiceState.cs} (95%) create mode 100644 src/Ryujinx.Audio/Renderer/Dsp/Command/FillBufferCommand.cs rename src/Ryujinx.Audio/Renderer/Parameter/{BiquadFilterParameter.cs => BiquadFilterParameter1.cs} (95%) create mode 100644 src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter2.cs rename src/Ryujinx.Audio/Renderer/Parameter/Effect/{BiquadFilterEffectParameter.cs => BiquadFilterEffectParameter1.cs} (96%) create mode 100644 src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter2.cs create mode 100644 src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion3.cs create mode 100644 src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2a.cs rename src/Ryujinx.Audio/Renderer/Parameter/{SplitterDestinationInParameterVersion2.cs => SplitterDestinationInParameterVersion2b.cs} (90%) rename src/Ryujinx.Audio/Renderer/Parameter/{VoiceInParameter.cs => VoiceInParameter1.cs} (98%) create mode 100644 src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter2.cs rename src/Ryujinx.Audio/Renderer/Server/{BehaviourContext.cs => BehaviourInfo.cs} (89%) rename src/Ryujinx.Audio/Renderer/Server/MemoryPool/{MemoryPoolState.cs => MemoryPoolInfo.cs} (73%) rename src/Ryujinx.Audio/Renderer/Server/Mix/{MixState.cs => MixInfo.cs} (93%) rename src/Ryujinx.Audio/Renderer/Server/Upsampler/{UpsamplerState.cs => UpsamplerInfo.cs} (82%) rename src/Ryujinx.Audio/Renderer/Server/Voice/{VoiceState.cs => VoiceInfo.cs} (66%) rename src/Ryujinx.Tests/Audio/Renderer/Common/{VoiceUpdateStateTests.cs => VoiceStateTests.cs} (68%) delete mode 100644 src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs create mode 100644 src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourInfoTests.cs rename src/Ryujinx.Tests/Audio/Renderer/Server/{MemoryPoolStateTests.cs => MemoryPoolInfoTests.cs} (80%) rename src/Ryujinx.Tests/Audio/Renderer/Server/{MixStateTests.cs => MixInfoTests.cs} (73%) rename src/Ryujinx.Tests/Audio/Renderer/Server/{VoiceStateTests.cs => VoiceInfoTests.cs} (71%) diff --git a/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs b/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs index 3b8d15dc5..5b4f39e23 100644 --- a/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs @@ -1,9 +1,11 @@ +using Ryujinx.Audio.Renderer.Server; +using Ryujinx.Audio.Renderer.Server.MemoryPool; using System.Runtime.InteropServices; namespace Ryujinx.Audio.Renderer.Common { /// - /// Represents the input parameter for . + /// Represents the input parameter for . /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct BehaviourParameter @@ -21,7 +23,7 @@ namespace Ryujinx.Audio.Renderer.Common /// /// The flags given controlling behaviour of the audio renderer /// - /// See and . + /// See and . public ulong Flags; /// @@ -43,7 +45,7 @@ namespace Ryujinx.Audio.Renderer.Common /// /// Extra information given with the /// - /// This is usually used to report a faulting cpu address when a mapping fail. + /// This is usually used to report a faulting cpu address when a mapping fail. public ulong ExtraErrorInfo; } } diff --git a/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs b/src/Ryujinx.Audio/Renderer/Common/VoiceState.cs similarity index 95% rename from src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs rename to src/Ryujinx.Audio/Renderer/Common/VoiceState.cs index 7f881373f..508daa6e2 100644 --- a/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs +++ b/src/Ryujinx.Audio/Renderer/Common/VoiceState.cs @@ -1,4 +1,5 @@ using Ryujinx.Audio.Renderer.Dsp.State; +using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; using System; @@ -11,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Common /// /// This is shared between the server and audio processor. [StructLayout(LayoutKind.Sequential, Pack = Align)] - public struct VoiceUpdateState + public struct VoiceState { public const int Align = 0x10; public const int BiquadStateOffset = 0x0; @@ -25,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Common /// The total amount of samples that was played. /// /// This is reset to 0 when a finishes playing and is set. - /// This is reset to 0 when looping while is set. + /// This is reset to 0 when looping while is set. public ulong PlayedSampleCount; /// diff --git a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs index 2122f2b44..4f98a8fb5 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs @@ -1,5 +1,7 @@ using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Parameter.Effect; +using Ryujinx.Common.Memory; using System; using System.Runtime.CompilerServices; @@ -9,6 +11,112 @@ namespace Ryujinx.Audio.Renderer.Dsp { private const int FixedPointPrecisionForParameter = 14; + public static BiquadFilterParameter1 ToBiquadFilterParameter1(BiquadFilterParameter2 parameter) + { + BiquadFilterParameter1 result = new() + { + Enable = parameter.Enable, Numerator = new Array3(), Denominator = new Array2() + }; + + Span resultNumeratorSpan = result.Numerator.AsSpan(); + Span resultDenominatorSpan = result.Denominator.AsSpan(); + + Span parameterNumeratorSpan = parameter.Numerator.AsSpan(); + Span parameterDenominatorSpan = parameter.Denominator.AsSpan(); + + + resultNumeratorSpan[0] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[0], FixedPointPrecisionForParameter); + resultNumeratorSpan[1] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[1], FixedPointPrecisionForParameter); + resultNumeratorSpan[2] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[2], FixedPointPrecisionForParameter); + + resultDenominatorSpan[0] = (short)FixedPointHelper.ToFixed(parameterDenominatorSpan[0], FixedPointPrecisionForParameter); + resultDenominatorSpan[1] = (short)FixedPointHelper.ToFixed(parameterDenominatorSpan[1], FixedPointPrecisionForParameter); + + return result; + } + + public static BiquadFilterParameter2 ToBiquadFilterParameter2(BiquadFilterParameter1 parameter) + { + BiquadFilterParameter2 result = new() + { + Enable = parameter.Enable, Numerator = new Array3(), Denominator = new Array2() + }; + + Span resultNumeratorSpan = result.Numerator.AsSpan(); + Span resultDenominatorSpan = result.Denominator.AsSpan(); + + Span parameterNumeratorSpan = parameter.Numerator.AsSpan(); + Span parameterDenominatorSpan = parameter.Denominator.AsSpan(); + + + resultNumeratorSpan[0] = FixedPointHelper.ToFloat(parameterNumeratorSpan[0], FixedPointPrecisionForParameter); + resultNumeratorSpan[1] = FixedPointHelper.ToFloat(parameterNumeratorSpan[1], FixedPointPrecisionForParameter); + resultNumeratorSpan[2] = FixedPointHelper.ToFloat(parameterNumeratorSpan[2], FixedPointPrecisionForParameter); + + resultDenominatorSpan[0] = FixedPointHelper.ToFloat(parameterDenominatorSpan[0], FixedPointPrecisionForParameter); + resultDenominatorSpan[1] = FixedPointHelper.ToFloat(parameterDenominatorSpan[1], FixedPointPrecisionForParameter); + + return result; + } + + public static BiquadFilterEffectParameter1 ToBiquadFilterEffectParameter1(BiquadFilterEffectParameter2 parameter) + { + BiquadFilterEffectParameter1 result = new() + { + Input = parameter.Input, + Output = parameter.Output, + Numerator = new Array3(), + Denominator = new Array2(), + ChannelCount = parameter.ChannelCount, + Status = parameter.Status, + }; + + Span resultNumeratorSpan = result.Numerator.AsSpan(); + Span resultDenominatorSpan = result.Denominator.AsSpan(); + + Span parameterNumeratorSpan = parameter.Numerator.AsSpan(); + Span parameterDenominatorSpan = parameter.Denominator.AsSpan(); + + + resultNumeratorSpan[0] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[0], FixedPointPrecisionForParameter); + resultNumeratorSpan[1] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[1], FixedPointPrecisionForParameter); + resultNumeratorSpan[2] = (short)FixedPointHelper.ToFixed(parameterNumeratorSpan[2], FixedPointPrecisionForParameter); + + resultDenominatorSpan[0] = (short)FixedPointHelper.ToFixed(parameterDenominatorSpan[0], FixedPointPrecisionForParameter); + resultDenominatorSpan[1] = (short)FixedPointHelper.ToFixed(parameterDenominatorSpan[1], FixedPointPrecisionForParameter); + + return result; + } + + public static BiquadFilterEffectParameter2 ToBiquadFilterEffectParameter2(BiquadFilterEffectParameter1 parameter) + { + BiquadFilterEffectParameter2 result = new() + { + Input = parameter.Input, + Output = parameter.Output, + Numerator = new Array3(), + Denominator = new Array2(), + ChannelCount = parameter.ChannelCount, + Status = parameter.Status, + }; + + Span resultNumeratorSpan = result.Numerator.AsSpan(); + Span resultDenominatorSpan = result.Denominator.AsSpan(); + + Span parameterNumeratorSpan = parameter.Numerator.AsSpan(); + Span parameterDenominatorSpan = parameter.Denominator.AsSpan(); + + + resultNumeratorSpan[0] = FixedPointHelper.ToFloat(parameterNumeratorSpan[0], FixedPointPrecisionForParameter); + resultNumeratorSpan[1] = FixedPointHelper.ToFloat(parameterNumeratorSpan[1], FixedPointPrecisionForParameter); + resultNumeratorSpan[2] = FixedPointHelper.ToFloat(parameterNumeratorSpan[2], FixedPointPrecisionForParameter); + + resultDenominatorSpan[0] = FixedPointHelper.ToFloat(parameterDenominatorSpan[0], FixedPointPrecisionForParameter); + resultDenominatorSpan[1] = FixedPointHelper.ToFloat(parameterDenominatorSpan[1], FixedPointPrecisionForParameter); + + return result; + } + /// /// Apply a single biquad filter. /// @@ -20,21 +128,21 @@ namespace Ryujinx.Audio.Renderer.Dsp /// The count of samples to process [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ProcessBiquadFilter( - ref BiquadFilterParameter parameter, + ref BiquadFilterParameter2 parameter, ref BiquadFilterState state, Span outputBuffer, ReadOnlySpan inputBuffer, uint sampleCount) { - Span numeratorSpan = parameter.Numerator.AsSpan(); - Span denominatorSpan = parameter.Denominator.AsSpan(); + Span numeratorSpan = parameter.Numerator.AsSpan(); + Span denominatorSpan = parameter.Denominator.AsSpan(); - float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter); + float a0 = numeratorSpan[0]; + float a1 = numeratorSpan[1]; + float a2 = numeratorSpan[2]; - float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter); + float b1 = denominatorSpan[0]; + float b2 = denominatorSpan[1]; for (int i = 0; i < sampleCount; i++) { @@ -60,22 +168,22 @@ namespace Ryujinx.Audio.Renderer.Dsp /// Mix volume [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ProcessBiquadFilterAndMix( - ref BiquadFilterParameter parameter, + ref BiquadFilterParameter2 parameter, ref BiquadFilterState state, Span outputBuffer, ReadOnlySpan inputBuffer, uint sampleCount, float volume) { - Span numeratorSpan = parameter.Numerator.AsSpan(); - Span denominatorSpan = parameter.Denominator.AsSpan(); + Span numeratorSpan = parameter.Numerator.AsSpan(); + Span denominatorSpan = parameter.Denominator.AsSpan(); - float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter); + float a0 = numeratorSpan[0]; + float a1 = numeratorSpan[1]; + float a2 = numeratorSpan[2]; - float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter); + float b1 = denominatorSpan[0]; + float b2 = denominatorSpan[1]; for (int i = 0; i < sampleCount; i++) { @@ -105,7 +213,7 @@ namespace Ryujinx.Audio.Renderer.Dsp /// Last filtered sample value [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ProcessBiquadFilterAndMixRamp( - ref BiquadFilterParameter parameter, + ref BiquadFilterParameter2 parameter, ref BiquadFilterState state, Span outputBuffer, ReadOnlySpan inputBuffer, @@ -113,15 +221,15 @@ namespace Ryujinx.Audio.Renderer.Dsp float volume, float ramp) { - Span numeratorSpan = parameter.Numerator.AsSpan(); - Span denominatorSpan = parameter.Denominator.AsSpan(); + Span numeratorSpan = parameter.Numerator.AsSpan(); + Span denominatorSpan = parameter.Denominator.AsSpan(); - float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter); + float a0 = numeratorSpan[0]; + float a1 = numeratorSpan[1]; + float a2 = numeratorSpan[2]; - float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter); + float b1 = denominatorSpan[0]; + float b2 = denominatorSpan[1]; float mixState = 0f; @@ -155,7 +263,7 @@ namespace Ryujinx.Audio.Renderer.Dsp /// The count of samples to process [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ProcessBiquadFilter( - ReadOnlySpan parameters, + ReadOnlySpan parameters, Span states, Span outputBuffer, ReadOnlySpan inputBuffer, @@ -163,19 +271,19 @@ namespace Ryujinx.Audio.Renderer.Dsp { for (int stageIndex = 0; stageIndex < parameters.Length; stageIndex++) { - BiquadFilterParameter parameter = parameters[stageIndex]; + BiquadFilterParameter2 parameter = parameters[stageIndex]; ref BiquadFilterState state = ref states[stageIndex]; - Span numeratorSpan = parameter.Numerator.AsSpan(); - Span denominatorSpan = parameter.Denominator.AsSpan(); + Span numeratorSpan = parameter.Numerator.AsSpan(); + Span denominatorSpan = parameter.Denominator.AsSpan(); - float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter); + float a0 = numeratorSpan[0]; + float a1 = numeratorSpan[1]; + float a2 = numeratorSpan[2]; - float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter); + float b1 = denominatorSpan[0]; + float b2 = denominatorSpan[1]; for (int i = 0; i < sampleCount; i++) { @@ -204,8 +312,8 @@ namespace Ryujinx.Audio.Renderer.Dsp /// Mix volume [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ProcessDoubleBiquadFilterAndMix( - ref BiquadFilterParameter parameter0, - ref BiquadFilterParameter parameter1, + ref BiquadFilterParameter2 parameter0, + ref BiquadFilterParameter2 parameter1, ref BiquadFilterState state0, ref BiquadFilterState state1, Span outputBuffer, @@ -213,25 +321,25 @@ namespace Ryujinx.Audio.Renderer.Dsp uint sampleCount, float volume) { - Span numerator0Span = parameter0.Numerator.AsSpan(); - Span numerator1Span = parameter1.Numerator.AsSpan(); - Span denominator0Span = parameter0.Denominator.AsSpan(); - Span denominator1Span = parameter1.Denominator.AsSpan(); + Span numerator0Span = parameter0.Numerator.AsSpan(); + Span numerator1Span = parameter1.Numerator.AsSpan(); + Span denominator0Span = parameter0.Denominator.AsSpan(); + Span denominator1Span = parameter1.Denominator.AsSpan(); - float a00 = FixedPointHelper.ToFloat(numerator0Span[0], FixedPointPrecisionForParameter); - float a10 = FixedPointHelper.ToFloat(numerator0Span[1], FixedPointPrecisionForParameter); - float a20 = FixedPointHelper.ToFloat(numerator0Span[2], FixedPointPrecisionForParameter); + float a00 = numerator0Span[0]; + float a10 = numerator0Span[1]; + float a20 = numerator0Span[2]; - float b10 = FixedPointHelper.ToFloat(denominator0Span[0], FixedPointPrecisionForParameter); - float b20 = FixedPointHelper.ToFloat(denominator0Span[1], FixedPointPrecisionForParameter); + float b10 = denominator0Span[0]; + float b20 = denominator0Span[1]; - float a01 = FixedPointHelper.ToFloat(numerator1Span[0], FixedPointPrecisionForParameter); - float a11 = FixedPointHelper.ToFloat(numerator1Span[1], FixedPointPrecisionForParameter); - float a21 = FixedPointHelper.ToFloat(numerator1Span[2], FixedPointPrecisionForParameter); + float a01 = numerator1Span[0]; + float a11 = numerator1Span[1]; + float a21 = numerator1Span[2]; - float b11 = FixedPointHelper.ToFloat(denominator1Span[0], FixedPointPrecisionForParameter); - float b21 = FixedPointHelper.ToFloat(denominator1Span[1], FixedPointPrecisionForParameter); + float b11 = denominator1Span[0]; + float b21 = denominator1Span[1]; for (int i = 0; i < sampleCount; i++) { @@ -269,8 +377,8 @@ namespace Ryujinx.Audio.Renderer.Dsp /// Last filtered sample value [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ProcessDoubleBiquadFilterAndMixRamp( - ref BiquadFilterParameter parameter0, - ref BiquadFilterParameter parameter1, + ref BiquadFilterParameter2 parameter0, + ref BiquadFilterParameter2 parameter1, ref BiquadFilterState state0, ref BiquadFilterState state1, Span outputBuffer, @@ -279,24 +387,24 @@ namespace Ryujinx.Audio.Renderer.Dsp float volume, float ramp) { - Span numerator0Span = parameter0.Numerator.AsSpan(); - Span numerator1Span = parameter1.Numerator.AsSpan(); - Span denominator0Span = parameter0.Denominator.AsSpan(); - Span denominator1Span = parameter1.Denominator.AsSpan(); + Span numerator0Span = parameter0.Numerator.AsSpan(); + Span numerator1Span = parameter1.Numerator.AsSpan(); + Span denominator0Span = parameter0.Denominator.AsSpan(); + Span denominator1Span = parameter1.Denominator.AsSpan(); - float a00 = FixedPointHelper.ToFloat(numerator0Span[0], FixedPointPrecisionForParameter); - float a10 = FixedPointHelper.ToFloat(numerator0Span[1], FixedPointPrecisionForParameter); - float a20 = FixedPointHelper.ToFloat(numerator0Span[2], FixedPointPrecisionForParameter); + float a00 = numerator0Span[0]; + float a10 = numerator0Span[1]; + float a20 = numerator0Span[2]; - float b10 = FixedPointHelper.ToFloat(denominator0Span[0], FixedPointPrecisionForParameter); - float b20 = FixedPointHelper.ToFloat(denominator0Span[1], FixedPointPrecisionForParameter); + float b10 = denominator0Span[0]; + float b20 = denominator0Span[1]; - float a01 = FixedPointHelper.ToFloat(numerator1Span[0], FixedPointPrecisionForParameter); - float a11 = FixedPointHelper.ToFloat(numerator1Span[1], FixedPointPrecisionForParameter); - float a21 = FixedPointHelper.ToFloat(numerator1Span[2], FixedPointPrecisionForParameter); + float a01 = numerator1Span[0]; + float a11 = numerator1Span[1]; + float a21 = numerator1Span[2]; - float b11 = FixedPointHelper.ToFloat(denominator1Span[0], FixedPointPrecisionForParameter); - float b21 = FixedPointHelper.ToFloat(denominator1Span[1], FixedPointPrecisionForParameter); + float b11 = denominator1Span[0]; + float b21 = denominator1Span[1]; float mixState = 0f; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs index 59786c059..60161ee7a 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs @@ -2,7 +2,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Server.Voice; using System; -using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using Ryujinx.Audio.Renderer.Parameter; using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; namespace Ryujinx.Audio.Renderer.Dsp.Command @@ -24,23 +24,23 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public WaveBuffer[] WaveBuffers { get; } - public Memory State { get; } + public Memory State { get; } public ulong AdpcmParameter { get; } public ulong AdpcmParameterSize { get; } public DecodingBehaviour DecodingBehaviour { get; } - public AdpcmDataSourceCommandVersion1(ref VoiceState serverState, Memory state, ushort outputBufferIndex, int nodeId) + public AdpcmDataSourceCommandVersion1(ref VoiceInfo serverInfo, Memory state, ushort outputBufferIndex, int nodeId) { Enabled = true; NodeId = nodeId; OutputBufferIndex = outputBufferIndex; - SampleRate = serverState.SampleRate; - Pitch = serverState.Pitch; + SampleRate = serverInfo.SampleRate; + Pitch = serverInfo.Pitch; - Span waveBufferSpan = serverState.WaveBuffers.AsSpan(); + Span waveBufferSpan = serverInfo.WaveBuffers.AsSpan(); WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; @@ -51,10 +51,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command WaveBuffers[i] = voiceWaveBuffer.ToCommon(1); } - AdpcmParameter = serverState.DataSourceStateAddressInfo.GetReference(true); - AdpcmParameterSize = serverState.DataSourceStateAddressInfo.Size; + AdpcmParameter = serverInfo.DataSourceStateAddressInfo.GetReference(true); + AdpcmParameterSize = serverInfo.DataSourceStateAddressInfo.Size; State = state; - DecodingBehaviour = serverState.DecodingBehaviour; + DecodingBehaviour = serverInfo.DecodingBehaviour; } public void Process(CommandList context) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs index 106fc0357..624c0d55b 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterAndMixCommand.cs @@ -18,12 +18,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public ushort InputBufferIndex { get; } public ushort OutputBufferIndex { get; } - private BiquadFilterParameter _parameter; + private BiquadFilterParameter2 _parameter; public Memory BiquadFilterState { get; } public Memory PreviousBiquadFilterState { get; } - public Memory State { get; } + public Memory State { get; } public int LastSampleIndex { get; } @@ -40,8 +40,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, - Memory state, - ref BiquadFilterParameter filter, + Memory state, + ref BiquadFilterParameter2 filter, Memory biquadFilterState, Memory previousBiquadFilterState, bool needInitialization, diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs index ac1e581f6..a8c996428 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs @@ -19,11 +19,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public int OutputBufferIndex { get; } public bool NeedInitialization { get; } - private BiquadFilterParameter _parameter; + private BiquadFilterParameter2 _parameter; public BiquadFilterCommand( int baseIndex, - ref BiquadFilterParameter filter, + ref BiquadFilterParameter2 filter, Memory biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs index de186b2f5..e7c9a2e81 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs @@ -129,7 +129,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { startTime = PerformanceCounter.ElapsedNanoseconds; } - + command.Process(this); if (shouldMeter) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs index de5c0ea2c..377eb6e57 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command Volume, VolumeRamp, BiquadFilter, + BiquadFilterFloatCoeff, // new Mix, MixRamp, MixRampGrouped, @@ -31,9 +32,17 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command LimiterVersion1, LimiterVersion2, MultiTapBiquadFilter, + MultiTapBiquadFilterFloatCoeff, // new CaptureBuffer, Compressor, BiquadFilterAndMix, + BiquadFilterAndMixFloatCoeff, // new MultiTapBiquadFilterAndMix, + MultiTapBiquadFilterAndMixFloatCoef, // new + AuxiliaryBufferGrouped, // new + FillMixBuffer, // new + BiquadFilterCrossFade, // new + MultiTapBiquadFilterCrossFade, // new + FillBuffer, // new } } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs index d3d3d2418..1fbd95c32 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs @@ -2,7 +2,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Server.Voice; using System; -using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using Ryujinx.Audio.Renderer.Parameter; using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; namespace Ryujinx.Audio.Renderer.Dsp.Command @@ -24,7 +24,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public WaveBuffer[] WaveBuffers { get; } - public Memory State { get; } + public Memory State { get; } public ulong ExtraParameter { get; } public ulong ExtraParameterSize { get; } @@ -39,21 +39,21 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public SampleRateConversionQuality SrcQuality { get; } - public DataSourceVersion2Command(ref VoiceState serverState, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) + public DataSourceVersion2Command(ref VoiceInfo serverInfo, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { Enabled = true; NodeId = nodeId; ChannelIndex = channelIndex; - ChannelCount = serverState.ChannelsCount; - SampleFormat = serverState.SampleFormat; - SrcQuality = serverState.SrcQuality; + ChannelCount = serverInfo.ChannelsCount; + SampleFormat = serverInfo.SampleFormat; + SrcQuality = serverInfo.SrcQuality; CommandType = GetCommandTypeBySampleFormat(SampleFormat); OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex); - SampleRate = serverState.SampleRate; - Pitch = serverState.Pitch; + SampleRate = serverInfo.SampleRate; + Pitch = serverInfo.Pitch; - Span waveBufferSpan = serverState.WaveBuffers.AsSpan(); + Span waveBufferSpan = serverInfo.WaveBuffers.AsSpan(); WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; @@ -66,12 +66,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (SampleFormat == SampleFormat.Adpcm) { - ExtraParameter = serverState.DataSourceStateAddressInfo.GetReference(true); - ExtraParameterSize = serverState.DataSourceStateAddressInfo.Size; + ExtraParameter = serverInfo.DataSourceStateAddressInfo.GetReference(true); + ExtraParameterSize = serverInfo.DataSourceStateAddressInfo.Size; } State = state; - DecodingBehaviour = serverState.DecodingBehaviour; + DecodingBehaviour = serverInfo.DecodingBehaviour; } private static CommandType GetCommandTypeBySampleFormat(SampleFormat sampleFormat) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs index a76f690e4..18ae11eb4 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs @@ -17,10 +17,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public ushort[] OutputBufferIndices { get; } - public Memory State { get; } + public Memory State { get; } public Memory DepopBuffer { get; } - public DepopPrepareCommand(Memory state, Memory depopBuffer, uint mixBufferCount, uint bufferOffset, int nodeId, bool enabled) + public DepopPrepareCommand(Memory state, Memory depopBuffer, uint mixBufferCount, uint bufferOffset, int nodeId, bool enabled) { Enabled = enabled; NodeId = nodeId; @@ -39,7 +39,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public void Process(CommandList context) { - ref VoiceUpdateState state = ref State.Span[0]; + ref VoiceState state = ref State.Span[0]; Span depopBuffer = DepopBuffer.Span; Span lastSamplesSpan = state.LastSamples.AsSpan(); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs index 322c5d386..b0e4b3c99 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs @@ -42,9 +42,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]); } - if (sink.UpsamplerState != null) + if (sink.UpsamplerInfo != null) { - Buffers = sink.UpsamplerState.OutputBuffer; + Buffers = sink.UpsamplerInfo.OutputBuffer; } else { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/FillBufferCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/FillBufferCommand.cs new file mode 100644 index 000000000..ca5428c56 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/FillBufferCommand.cs @@ -0,0 +1,69 @@ +using Ryujinx.Audio.Renderer.Server.Splitter; +using System; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Audio.Renderer.Dsp.Command +{ + public class FillBufferCommand : ICommand + { + public bool Enabled { get; set; } + + public int NodeId { get; } + + public CommandType CommandType => CommandType.FillBuffer; + + public uint EstimatedProcessingTime { get; set; } + + public SplitterDestinationVersion1 Destination1 { get; } + public SplitterDestinationVersion2 Destination2 { get; } + public bool IsV2 { get; } + public int Length { get; } + public float Value { get; } + + public FillBufferCommand(SplitterDestinationVersion1 destination, int length, float value, int nodeId) + { + Enabled = true; + NodeId = nodeId; + + Destination1 = destination; + IsV2 = false; + Length = length; + Value = value; + } + + public FillBufferCommand(SplitterDestinationVersion2 destination, int length, float value, int nodeId) + { + Enabled = true; + NodeId = nodeId; + + Destination2 = destination; + IsV2 = true; + Length = length; + Value = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ProcessFillBuffer() + { + if (IsV2) + { + for (int i = 0; i < Length; i++) + { + Destination2.PreviousMixBufferVolume[i] = Value; + } + } + else + { + for (int i = 0; i < Length; i++) + { + Destination1.PreviousMixBufferVolume[i] = Value; + } + } + } + + public void Process(CommandList context) + { + ProcessFillBuffer(); + } + } +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs index f77a233e1..6c5f7628c 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs @@ -20,11 +20,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public float Volume0 { get; } public float Volume1 { get; } - public Memory State { get; } + public Memory State { get; } public int LastSampleIndex { get; } - public MixRampCommand(float volume0, float volume1, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory state, int nodeId) + public MixRampCommand(float volume0, float volume1, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory state, int nodeId) { Enabled = true; NodeId = nodeId; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs index bc1f277ac..0d732c3fa 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public float[] Volume0 { get; } public float[] Volume1 { get; } - public Memory State { get; } + public Memory State { get; } public MixRampGroupedCommand( uint mixBufferCount, @@ -30,7 +30,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command uint outputBufferIndex, ReadOnlySpan volume0, ReadOnlySpan volume1, - Memory state, + Memory state, int nodeId) { Enabled = true; @@ -79,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public void Process(CommandList context) { - ref VoiceUpdateState state = ref State.Span[0]; + ref VoiceState state = ref State.Span[0]; Span lastSamplesSpan = state.LastSamples.AsSpan(); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs index e359371b4..ee28ce2fb 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterAndMixCommand.cs @@ -18,15 +18,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public ushort InputBufferIndex { get; } public ushort OutputBufferIndex { get; } - private BiquadFilterParameter _parameter0; - private BiquadFilterParameter _parameter1; + private BiquadFilterParameter2 _parameter0; + private BiquadFilterParameter2 _parameter1; public Memory BiquadFilterState0 { get; } public Memory BiquadFilterState1 { get; } public Memory PreviousBiquadFilterState0 { get; } public Memory PreviousBiquadFilterState1 { get; } - public Memory State { get; } + public Memory State { get; } public int LastSampleIndex { get; } @@ -44,9 +44,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, - Memory state, - ref BiquadFilterParameter filter0, - ref BiquadFilterParameter filter1, + Memory state, + ref BiquadFilterParameter2 filter0, + ref BiquadFilterParameter2 filter1, Memory biquadFilterState0, Memory biquadFilterState1, Memory previousBiquadFilterState0, diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs index e159f8ef7..84998056f 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MultiTapBiquadFilterCommand.cs @@ -14,13 +14,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public uint EstimatedProcessingTime { get; set; } - private readonly BiquadFilterParameter[] _parameters; + private readonly BiquadFilterParameter2[] _parameters; private readonly Memory _biquadFilterStates; private readonly int _inputBufferIndex; private readonly int _outputBufferIndex; private readonly bool[] _isInitialized; - public MultiTapBiquadFilterCommand(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) + public MultiTapBiquadFilterCommand(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) { _parameters = filters.ToArray(); _biquadFilterStates = biquadFilterStateMemory; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs index 2b5f0de72..d54158541 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs @@ -2,7 +2,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Server.Voice; using System; -using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using Ryujinx.Audio.Renderer.Parameter; using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; namespace Ryujinx.Audio.Renderer.Dsp.Command @@ -27,23 +27,23 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public WaveBuffer[] WaveBuffers { get; } - public Memory State { get; } + public Memory State { get; } public DecodingBehaviour DecodingBehaviour { get; } - public PcmFloatDataSourceCommandVersion1(ref VoiceState serverState, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) + public PcmFloatDataSourceCommandVersion1(ref VoiceInfo serverInfo, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { Enabled = true; NodeId = nodeId; OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex); - SampleRate = serverState.SampleRate; + SampleRate = serverInfo.SampleRate; ChannelIndex = channelIndex; - ChannelCount = serverState.ChannelsCount; - Pitch = serverState.Pitch; + ChannelCount = serverInfo.ChannelsCount; + Pitch = serverInfo.Pitch; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; - Span waveBufferSpan = serverState.WaveBuffers.AsSpan(); + Span waveBufferSpan = serverInfo.WaveBuffers.AsSpan(); for (int i = 0; i < WaveBuffers.Length; i++) { @@ -53,7 +53,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } State = state; - DecodingBehaviour = serverState.DecodingBehaviour; + DecodingBehaviour = serverInfo.DecodingBehaviour; } public void Process(CommandList context) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs index 9c30de41d..91619d80f 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs @@ -2,7 +2,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Server.Voice; using System; -using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using Ryujinx.Audio.Renderer.Parameter; using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; namespace Ryujinx.Audio.Renderer.Dsp.Command @@ -27,23 +27,23 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public WaveBuffer[] WaveBuffers { get; } - public Memory State { get; } + public Memory State { get; } public DecodingBehaviour DecodingBehaviour { get; } - public PcmInt16DataSourceCommandVersion1(ref VoiceState serverState, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) + public PcmInt16DataSourceCommandVersion1(ref VoiceInfo serverInfo, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { Enabled = true; NodeId = nodeId; OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex); - SampleRate = serverState.SampleRate; + SampleRate = serverInfo.SampleRate; ChannelIndex = channelIndex; - ChannelCount = serverState.ChannelsCount; - Pitch = serverState.Pitch; + ChannelCount = serverInfo.ChannelsCount; + Pitch = serverInfo.Pitch; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; - Span waveBufferSpan = serverState.WaveBuffers.AsSpan(); + Span waveBufferSpan = serverInfo.WaveBuffers.AsSpan(); for (int i = 0; i < WaveBuffers.Length; i++) { @@ -53,7 +53,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } State = state; - DecodingBehaviour = serverState.DecodingBehaviour; + DecodingBehaviour = serverInfo.DecodingBehaviour; } public void Process(CommandList context) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs index 8882500cd..5d23addae 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs @@ -18,11 +18,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public uint InputSampleCount { get; } public uint InputSampleRate { get; } - public UpsamplerState UpsamplerInfo { get; } + public UpsamplerInfo UpsamplerInfo { get; } public Memory OutBuffer { get; } - public UpsampleCommand(uint bufferOffset, UpsamplerState info, uint inputCount, Span inputBufferOffset, uint bufferCount, uint sampleCount, uint sampleRate, int nodeId) + public UpsampleCommand(uint bufferOffset, UpsamplerInfo info, uint inputCount, Span inputBufferOffset, uint bufferCount, uint sampleCount, uint sampleRate, int nodeId) { Enabled = true; NodeId = nodeId; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs index 130836c6b..9e4e04890 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; -using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using Ryujinx.Audio.Renderer.Parameter; namespace Ryujinx.Audio.Renderer.Dsp { @@ -42,7 +42,7 @@ namespace Ryujinx.Audio.Renderer.Dsp }; } - public static void ProcessWaveBuffers(IVirtualMemoryManager memoryManager, Span outputBuffer, ref WaveBufferInformation info, Span wavebuffers, ref VoiceUpdateState voiceState, uint targetSampleRate, int sampleCount) + public static void ProcessWaveBuffers(IVirtualMemoryManager memoryManager, Span outputBuffer, ref WaveBufferInformation info, Span wavebuffers, ref VoiceState voiceState, uint targetSampleRate, int sampleCount) { const int TempBufferSize = 0x3F00; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs index 16048d7ff..0de34101e 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; -using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using Ryujinx.Audio.Renderer.Parameter; namespace Ryujinx.Audio.Renderer.Dsp { diff --git a/src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs b/src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs index 491a05c86..c70e16544 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs @@ -1,3 +1,4 @@ +using Ryujinx.Audio.Renderer.Server; using Ryujinx.Audio.Renderer.Server.Types; using System.Runtime.InteropServices; @@ -93,7 +94,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// /// The user audio revision /// - /// + /// public int Revision; } } diff --git a/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter1.cs similarity index 95% rename from src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter1.cs index f1492b0b1..5a3091f62 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter1.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// Biquad filter parameters. /// [StructLayout(LayoutKind.Sequential, Size = 0xC, Pack = 1)] - public struct BiquadFilterParameter + public struct BiquadFilterParameter1 { /// /// Set to true if the biquad filter is active. diff --git a/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter2.cs b/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter2.cs new file mode 100644 index 000000000..8e47b22e0 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter2.cs @@ -0,0 +1,36 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Parameter +{ + /// + /// Biquad filter parameters. + /// + [StructLayout(LayoutKind.Sequential, Size = 0x18, Pack = 1)] + public struct BiquadFilterParameter2 + { + /// + /// Set to true if the biquad filter is active. + /// + [MarshalAs(UnmanagedType.I1)] + public bool Enable; + + /// + /// Reserved/padding. + /// + private readonly byte _reserved1; + private readonly byte _reserved2; + private readonly byte _reserved3; + + /// + /// Biquad filter numerator (b0, b1, b2). + /// + public Array3 Numerator; + + /// + /// Biquad filter denominator (a1, a2). + /// + /// a0 = 1 + public Array2 Denominator; + } +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter1.cs similarity index 96% rename from src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter1.cs index b12a941a5..443b257be 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter1.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// for . /// [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct BiquadFilterEffectParameter + public struct BiquadFilterEffectParameter1 { /// /// The input channel indices that will be used by the . diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter2.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter2.cs new file mode 100644 index 000000000..0c74f1e7b --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter2.cs @@ -0,0 +1,49 @@ +using Ryujinx.Audio.Renderer.Server.Effect; +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Parameter.Effect +{ + /// + /// for . + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct BiquadFilterEffectParameter2 + { + /// + /// The input channel indices that will be used by the . + /// + public Array6 Input; + + /// + /// The output channel indices that will be used by the . + /// + public Array6 Output; + + /// + /// Biquad filter numerator (b0, b1, b2). + /// + public Array3 Numerator; + + /// + /// Biquad filter denominator (a1, a2). + /// + /// a0 = 1 + public Array2 Denominator; + + /// + /// The total channel count used. + /// + public byte ChannelCount; + + /// + /// The current usage status of the effect on the client side. + /// + public UsageState Status; + + /// + /// Reserved/unused. + /// + private readonly ushort _reserved; + } +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion3.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion3.cs new file mode 100644 index 000000000..2cf4911a6 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion3.cs @@ -0,0 +1,97 @@ +using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Common.Utilities; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Parameter +{ + /// + /// Input information for an effect version 2. (added with REV9) + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct EffectInParameterVersion3 : IEffectInParameter + { + /// + /// Type of the effect. + /// + public EffectType Type; + + /// + /// Set to true if the effect is new. + /// + [MarshalAs(UnmanagedType.I1)] + public bool IsNew; + + /// + /// Set to true if the effect must be active. + /// + [MarshalAs(UnmanagedType.I1)] + public bool IsEnabled; + + /// + /// Reserved/padding. + /// + private readonly byte _reserved1; + + /// + /// The target mix id of the effect. + /// + public int MixId; + + /// + /// Address of the processing workbuffer. + /// + /// This is additional data that could be required by the effect processing. + public ulong BufferBase; + + /// + /// Size of the processing workbuffer. + /// + /// This is additional data that could be required by the effect processing. + public ulong BufferSize; + + /// + /// Position of the effect while processing effects. + /// + public uint ProcessingOrder; + + /// + /// Reserved/padding. + /// + private readonly uint _reserved2; + + /// + /// Specific data storage. + /// + private SpecificDataStruct _specificDataStart; + + [StructLayout(LayoutKind.Sequential, Size = 0xA0, Pack = 1)] + private struct SpecificDataStruct { } + + public Span SpecificData => SpanHelpers.AsSpan(ref _specificDataStart); + + readonly EffectType IEffectInParameter.Type => Type; + + readonly bool IEffectInParameter.IsNew => IsNew; + + readonly bool IEffectInParameter.IsEnabled => IsEnabled; + + readonly int IEffectInParameter.MixId => MixId; + + readonly ulong IEffectInParameter.BufferBase => BufferBase; + + readonly ulong IEffectInParameter.BufferSize => BufferSize; + + readonly uint IEffectInParameter.ProcessingOrder => ProcessingOrder; + + /// + /// Check if the given channel count is valid. + /// + /// The channel count to check + /// Returns true if the channel count is valid. + public static bool IsChannelCountValid(int channelCount) + { + return channelCount is 1 or 2 or 4 or 6; + } + } +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs index 7ee49f11a..c3224c57d 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs @@ -17,11 +17,11 @@ namespace Ryujinx.Audio.Renderer.Parameter /// The mix to output the result of the splitter. /// int DestinationId { get; } - + /// /// Biquad filter parameters. /// - Array2 BiquadFilters { get; } + Array2 BiquadFilters2 { get; } /// /// Set to true if in use. diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs index 3c1ac09c0..86ceea30a 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// /// Reserved/padding. /// - private readonly ushort _reserved1; + private readonly ushort _magic; // 0xCAFE /// /// The node id of the sink. diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs index f346efcb0..fbc036806 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs @@ -60,8 +60,8 @@ namespace Ryujinx.Audio.Renderer.Parameter readonly int ISplitterDestinationInParameter.Id => Id; readonly int ISplitterDestinationInParameter.DestinationId => DestinationId; - - readonly Array2 ISplitterDestinationInParameter.BiquadFilters => default; + + readonly Array2 ISplitterDestinationInParameter.BiquadFilters2 => default; readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume; diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2a.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2a.cs new file mode 100644 index 000000000..4270b5e20 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2a.cs @@ -0,0 +1,100 @@ +using Ryujinx.Audio.Renderer.Dsp; +using Ryujinx.Common.Memory; +using Ryujinx.Common.Utilities; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Parameter +{ + /// + /// Input header for a splitter destination version 2 update. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SplitterDestinationInParameterVersion2a : ISplitterDestinationInParameter + { + /// + /// Magic of the input header. + /// + public uint Magic; + + /// + /// Target splitter destination data id. + /// + public int Id; + + /// + /// Mix buffer volumes storage. + /// + private MixArray _mixBufferVolume; + + /// + /// The mix to output the result of the splitter. + /// + public int DestinationId; + + /// + /// Biquad filter parameters. + /// + public Array2 BiquadFilters; + + /// + /// Set to true if in use. + /// + [MarshalAs(UnmanagedType.I1)] + public bool IsUsed; + + /// + /// Set to true to force resetting the previous mix volumes. + /// + [MarshalAs(UnmanagedType.I1)] + public bool ResetPrevVolume; + + /// + /// Reserved/padding. + /// + private unsafe fixed byte _reserved[10]; + + [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)] + private struct MixArray { } + + /// + /// Mix buffer volumes. + /// + /// Used when a splitter id is specified in the mix. + public Span MixBufferVolume => SpanHelpers.AsSpan(ref _mixBufferVolume); + + readonly int ISplitterDestinationInParameter.Id => Id; + + readonly int ISplitterDestinationInParameter.DestinationId => DestinationId; + + readonly Array2 ISplitterDestinationInParameter.BiquadFilters2 + { + get + { + Array2 newFilters = new(); + Span newFiltersSpan = newFilters.AsSpan(); + newFiltersSpan[0] = BiquadFilterHelper.ToBiquadFilterParameter2(BiquadFilters[0]); + newFiltersSpan[1] = BiquadFilterHelper.ToBiquadFilterParameter2(BiquadFilters[1]); + + return newFilters; + } + } + + readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; + readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume; + + /// + /// The expected constant of any input header. + /// + private const uint ValidMagic = 0x44444E53; + + /// + /// Check if the magic is valid. + /// + /// Returns true if the magic is valid. + public readonly bool IsMagicValid() + { + return Magic == ValidMagic; + } + } +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2b.cs similarity index 90% rename from src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs rename to src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2b.cs index 1d867919d..81e9d823b 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2b.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// Input header for a splitter destination version 2 update. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct SplitterDestinationInParameterVersion2 : ISplitterDestinationInParameter + public struct SplitterDestinationInParameterVersion2b : ISplitterDestinationInParameter { /// /// Magic of the input header. @@ -34,7 +34,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// /// Biquad filter parameters. /// - public Array2 BiquadFilters; + public Array2 BiquadFilters; /// /// Set to true if in use. @@ -66,7 +66,7 @@ namespace Ryujinx.Audio.Renderer.Parameter readonly int ISplitterDestinationInParameter.DestinationId => DestinationId; - readonly Array2 ISplitterDestinationInParameter.BiquadFilters => BiquadFilters; + readonly Array2 ISplitterDestinationInParameter.BiquadFilters2 => BiquadFilters; readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume; diff --git a/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter1.cs similarity index 98% rename from src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter1.cs index f33d82aa0..a3633edbd 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter1.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// Input information for a voice. /// [StructLayout(LayoutKind.Sequential, Size = 0x170, Pack = 1)] - public struct VoiceInParameter + public struct VoiceInParameter1 { /// /// Id of the voice. @@ -79,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// /// Biquad filters to apply to the output of the voice. /// - public Array2 BiquadFilters; + public Array2 BiquadFilters; /// /// Total count of of the voice. @@ -171,8 +171,9 @@ namespace Ryujinx.Audio.Renderer.Parameter /// Reserved/unused. /// private unsafe fixed uint _reserved3[2]; - - /// + } + + /// /// Input information for a voice wavebuffer. /// [StructLayout(LayoutKind.Sequential, Size = 0x38, Pack = 1)] @@ -328,5 +329,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// Low, } - } } diff --git a/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter2.cs b/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter2.cs new file mode 100644 index 000000000..78e47b69a --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter2.cs @@ -0,0 +1,176 @@ +using Ryujinx.Audio.Common; +using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Dsp; +using Ryujinx.Common.Memory; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Parameter +{ + /// + /// Input information for a voice. + /// + [StructLayout(LayoutKind.Sequential, Size = 0x188, Pack = 1)] + public struct VoiceInParameter2 + { + /// + /// Id of the voice. + /// + public int Id; + + /// + /// Node id of the voice. + /// + public int NodeId; + + /// + /// Set to true if the voice is new. + /// + [MarshalAs(UnmanagedType.I1)] + public bool IsNew; + + /// + /// Set to true if the voice is used. + /// + [MarshalAs(UnmanagedType.I1)] + public bool InUse; + + /// + /// The voice wanted by the user. + /// + public PlayState PlayState; + + /// + /// The of the voice. + /// + public SampleFormat SampleFormat; + + /// + /// The sample rate of the voice. + /// + public uint SampleRate; + + /// + /// The priority of the voice. + /// + public uint Priority; + + /// + /// Target sorting position of the voice. (Used to sort voices with the same ) + /// + public uint SortingOrder; + + /// + /// The total channel count used. + /// + public uint ChannelCount; + + /// + /// The pitch used on the voice. + /// + public float Pitch; + + /// + /// The output volume of the voice. + /// + public float Volume; + + /// + /// Biquad filters to apply to the output of the voice. + /// + public Array2 BiquadFilters; + + /// + /// Total count of of the voice. + /// + public uint WaveBuffersCount; + + /// + /// Current playing of the voice. + /// + public uint WaveBuffersIndex; + + /// + /// Reserved/unused. + /// + private readonly uint + _reserved1; + + /// + /// User state address required by the data source. + /// + /// Only used for as the address of the GC-ADPCM coefficients. + public ulong DataSourceStateAddress; + + /// + /// User state size required by the data source. + /// + /// Only used for as the size of the GC-ADPCM coefficients. + public ulong DataSourceStateSize; + + /// + /// The target mix id of the voice. + /// + public int MixId; + + /// + /// The target splitter id of the voice. + /// + public uint SplitterId; + + /// + /// The wavebuffer parameters of this voice. + /// + public Array4 WaveBuffers; + + /// + /// The channel resource ids associated to the voice. + /// + public Array6 ChannelResourceIds; + + /// + /// Reset the voice drop flag during voice server update. + /// + [MarshalAs(UnmanagedType.I1)] + public bool ResetVoiceDropFlag; + + /// + /// Flush the amount of wavebuffer specified. This will result in the wavebuffer being skipped and marked played. + /// + /// This was added on REV5. + public byte FlushWaveBufferCount; + + /// + /// Reserved/unused. + /// + private readonly ushort _reserved2; + + /// + /// Change the behaviour of the voice. + /// + /// This was added on REV5. + public DecodingBehaviour DecodingBehaviourFlags; + + /// + /// Change the Sample Rate Conversion (SRC) quality of the voice. + /// + /// This was added on REV8. + public SampleRateConversionQuality SrcQuality; + + /// + /// This was previously used for opus codec support on the Audio Renderer and was removed on REV3. + /// + public uint ExternalContext; + + /// + /// This was previously used for opus codec support on the Audio Renderer and was removed on REV3. + /// + public uint ExternalContextSize; + + /// + /// Reserved/unused. + /// + private unsafe fixed uint _reserved3[2]; + } +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs index a7c749835..0ec864cdc 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs @@ -1,3 +1,5 @@ +using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Server; using System.Runtime.InteropServices; namespace Ryujinx.Audio.Renderer.Parameter @@ -5,7 +7,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// /// Output information about a voice. /// - /// See + /// See [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct VoiceOutStatus { @@ -13,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// The total amount of samples that was played. /// /// This is reset to 0 when a finishes playing and is set. - /// This is reset to 0 when looping while is set. + /// This is reset to 0 when looping while is set. public ulong PlayedSampleCount; /// diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 81a5729f4..aab70e79b 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Renderer.Server private AudioRendererRenderingDevice _renderingDevice; private AudioRendererExecutionMode _executionMode; private readonly IWritableEvent _systemEvent; - private MemoryPoolState _dspMemoryPoolState; + private MemoryPoolInfo _dspMemoryPoolInfo; private readonly VoiceContext _voiceContext; private readonly MixContext _mixContext; private readonly SinkContext _sinkContext; @@ -40,13 +40,13 @@ namespace Ryujinx.Audio.Renderer.Server private PerformanceManager _performanceManager; private UpsamplerManager _upsamplerManager; private bool _isActive; - private BehaviourContext _behaviourContext; + private BehaviourInfo _behaviourInfo; #pragma warning disable IDE0052 // Remove unread private member private ulong _totalElapsedTicksUpdating; private ulong _totalElapsedTicks; #pragma warning restore IDE0052 private int _sessionId; - private Memory _memoryPools; + private Memory _memoryPools; private uint _sampleRate; private uint _sampleCount; @@ -84,7 +84,7 @@ namespace Ryujinx.Audio.Renderer.Server public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent) { _manager = manager; - _dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); + _dspMemoryPoolInfo = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Dsp); _voiceContext = new VoiceContext(); _mixContext = new MixContext(); _sinkContext = new SinkContext(); @@ -93,7 +93,7 @@ namespace Ryujinx.Audio.Renderer.Server _commandProcessingTimeEstimator = null; _systemEvent = systemEvent; - _behaviourContext = new BehaviourContext(); + _behaviourInfo = new BehaviourInfo(); _totalElapsedTicksUpdating = 0; _sessionId = 0; @@ -110,7 +110,7 @@ namespace Ryujinx.Audio.Renderer.Server ulong appletResourceId, IVirtualMemoryManager memoryManager) { - if (!BehaviourContext.CheckValidRevision(parameter.Revision)) + if (!BehaviourInfo.CheckValidRevision(parameter.Revision)) { return ResultCode.OperationFailed; } @@ -122,9 +122,9 @@ namespace Ryujinx.Audio.Renderer.Server Debug.Assert(parameter.RenderingDevice == AudioRendererRenderingDevice.Dsp && parameter.ExecutionMode == AudioRendererExecutionMode.Auto); - Logger.Info?.Print(LogClass.AudioRenderer, $"Initializing with REV{BehaviourContext.GetRevisionNumber(parameter.Revision)}"); + Logger.Info?.Print(LogClass.AudioRenderer, $"Initializing with REV{BehaviourInfo.GetRevisionNumber(parameter.Revision)}"); - _behaviourContext.SetUserRevision(parameter.Revision); + _behaviourInfo.SetUserRevision(parameter.Revision); _sampleRate = parameter.SampleRate; _sampleCount = parameter.SampleCount; @@ -151,7 +151,7 @@ namespace Ryujinx.Audio.Renderer.Server workBufferAllocator = new WorkBufferAllocator(workBufferMemory); PoolMapper poolMapper = new(processHandle, false); - poolMapper.InitializeSystemPool(ref _dspMemoryPoolState, workBuffer, workBufferSize); + poolMapper.InitializeSystemPool(ref _dspMemoryPoolInfo, workBuffer, workBufferSize); _mixBuffer = workBufferAllocator.Allocate(_sampleCount * (_voiceChannelCountMax + _mixBufferCount), 0x10); @@ -176,7 +176,7 @@ namespace Ryujinx.Audio.Renderer.Server Memory splitterBqfStates = Memory.Empty; - if (_behaviourContext.IsBiquadFilterParameterForSplitterEnabled() && + if (_behaviourInfo.IsBiquadFilterParameterForSplitterEnabled() && parameter.SplitterCount > 0 && parameter.SplitterDestinationCount > 0) { @@ -191,23 +191,23 @@ namespace Ryujinx.Audio.Renderer.Server } // Invalidate DSP cache on what was currently allocated with workBuffer. - AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset); + AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolInfo.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset); Debug.Assert((workBufferAllocator.Offset % Constants.BufferAlignment) == 0); - Memory voices = workBufferAllocator.Allocate(parameter.VoiceCount, VoiceState.Alignment); + Memory voices = workBufferAllocator.Allocate(parameter.VoiceCount, VoiceInfo.Alignment); if (voices.IsEmpty) { return ResultCode.WorkBufferTooSmall; } - foreach (ref VoiceState voice in voices.Span) + foreach (ref VoiceInfo voice in voices.Span) { voice.Initialize(); } - // A pain to handle as we can't have VoiceState*, use indices to be a bit more safe + // A pain to handle as we can't have VoiceInfo*, use indices to be a bit more safe Memory sortedVoices = workBufferAllocator.Allocate(parameter.VoiceCount, 0x10); if (sortedVoices.IsEmpty) @@ -233,16 +233,16 @@ namespace Ryujinx.Audio.Renderer.Server voiceChannelResource.IsUsed = false; } - Memory voiceUpdateStates = workBufferAllocator.Allocate(parameter.VoiceCount, VoiceUpdateState.Align); + Memory voiceStates = workBufferAllocator.Allocate(parameter.VoiceCount, VoiceState.Align); - if (voiceUpdateStates.IsEmpty) + if (voiceStates.IsEmpty) { return ResultCode.WorkBufferTooSmall; } uint mixesCount = parameter.SubMixBufferCount + 1; - Memory mixes = workBufferAllocator.Allocate(mixesCount, MixState.Alignment); + Memory mixes = workBufferAllocator.Allocate(mixesCount, MixInfo.Alignment); if (mixes.IsEmpty) { @@ -251,18 +251,18 @@ namespace Ryujinx.Audio.Renderer.Server if (parameter.EffectCount == 0) { - foreach (ref MixState mix in mixes.Span) + foreach (ref MixInfo mix in mixes.Span) { - mix = new MixState(Memory.Empty, ref _behaviourContext); + mix = new MixInfo(Memory.Empty, ref _behaviourInfo); } } else { Memory effectProcessingOrderArray = workBufferAllocator.Allocate(parameter.EffectCount * mixesCount, 0x10); - foreach (ref MixState mix in mixes.Span) + foreach (ref MixInfo mix in mixes.Span) { - mix = new MixState(effectProcessingOrderArray[..(int)parameter.EffectCount], ref _behaviourContext); + mix = new MixInfo(effectProcessingOrderArray[..(int)parameter.EffectCount], ref _behaviourInfo); effectProcessingOrderArray = effectProcessingOrderArray[(int)parameter.EffectCount..]; } @@ -271,20 +271,20 @@ namespace Ryujinx.Audio.Renderer.Server // Initialize the final mix id mixes.Span[0].MixId = Constants.FinalMixId; - Memory sortedMixesState = workBufferAllocator.Allocate(mixesCount, 0x10); + Memory sortedMixesInfo = workBufferAllocator.Allocate(mixesCount, 0x10); - if (sortedMixesState.IsEmpty) + if (sortedMixesInfo.IsEmpty) { return ResultCode.WorkBufferTooSmall; } // Clear memory (use -1 as it's an invalid index) - sortedMixesState.Span.Fill(-1); + sortedMixesInfo.Span.Fill(-1); Memory nodeStatesWorkBuffer = Memory.Empty; Memory edgeMatrixWorkBuffer = Memory.Empty; - if (_behaviourContext.IsSplitterSupported()) + if (_behaviourInfo.IsSplitterSupported()) { nodeStatesWorkBuffer = workBufferAllocator.Allocate((uint)NodeStates.GetWorkBufferSize((int)mixesCount), 1); edgeMatrixWorkBuffer = workBufferAllocator.Allocate((uint)EdgeMatrix.GetWorkBufferSize((int)mixesCount), 1); @@ -295,21 +295,21 @@ namespace Ryujinx.Audio.Renderer.Server } } - _mixContext.Initialize(sortedMixesState, mixes, nodeStatesWorkBuffer, edgeMatrixWorkBuffer); + _mixContext.Initialize(sortedMixesInfo, mixes, nodeStatesWorkBuffer, edgeMatrixWorkBuffer); - _memoryPools = workBufferAllocator.Allocate(_memoryPoolCount, MemoryPoolState.Alignment); + _memoryPools = workBufferAllocator.Allocate(_memoryPoolCount, MemoryPoolInfo.Alignment); if (_memoryPools.IsEmpty) { return ResultCode.WorkBufferTooSmall; } - foreach (ref MemoryPoolState state in _memoryPools.Span) + foreach (ref MemoryPoolInfo info in _memoryPools.Span) { - state = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); + info = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu); } - if (!_splitterContext.Initialize(ref _behaviourContext, ref parameter, workBufferAllocator, splitterBqfStates)) + if (!_splitterContext.Initialize(ref _behaviourInfo, ref parameter, workBufferAllocator, splitterBqfStates)) { return ResultCode.WorkBufferTooSmall; } @@ -318,21 +318,21 @@ namespace Ryujinx.Audio.Renderer.Server _upsamplerManager = new UpsamplerManager(upSamplerWorkBuffer, _upsamplerCount); - _effectContext.Initialize(parameter.EffectCount, _behaviourContext.IsEffectInfoVersion2Supported() ? parameter.EffectCount : 0); + _effectContext.Initialize(parameter.EffectCount, _behaviourInfo.IsEffectInfoVersion2Supported() ? parameter.EffectCount : 0); _sinkContext.Initialize(parameter.SinkCount); - Memory voiceUpdateStatesDsp = workBufferAllocator.Allocate(parameter.VoiceCount, VoiceUpdateState.Align); + Memory voiceStatesDsp = workBufferAllocator.Allocate(parameter.VoiceCount, VoiceState.Align); - if (voiceUpdateStatesDsp.IsEmpty) + if (voiceStatesDsp.IsEmpty) { return ResultCode.WorkBufferTooSmall; } - _voiceContext.Initialize(sortedVoices, voices, voiceChannelResources, voiceUpdateStates, voiceUpdateStatesDsp, parameter.VoiceCount); + _voiceContext.Initialize(sortedVoices, voices, voiceChannelResources, voiceStates, voiceStatesDsp, parameter.VoiceCount); if (parameter.PerformanceMetricFramesCount > 0) { - ulong performanceBufferSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref _behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC; + ulong performanceBufferSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref _behaviourInfo) * (parameter.PerformanceMetricFramesCount + 1) + 0xC; _performanceBuffer = workBufferAllocator.Allocate(performanceBufferSize, Constants.BufferAlignment); @@ -341,7 +341,7 @@ namespace Ryujinx.Audio.Renderer.Server return ResultCode.WorkBufferTooSmall; } - _performanceManager = PerformanceManager.Create(_performanceBuffer, ref parameter, _behaviourContext); + _performanceManager = PerformanceManager.Create(_performanceBuffer, ref parameter, _behaviourInfo); } else { @@ -359,14 +359,14 @@ namespace Ryujinx.Audio.Renderer.Server _elapsedFrameCount = 0; _voiceDropParameter = 1.0f; - _commandProcessingTimeEstimator = _behaviourContext.GetCommandProcessingTimeEstimatorVersion() switch + _commandProcessingTimeEstimator = _behaviourInfo.GetCommandProcessingTimeEstimatorVersion() switch { 1 => new CommandProcessingTimeEstimatorVersion1(_sampleCount, _mixBufferCount), 2 => new CommandProcessingTimeEstimatorVersion2(_sampleCount, _mixBufferCount), 3 => new CommandProcessingTimeEstimatorVersion3(_sampleCount, _mixBufferCount), 4 => new CommandProcessingTimeEstimatorVersion4(_sampleCount, _mixBufferCount), 5 => new CommandProcessingTimeEstimatorVersion5(_sampleCount, _mixBufferCount), - _ => throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourContext.GetCommandProcessingTimeEstimatorVersion()}."), + _ => throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourInfo.GetCommandProcessingTimeEstimatorVersion()}."), }; return ResultCode.Success; @@ -411,11 +411,11 @@ namespace Ryujinx.Audio.Renderer.Server output.Span.Clear(); - StateUpdater stateUpdater = new(input, output, _processHandle, _behaviourContext); + StateUpdater stateUpdater = new(input, output, _processHandle, _behaviourInfo); ResultCode result; - result = stateUpdater.UpdateBehaviourContext(); + result = stateUpdater.UpdateBehaviourInfo(); if (result != ResultCode.Success) { @@ -436,9 +436,16 @@ namespace Ryujinx.Audio.Renderer.Server return result; } - PoolMapper poolMapper = new(_processHandle, _memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); + PoolMapper poolMapper = new(_processHandle, _memoryPools, _behaviourInfo.IsMemoryPoolForceMappingEnabled()); - result = stateUpdater.UpdateVoices(_voiceContext, poolMapper); + if (_behaviourInfo.IsBiquadFilterParameterFloatSupported()) + { + result = stateUpdater.UpdateVoices2(_voiceContext, poolMapper); + } + else + { + result = stateUpdater.UpdateVoices1(_voiceContext, poolMapper); + } if (result != ResultCode.Success) { @@ -452,7 +459,7 @@ namespace Ryujinx.Audio.Renderer.Server return result; } - if (_behaviourContext.IsSplitterSupported()) + if (_behaviourInfo.IsSplitterSupported()) { result = stateUpdater.UpdateSplitter(_splitterContext); @@ -490,7 +497,7 @@ namespace Ryujinx.Audio.Renderer.Server return result; } - if (_behaviourContext.IsElapsedFrameCountSupported()) + if (_behaviourInfo.IsElapsedFrameCountSupported()) { result = stateUpdater.UpdateRendererInfo(_elapsedFrameCount); @@ -557,7 +564,7 @@ namespace Ryujinx.Audio.Renderer.Server break; } - ref VoiceState voice = ref _voiceContext.GetState(NodeIdHelper.GetBase(targetNodeId)); + ref VoiceInfo voice = ref _voiceContext.GetState(NodeIdHelper.GetBase(targetNodeId)); if (voice.Priority == Constants.VoiceHighestPriority) { @@ -646,7 +653,7 @@ namespace Ryujinx.Audio.Renderer.Server _voiceContext.UpdateForCommandGeneration(); - if (_behaviourContext.IsEffectInfoVersion2Supported()) + if (_behaviourInfo.IsEffectInfoVersion2Supported()) { _effectContext.UpdateResultStateForCommandGeneration(); } @@ -661,7 +668,7 @@ namespace Ryujinx.Audio.Renderer.Server private int GetMaxAllocatedTimeForDsp() { - return (int)(Constants.AudioProcessorMaxUpdateTimePerSessions * _behaviourContext.GetAudioRendererProcessingTimeLimit() * (GetRenderingTimeLimit() / 100.0f)); + return (int)(Constants.AudioProcessorMaxUpdateTimePerSessions * _behaviourInfo.GetAudioRendererProcessingTimeLimit() * (GetRenderingTimeLimit() / 100.0f)); } public void SendCommands() @@ -736,7 +743,7 @@ namespace Ryujinx.Audio.Renderer.Server return new RendererSystemContext { ChannelCount = _manager.Processor.OutputDevices[_sessionId].GetChannelCount(), - BehaviourContext = _behaviourContext, + BehaviourInfo = _behaviourInfo, DepopBuffer = _depopBuffer, MixBufferCount = GetMixBufferCount(), SessionId = _sessionId, @@ -751,9 +758,9 @@ namespace Ryujinx.Audio.Renderer.Server public static ulong GetWorkBufferSize(ref AudioRendererConfiguration parameter) { - BehaviourContext behaviourContext = new(); + BehaviourInfo behaviourInfo = new(); - behaviourContext.SetUserRevision(parameter.Revision); + behaviourInfo.SetUserRevision(parameter.Revision); uint mixesCount = parameter.SubMixBufferCount + 1; @@ -771,28 +778,28 @@ namespace Ryujinx.Audio.Renderer.Server size = WorkBufferAllocator.GetTargetSize(size, BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment); // Voice - size = WorkBufferAllocator.GetTargetSize(size, parameter.VoiceCount, VoiceState.Alignment); + size = WorkBufferAllocator.GetTargetSize(size, parameter.VoiceCount, VoiceInfo.Alignment); size = WorkBufferAllocator.GetTargetSize(size, parameter.VoiceCount, 0x10); size = WorkBufferAllocator.GetTargetSize(size, parameter.VoiceCount, VoiceChannelResource.Alignment); - size = WorkBufferAllocator.GetTargetSize(size, parameter.VoiceCount, VoiceUpdateState.Align); + size = WorkBufferAllocator.GetTargetSize(size, parameter.VoiceCount, VoiceState.Align); // Mix - size = WorkBufferAllocator.GetTargetSize(size, mixesCount, MixState.Alignment); + size = WorkBufferAllocator.GetTargetSize(size, mixesCount, MixInfo.Alignment); size = WorkBufferAllocator.GetTargetSize(size, parameter.EffectCount * mixesCount, 0x10); size = WorkBufferAllocator.GetTargetSize(size, mixesCount, 0x10); - if (behaviourContext.IsSplitterSupported()) + if (behaviourInfo.IsSplitterSupported()) { size += (ulong)BitUtils.AlignUp(NodeStates.GetWorkBufferSize((int)mixesCount) + EdgeMatrix.GetWorkBufferSize((int)mixesCount), 0x10); } // Memory Pool - size = WorkBufferAllocator.GetTargetSize(size, memoryPoolCount, MemoryPoolState.Alignment); + size = WorkBufferAllocator.GetTargetSize(size, memoryPoolCount, MemoryPoolInfo.Alignment); // Splitter - size = SplitterContext.GetWorkBufferSize(size, ref behaviourContext, ref parameter); + size = SplitterContext.GetWorkBufferSize(size, ref behaviourInfo, ref parameter); - if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled() && + if (behaviourInfo.IsBiquadFilterParameterForSplitterEnabled() && parameter.SplitterCount > 0 && parameter.SplitterDestinationCount > 0) { @@ -800,12 +807,12 @@ namespace Ryujinx.Audio.Renderer.Server } // DSP Voice - size = WorkBufferAllocator.GetTargetSize(size, parameter.VoiceCount, VoiceUpdateState.Align); + size = WorkBufferAllocator.GetTargetSize(size, parameter.VoiceCount, VoiceState.Align); // Performance if (parameter.PerformanceMetricFramesCount > 0) { - ulong performanceMetricsPerFramesSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC; + ulong performanceMetricsPerFramesSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref behaviourInfo) * (parameter.PerformanceMetricFramesCount + 1) + 0xC; size += BitUtils.AlignUp(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment); } @@ -847,13 +854,13 @@ namespace Ryujinx.Audio.Renderer.Server } PoolMapper mapper = new(_processHandle, false); - mapper.Unmap(ref _dspMemoryPoolState); + mapper.Unmap(ref _dspMemoryPoolInfo); PoolMapper.ClearUsageState(_memoryPools); for (int i = 0; i < _memoryPoolCount; i++) { - ref MemoryPoolState memoryPool = ref _memoryPools.Span[i]; + ref MemoryPoolInfo memoryPool = ref _memoryPools.Span[i]; if (memoryPool.IsMapped()) { @@ -875,7 +882,7 @@ namespace Ryujinx.Audio.Renderer.Server public void SetVoiceDropParameter(float voiceDropParameter) { - _voiceDropParameter = Math.Clamp(voiceDropParameter, 0.0f, 2.0f); + _voiceDropParameter = Math.Clamp(voiceDropParameter, 0.0f, 4.0f); } public float GetVoiceDropParameter() diff --git a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/src/Ryujinx.Audio/Renderer/Server/BehaviourInfo.cs similarity index 89% rename from src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs rename to src/Ryujinx.Audio/Renderer/Server/BehaviourInfo.cs index f725eb9f3..04463e876 100644 --- a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/BehaviourInfo.cs @@ -1,3 +1,5 @@ +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Common.Memory; using System; using System.Buffers; using System.Diagnostics; @@ -9,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Server /// Behaviour context. /// /// This handles features based on the audio renderer revision provided by the user. - public class BehaviourContext + public class BehaviourInfo { /// /// The base magic of the Audio Renderer revision. @@ -40,7 +42,7 @@ namespace Ryujinx.Audio.Renderer.Server public const int Revision4 = 4 << 24; /// - /// REV5: , were added to voice. + /// REV5: , were added to voice. /// A new performance frame format (version 2) was added with support for more information about DSP timing. /// was added to supply the count of update done sent to the DSP. /// A new version of the command estimator was added to address timing changes caused by the voice changes. @@ -64,7 +66,7 @@ namespace Ryujinx.Audio.Renderer.Server /// /// REV8: /// Wavebuffer was changed to support more control over loop (you can now specify where to start and end a loop, and how many times to loop). - /// was added (see for more info). + /// was added (see for more info). /// Final leftovers of the codec system were removed. /// support was added. /// A new version of the command estimator was added to address timing changes caused by the voice and command changes. @@ -115,16 +117,27 @@ namespace Ryujinx.Audio.Renderer.Server /// /// This was added in system update 18.0.0 public const int Revision13 = 13 << 24; + + /// + /// REV14: + /// Fixes the Depop Bug. + /// + /// + /// This was added in system update 19.0.0 + public const int Revision14 = 14 << 24; + + /// + /// REV15: + /// Support for float coefficients in biquad filters + /// + /// + /// This was added in system update 19.0.0 + public const int Revision15 = 15 << 24; /// /// Last revision supported by the implementation. /// - public const int LastRevision = Revision13; - - /// - /// Target revision magic supported by the implementation. - /// - public const int ProcessRevision = BaseRevisionMagic + LastRevision; + public const int LastRevision = Revision15; /// /// Get the revision number from the revision magic. @@ -133,15 +146,25 @@ namespace Ryujinx.Audio.Renderer.Server /// The revision number. public static int GetRevisionNumber(int revision) => (revision - BaseRevisionMagic) >> 24; + /// + /// Target revision magic supported by the implementation. + /// + public const int ProcessRevision = BaseRevisionMagic + LastRevision; + /// /// Current active revision. /// public int UserRevision { get; private set; } + + /// + /// Current flags of the . + /// + private ulong _flags; /// /// Error storage. /// - private readonly ErrorInfo[] _errorInfos; + private readonly Array10 _errorInfos; /// /// Current position in the array. @@ -149,17 +172,12 @@ namespace Ryujinx.Audio.Renderer.Server private uint _errorIndex; /// - /// Current flags of the . + /// Create a new instance of . /// - private ulong _flags; - - /// - /// Create a new instance of . - /// - public BehaviourContext() + public BehaviourInfo() { UserRevision = 0; - _errorInfos = new ErrorInfo[Constants.MaxErrorInfos]; + _errorInfos = new Array10(); _errorIndex = 0; } @@ -173,7 +191,7 @@ namespace Ryujinx.Audio.Renderer.Server } /// - /// Update flags of the . + /// Update flags of the . /// /// The new flags. public void UpdateFlags(ulong flags) @@ -321,9 +339,9 @@ namespace Ryujinx.Audio.Renderer.Server } /// - /// Check if the audio renderer should support . + /// Check if the audio renderer should support . /// - /// True if the audio renderer should support . + /// True if the audio renderer should support . public bool IsDecodingBehaviourFlagSupported() { return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision5); @@ -400,6 +418,24 @@ namespace Ryujinx.Audio.Renderer.Server { return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision13); } + + /// + /// Check if the audio renderer should support the depop bug fix. + /// + /// True if the audio renderer supports the depop bug fix + public bool IsSplitterDepopBugFixEnabled() + { + return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision14); + } + + /// + /// Check if the audio renderer should support biquad filter with float coefficients. + /// + /// True if the audio renderer support biquad filter with float coefficients + public bool IsBiquadFilterParameterFloatSupported() + { + return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision15); + } /// /// Get the version of the . @@ -440,7 +476,7 @@ namespace Ryujinx.Audio.Renderer.Server if (_errorIndex <= Constants.MaxErrorInfos - 1) { - _errorInfos[_errorIndex++] = errorInfo; + _errorInfos[(int)_errorIndex++] = errorInfo; } } @@ -457,22 +493,8 @@ namespace Ryujinx.Audio.Renderer.Server } errorCount = Math.Min(_errorIndex, Constants.MaxErrorInfos); - - for (int i = 0; i < Constants.MaxErrorInfos; i++) - { - if (i < errorCount) - { - errorInfos[i] = _errorInfos[i]; - } - else - { - errorInfos[i] = new ErrorInfo - { - ErrorCode = 0, - ExtraErrorInfo = 0, - }; - } - } + + _errorInfos.AsSpan().CopyTo(errorInfos); } /// diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs index 4c353b37e..8c4f0a479 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs @@ -5,9 +5,11 @@ using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Server.Performance; using Ryujinx.Audio.Renderer.Server.Sink; +using Ryujinx.Audio.Renderer.Server.Splitter; using Ryujinx.Audio.Renderer.Server.Upsampler; using Ryujinx.Audio.Renderer.Server.Voice; using System; +using System.Runtime.CompilerServices; using CpuAddress = System.UInt64; namespace Ryujinx.Audio.Renderer.Server @@ -77,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Server /// The target buffer offset. /// The node id associated to this command. /// Set to true if the voice was playing previously. - public void GenerateDepopPrepare(Memory state, Memory depopBuffer, uint bufferCount, uint bufferOffset, int nodeId, bool wasPlaying) + public void GenerateDepopPrepare(Memory state, Memory depopBuffer, uint bufferCount, uint bufferOffset, int nodeId, bool wasPlaying) { DepopPrepareCommand command = new(state, depopBuffer, bufferCount, bufferOffset, nodeId, wasPlaying); @@ -120,14 +122,14 @@ namespace Ryujinx.Audio.Renderer.Server /// /// Create a new . /// - /// The to generate the command from. - /// The to generate the command from. + /// The to generate the command from. + /// The to generate the command from. /// The output buffer index to use. /// The target channel index. /// The node id associated to this command. - public void GenerateDataSourceVersion2(ref VoiceState voiceState, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) + public void GenerateDataSourceVersion2(ref VoiceInfo voiceInfo, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { - DataSourceVersion2Command command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); + DataSourceVersion2Command command = new(ref voiceInfo, state, outputBufferIndex, channelIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -137,14 +139,14 @@ namespace Ryujinx.Audio.Renderer.Server /// /// Create a new . /// - /// The to generate the command from. - /// The to generate the command from. + /// The to generate the command from. + /// The to generate the command from. /// The output buffer index to use. /// The target channel index. /// The node id associated to this command. - public void GeneratePcmInt16DataSourceVersion1(ref VoiceState voiceState, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) + public void GeneratePcmInt16DataSourceVersion1(ref VoiceInfo voiceInfo, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { - PcmInt16DataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); + PcmInt16DataSourceCommandVersion1 command = new(ref voiceInfo, state, outputBufferIndex, channelIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -154,14 +156,14 @@ namespace Ryujinx.Audio.Renderer.Server /// /// Create a new . /// - /// The to generate the command from. - /// The to generate the command from. + /// The to generate the command from. + /// The to generate the command from. /// The output buffer index to use. /// The target channel index. /// The node id associated to this command. - public void GeneratePcmFloatDataSourceVersion1(ref VoiceState voiceState, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) + public void GeneratePcmFloatDataSourceVersion1(ref VoiceInfo voiceInfo, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { - PcmFloatDataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); + PcmFloatDataSourceCommandVersion1 command = new(ref voiceInfo, state, outputBufferIndex, channelIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -171,13 +173,13 @@ namespace Ryujinx.Audio.Renderer.Server /// /// Create a new . /// - /// The to generate the command from. - /// The to generate the command from. + /// The to generate the command from. + /// The to generate the command from. /// The output buffer index to use. /// The node id associated to this command. - public void GenerateAdpcmDataSourceVersion1(ref VoiceState voiceState, Memory state, ushort outputBufferIndex, int nodeId) + public void GenerateAdpcmDataSourceVersion1(ref VoiceInfo voiceInfo, Memory state, ushort outputBufferIndex, int nodeId) { - AdpcmDataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, nodeId); + AdpcmDataSourceCommandVersion1 command = new(ref voiceInfo, state, outputBufferIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -194,7 +196,7 @@ namespace Ryujinx.Audio.Renderer.Server /// The output buffer offset. /// Set to true if the biquad filter state needs to be initialized. /// The node id associated to this command. - public void GenerateBiquadFilter(int baseIndex, ref BiquadFilterParameter filter, Memory biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, bool needInitialization, int nodeId) + public void GenerateBiquadFilter(int baseIndex, ref BiquadFilterParameter2 filter, Memory biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, bool needInitialization, int nodeId) { BiquadFilterCommand command = new(baseIndex, ref filter, biquadFilterStateMemory, inputBufferOffset, outputBufferOffset, needInitialization, nodeId); @@ -213,7 +215,7 @@ namespace Ryujinx.Audio.Renderer.Server /// The output buffer offset. /// Set to true if the biquad filter state is initialized. /// The node id associated to this command. - public void GenerateMultiTapBiquadFilter(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) + public void GenerateMultiTapBiquadFilter(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) { MultiTapBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId); @@ -230,9 +232,9 @@ namespace Ryujinx.Audio.Renderer.Server /// The base output index. /// The previous volume. /// The new volume. - /// The to generate the command from. + /// The to generate the command from. /// The node id associated to this command. - public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, ReadOnlySpan previousVolume, ReadOnlySpan volume, Memory state, int nodeId) + public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, ReadOnlySpan previousVolume, ReadOnlySpan volume, Memory state, int nodeId) { MixRampGroupedCommand command = new(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId); @@ -248,10 +250,10 @@ namespace Ryujinx.Audio.Renderer.Server /// The new volume. /// The input buffer index. /// The output buffer index. - /// The index in the array to store the ramped sample. - /// The to generate the command from. + /// The index in the array to store the ramped sample. + /// The to generate the command from. /// The node id associated to this command. - public void GenerateMixRamp(float previousVolume, float volume, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory state, int nodeId) + public void GenerateMixRamp(float previousVolume, float volume, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory state, int nodeId) { MixRampCommand command = new(previousVolume, volume, inputBufferIndex, outputBufferIndex, lastSampleIndex, state, nodeId); @@ -267,8 +269,8 @@ namespace Ryujinx.Audio.Renderer.Server /// The new volume. /// The input buffer index. /// The output buffer index. - /// The index in the array to store the ramped sample. - /// The to generate the command from. + /// The index in the array to store the ramped sample. + /// The to generate the command from. /// The biquad filter parameter. /// The biquad state. /// The previous biquad state. @@ -282,8 +284,8 @@ namespace Ryujinx.Audio.Renderer.Server uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, - Memory state, - ref BiquadFilterParameter filter, + Memory state, + ref BiquadFilterParameter2 filter, Memory biquadFilterState, Memory previousBiquadFilterState, bool needInitialization, @@ -318,8 +320,8 @@ namespace Ryujinx.Audio.Renderer.Server /// The new volume. /// The input buffer index. /// The output buffer index. - /// The index in the array to store the ramped sample. - /// The to generate the command from. + /// The index in the array to store the ramped sample. + /// The to generate the command from. /// First biquad filter parameter. /// Second biquad filter parameter. /// First biquad state. @@ -337,9 +339,9 @@ namespace Ryujinx.Audio.Renderer.Server uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, - Memory state, - ref BiquadFilterParameter filter0, - ref BiquadFilterParameter filter1, + Memory state, + ref BiquadFilterParameter2 filter0, + ref BiquadFilterParameter2 filter1, Memory biquadFilterState0, Memory biquadFilterState1, Memory previousBiquadFilterState0, @@ -655,14 +657,14 @@ namespace Ryujinx.Audio.Renderer.Server /// Create a new . /// /// The offset of the mix buffer. - /// The associated. + /// The associated. /// The total input count. /// The input buffer mix offset. /// The buffer count per sample. /// The source sample count. /// The source sample rate. /// The node id associated to this command. - public void GenerateUpsample(uint bufferOffset, UpsamplerState upsampler, uint inputCount, Span inputBufferOffset, uint bufferCountPerSample, uint sampleCount, uint sampleRate, int nodeId) + public void GenerateUpsample(uint bufferOffset, UpsamplerInfo upsampler, uint inputCount, Span inputBufferOffset, uint bufferCountPerSample, uint sampleCount, uint sampleRate, int nodeId) { UpsampleCommand command = new(bufferOffset, upsampler, inputCount, inputBufferOffset, bufferCountPerSample, sampleCount, sampleRate, nodeId); @@ -687,5 +689,23 @@ namespace Ryujinx.Audio.Renderer.Server AddCommand(command); } + + public void GenerateFillBuffer(SplitterDestination destination, float value, int length, int nodeId) + { + FillBufferCommand command; + + if (Unsafe.IsNullRef(ref destination.GetV2RefOrNull())) + { + command = new(destination.GetV1RefOrNull(), length, value, nodeId); + } + else + { + command = new(destination.GetV2RefOrNull(), length, value, nodeId); + } + + command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); + + AddCommand(command); + } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs index 519de9b65..c88aa51f2 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Dsp; using Ryujinx.Audio.Renderer.Dsp.Command; using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; @@ -41,27 +42,27 @@ namespace Ryujinx.Audio.Renderer.Server _commandBuffer.GenerateClearMixBuffer(Constants.InvalidNodeId); } - private void GenerateDataSource(ref VoiceState voiceState, Memory dspState, int channelIndex) + private void GenerateDataSource(ref VoiceInfo voiceInfo, Memory dspState, int channelIndex) { - if (voiceState.MixId != Constants.UnusedMixId) + if (voiceInfo.MixId != Constants.UnusedMixId) { - ref MixState mix = ref _mixContext.GetState(voiceState.MixId); + ref MixInfo mix = ref _mixContext.GetState(voiceInfo.MixId); _commandBuffer.GenerateDepopPrepare( dspState, _rendererContext.DepopBuffer, mix.BufferCount, mix.BufferOffset, - voiceState.NodeId, - voiceState.WasPlaying); + voiceInfo.NodeId, + voiceInfo.WasPlaying); } - else if (voiceState.SplitterId != Constants.UnusedSplitterId) + else if (voiceInfo.SplitterId != Constants.UnusedSplitterId) { int destinationId = 0; while (true) { - SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++); + SplitterDestination destination = _splitterContext.GetDestination((int)voiceInfo.SplitterId, destinationId++); if (destination.IsNull) { @@ -74,15 +75,17 @@ namespace Ryujinx.Audio.Renderer.Server if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt) { - ref MixState mix = ref _mixContext.GetState(mixId); + ref MixInfo mix = ref _mixContext.GetState(mixId); + + // _commandBuffer.GenerateFillBuffer(); _commandBuffer.GenerateDepopPrepare( dspState, _rendererContext.DepopBuffer, mix.BufferCount, mix.BufferOffset, - voiceState.NodeId, - voiceState.WasPlaying); + voiceInfo.NodeId, + voiceInfo.WasPlaying); destination.MarkAsNeedToUpdateInternalState(); } @@ -90,71 +93,71 @@ namespace Ryujinx.Audio.Renderer.Server } } - if (!voiceState.WasPlaying) + if (!voiceInfo.WasPlaying) { - Debug.Assert(voiceState.SampleFormat != SampleFormat.Adpcm || channelIndex == 0); + Debug.Assert(voiceInfo.SampleFormat != SampleFormat.Adpcm || channelIndex == 0); - if (_rendererContext.BehaviourContext.IsWaveBufferVersion2Supported()) + if (_rendererContext.BehaviourInfo.IsWaveBufferVersion2Supported()) { _commandBuffer.GenerateDataSourceVersion2( - ref voiceState, + ref voiceInfo, dspState, (ushort)_rendererContext.MixBufferCount, (ushort)channelIndex, - voiceState.NodeId); + voiceInfo.NodeId); } else { - switch (voiceState.SampleFormat) + switch (voiceInfo.SampleFormat) { case SampleFormat.PcmInt16: _commandBuffer.GeneratePcmInt16DataSourceVersion1( - ref voiceState, + ref voiceInfo, dspState, (ushort)_rendererContext.MixBufferCount, (ushort)channelIndex, - voiceState.NodeId); + voiceInfo.NodeId); break; case SampleFormat.PcmFloat: _commandBuffer.GeneratePcmFloatDataSourceVersion1( - ref voiceState, + ref voiceInfo, dspState, (ushort)_rendererContext.MixBufferCount, (ushort)channelIndex, - voiceState.NodeId); + voiceInfo.NodeId); break; case SampleFormat.Adpcm: _commandBuffer.GenerateAdpcmDataSourceVersion1( - ref voiceState, + ref voiceInfo, dspState, (ushort)_rendererContext.MixBufferCount, - voiceState.NodeId); + voiceInfo.NodeId); break; default: - throw new NotImplementedException($"Unsupported data source {voiceState.SampleFormat}"); + throw new NotImplementedException($"Unsupported data source {voiceInfo.SampleFormat}"); } } } } - private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory state, int baseIndex, int bufferOffset, int nodeId) + private void GenerateBiquadFilterForVoice(ref VoiceInfo voiceInfo, Memory state, int baseIndex, int bufferOffset, int nodeId) { - bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing(); + bool supportsOptimizedPath = _rendererContext.BehaviourInfo.UseMultiTapBiquadFilterProcessing(); - Span biquadFiltersSpan = voiceState.BiquadFilters.AsSpan(); + Span biquadFiltersSpan = voiceInfo.BiquadFilters.AsSpan(); if (supportsOptimizedPath && biquadFiltersSpan[0].Enable && biquadFiltersSpan[1].Enable) { Memory biquadStateRawMemory = SpanMemoryManager.Cast(state)[..(Unsafe.SizeOf() * Constants.VoiceBiquadFilterCount)]; Memory stateMemory = SpanMemoryManager.Cast(biquadStateRawMemory); - _commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, biquadFiltersSpan, stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId); + _commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, biquadFiltersSpan, stateMemory, bufferOffset, bufferOffset, voiceInfo.BiquadFilterNeedInitialization, nodeId); } else { for (int i = 0; i < biquadFiltersSpan.Length; i++) { - ref BiquadFilterParameter filter = ref biquadFiltersSpan[i]; + ref BiquadFilterParameter2 filter = ref biquadFiltersSpan[i]; if (filter.Enable) { @@ -167,7 +170,7 @@ namespace Ryujinx.Audio.Renderer.Server stateMemory.Slice(i, 1), bufferOffset, bufferOffset, - !voiceState.BiquadFilterNeedInitialization[i], + !voiceInfo.BiquadFilterNeedInitialization[i], nodeId); } } @@ -176,7 +179,7 @@ namespace Ryujinx.Audio.Renderer.Server private void GenerateVoiceMixWithSplitter( SplitterDestination destination, - Memory state, + Memory state, uint bufferOffset, uint bufferCount, uint bufferIndex, @@ -185,8 +188,8 @@ namespace Ryujinx.Audio.Renderer.Server ReadOnlySpan mixVolumes = destination.MixBufferVolume; ReadOnlySpan previousMixVolumes = destination.PreviousMixBufferVolume; - ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0); - ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1); + ref BiquadFilterParameter2 bqf0 = ref destination.GetBiquadFilterParameter(0); + ref BiquadFilterParameter2 bqf1 = ref destination.GetBiquadFilterParameter(1); Memory bqfState = _splitterContext.GetBiquadFilterState(destination); @@ -270,7 +273,7 @@ namespace Ryujinx.Audio.Renderer.Server private void GenerateVoiceMix( ReadOnlySpan mixVolumes, ReadOnlySpan previousMixVolumes, - Memory state, + Memory state, uint bufferOffset, uint bufferCount, uint bufferIndex, @@ -309,27 +312,27 @@ namespace Ryujinx.Audio.Renderer.Server } } - private void GenerateVoice(ref VoiceState voiceState) + private void GenerateVoice(ref VoiceInfo voiceInfo) { - int nodeId = voiceState.NodeId; - uint channelsCount = voiceState.ChannelsCount; + int nodeId = voiceInfo.NodeId; + uint channelsCount = voiceInfo.ChannelsCount; - Span channelResourceIdsSpan = voiceState.ChannelResourceIds.AsSpan(); - Span biquadFiltersSpan = voiceState.BiquadFilters.AsSpan(); + Span channelResourceIdsSpan = voiceInfo.ChannelResourceIds.AsSpan(); + Span biquadFiltersSpan = voiceInfo.BiquadFilters.AsSpan(); for (int channelIndex = 0; channelIndex < channelsCount; channelIndex++) { - Memory dspStateMemory = _voiceContext.GetUpdateStateForDsp(channelResourceIdsSpan[channelIndex]); + Memory dspStateMemory = _voiceContext.GetUpdateStateForDsp(channelResourceIdsSpan[channelIndex]); ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(channelResourceIdsSpan[channelIndex]); PerformanceDetailType dataSourceDetailType = PerformanceDetailType.Adpcm; - if (voiceState.SampleFormat == SampleFormat.PcmInt16) + if (voiceInfo.SampleFormat == SampleFormat.PcmInt16) { dataSourceDetailType = PerformanceDetailType.PcmInt16; } - else if (voiceState.SampleFormat == SampleFormat.PcmFloat) + else if (voiceInfo.SampleFormat == SampleFormat.PcmFloat) { dataSourceDetailType = PerformanceDetailType.PcmFloat; } @@ -345,18 +348,18 @@ namespace Ryujinx.Audio.Renderer.Server GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); } - GenerateDataSource(ref voiceState, dspStateMemory, channelIndex); + GenerateDataSource(ref voiceInfo, dspStateMemory, channelIndex); if (performanceInitialized) { GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId); } - if (voiceState.WasPlaying) + if (voiceInfo.WasPlaying) { - voiceState.PreviousVolume = 0.0f; + voiceInfo.PreviousVolume = 0.0f; } - else if (voiceState.HasAnyDestination()) + else if (voiceInfo.HasAnyDestination()) { performanceInitialized = false; @@ -367,7 +370,7 @@ namespace Ryujinx.Audio.Renderer.Server GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); } - GenerateBiquadFilterForVoice(ref voiceState, dspStateMemory, (int)_rendererContext.MixBufferCount, channelIndex, nodeId); + GenerateBiquadFilterForVoice(ref voiceInfo, dspStateMemory, (int)_rendererContext.MixBufferCount, channelIndex, nodeId); if (performanceInitialized) { @@ -384,8 +387,8 @@ namespace Ryujinx.Audio.Renderer.Server } _commandBuffer.GenerateVolumeRamp( - voiceState.PreviousVolume, - voiceState.Volume, + voiceInfo.PreviousVolume, + voiceInfo.Volume, _rendererContext.MixBufferCount + (uint)channelIndex, nodeId); @@ -394,17 +397,17 @@ namespace Ryujinx.Audio.Renderer.Server GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId); } - voiceState.PreviousVolume = voiceState.Volume; + voiceInfo.PreviousVolume = voiceInfo.Volume; - if (voiceState.MixId == Constants.UnusedMixId) + if (voiceInfo.MixId == Constants.UnusedMixId) { - if (voiceState.SplitterId != Constants.UnusedSplitterId) + if (voiceInfo.SplitterId != Constants.UnusedSplitterId) { int destinationId = channelIndex; while (true) { - SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId); + SplitterDestination destination = _splitterContext.GetDestination((int)voiceInfo.SplitterId, destinationId); if (destination.IsNull) { @@ -419,7 +422,7 @@ namespace Ryujinx.Audio.Renderer.Server if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt) { - ref MixState mix = ref _mixContext.GetState(mixId); + ref MixInfo mix = ref _mixContext.GetState(mixId); if (destination.IsBiquadFilterEnabled()) { @@ -451,7 +454,7 @@ namespace Ryujinx.Audio.Renderer.Server } else { - ref MixState mix = ref _mixContext.GetState(voiceState.MixId); + ref MixInfo mix = ref _mixContext.GetState(voiceInfo.MixId); performanceInitialized = false; @@ -479,9 +482,9 @@ namespace Ryujinx.Audio.Renderer.Server channelResource.UpdateState(); } - for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++) + for (int i = 0; i < voiceInfo.BiquadFilterNeedInitialization.Length; i++) { - voiceState.BiquadFilterNeedInitialization[i] = biquadFiltersSpan[i].Enable; + voiceInfo.BiquadFilterNeedInitialization[i] = biquadFiltersSpan[i].Enable; } } } @@ -491,11 +494,11 @@ namespace Ryujinx.Audio.Renderer.Server { for (int i = 0; i < _voiceContext.GetCount(); i++) { - ref VoiceState sortedState = ref _voiceContext.GetSortedState(i); + ref VoiceInfo sortedInfo = ref _voiceContext.GetSortedState(i); - if (!sortedState.ShouldSkip() && sortedState.UpdateForCommandGeneration(_voiceContext)) + if (!sortedInfo.ShouldSkip() && sortedInfo.UpdateForCommandGeneration(_voiceContext)) { - int nodeId = sortedState.NodeId; + int nodeId = sortedInfo.NodeId; PerformanceEntryAddresses performanceEntry = new(); @@ -508,7 +511,7 @@ namespace Ryujinx.Audio.Renderer.Server GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); } - GenerateVoice(ref sortedState); + GenerateVoice(ref sortedInfo); if (performanceInitialized) { @@ -639,9 +642,9 @@ namespace Ryujinx.Audio.Renderer.Server if (effect.IsEnabled) { bool needInitialization = effect.Parameter.Status == UsageState.Invalid || - (effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed()); + (effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); - BiquadFilterParameter parameter = new() + BiquadFilterParameter2 parameter = new() { Enable = true, }; @@ -683,7 +686,7 @@ namespace Ryujinx.Audio.Renderer.Server ulong workBuffer = effect.GetWorkBuffer(-1); - if (_rendererContext.BehaviourContext.IsEffectInfoVersion2Supported()) + if (_rendererContext.BehaviourInfo.IsEffectInfoVersion2Supported()) { Memory dspResultState; @@ -777,7 +780,7 @@ namespace Ryujinx.Audio.Renderer.Server nodeId); } - private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect) + private void GenerateEffect(ref MixInfo mix, int effectId, BaseEffect effect) { int nodeId = mix.NodeId; @@ -807,13 +810,13 @@ namespace Ryujinx.Audio.Renderer.Server GenerateAuxEffect(mix.BufferOffset, (AuxiliaryBufferEffect)effect, nodeId); break; case EffectType.Delay: - GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported()); + GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId, _rendererContext.BehaviourInfo.IsNewEffectChannelMappingSupported()); break; case EffectType.Reverb: - GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported()); + GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported, _rendererContext.BehaviourInfo.IsNewEffectChannelMappingSupported()); break; case EffectType.Reverb3d: - GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported()); + GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId, _rendererContext.BehaviourInfo.IsNewEffectChannelMappingSupported()); break; case EffectType.BiquadFilter: GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId); @@ -839,7 +842,7 @@ namespace Ryujinx.Audio.Renderer.Server effect.UpdateForCommandGeneration(); } - private void GenerateEffects(ref MixState mix) + private void GenerateEffects(ref MixInfo mix) { ReadOnlySpan effectProcessingOrderArray = mix.EffectProcessingOrderArray; @@ -875,8 +878,8 @@ namespace Ryujinx.Audio.Renderer.Server ref bool isFirstMixBuffer, int nodeId) { - ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0); - ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1); + ref BiquadFilterParameter2 bqf0 = ref destination.GetBiquadFilterParameter(0); + ref BiquadFilterParameter2 bqf1 = ref destination.GetBiquadFilterParameter(1); Memory bqfState = _splitterContext.GetBiquadFilterState(destination); @@ -888,7 +891,7 @@ namespace Ryujinx.Audio.Renderer.Server inputBufferIndex, outputBufferIndex, 0, - Memory.Empty, + Memory.Empty, ref bqf0, ref bqf1, bqfState[..1], @@ -912,7 +915,7 @@ namespace Ryujinx.Audio.Renderer.Server inputBufferIndex, outputBufferIndex, 0, - Memory.Empty, + Memory.Empty, ref bqf0, bqfState[..1], bqfState.Slice(1, 1), @@ -931,7 +934,7 @@ namespace Ryujinx.Audio.Renderer.Server inputBufferIndex, outputBufferIndex, 0, - Memory.Empty, + Memory.Empty, ref bqf1, bqfState[..1], bqfState.Slice(1, 1), @@ -946,7 +949,7 @@ namespace Ryujinx.Audio.Renderer.Server isFirstMixBuffer = false; } - private void GenerateMix(ref MixState mix) + private void GenerateMix(ref MixInfo mix) { if (mix.HasAnyDestination()) { @@ -975,7 +978,7 @@ namespace Ryujinx.Audio.Renderer.Server if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt) { - ref MixState destinationMix = ref _mixContext.GetState(mixId); + ref MixInfo destinationMix = ref _mixContext.GetState(mixId); uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount); @@ -1014,7 +1017,7 @@ namespace Ryujinx.Audio.Renderer.Server } else { - ref MixState destinationMix = ref _mixContext.GetState(mix.DestinationMixId); + ref MixInfo destinationMix = ref _mixContext.GetState(mix.DestinationMixId); for (uint bufferIndex = 0; bufferIndex < mix.BufferCount; bufferIndex++) { @@ -1036,7 +1039,7 @@ namespace Ryujinx.Audio.Renderer.Server } } - private void GenerateSubMix(ref MixState subMix) + private void GenerateSubMix(ref MixInfo subMix) { _commandBuffer.GenerateDepopForMixBuffers( _rendererContext.DepopBuffer, @@ -1072,11 +1075,11 @@ namespace Ryujinx.Audio.Renderer.Server { for (int id = 0; id < _mixContext.GetCount(); id++) { - ref MixState sortedState = ref _mixContext.GetSortedState(id); + ref MixInfo sortedInfo = ref _mixContext.GetSortedState(id); - if (sortedState.IsUsed && sortedState.MixId != Constants.FinalMixId) + if (sortedInfo.IsUsed && sortedInfo.MixId != Constants.FinalMixId) { - int nodeId = sortedState.NodeId; + int nodeId = sortedInfo.NodeId; PerformanceEntryAddresses performanceEntry = new(); @@ -1089,7 +1092,7 @@ namespace Ryujinx.Audio.Renderer.Server GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId); } - GenerateSubMix(ref sortedState); + GenerateSubMix(ref sortedInfo); if (performanceInitialized) { @@ -1101,7 +1104,7 @@ namespace Ryujinx.Audio.Renderer.Server private void GenerateFinalMix() { - ref MixState finalMix = ref _mixContext.GetFinalState(); + ref MixInfo finalMix = ref _mixContext.GetFinalState(); _commandBuffer.GenerateDepopForMixBuffers( _rendererContext.DepopBuffer, @@ -1180,16 +1183,16 @@ namespace Ryujinx.Audio.Renderer.Server } } - private void GenerateCircularBuffer(CircularBufferSink sink, ref MixState finalMix) + private void GenerateCircularBuffer(CircularBufferSink sink, ref MixInfo finalMix) { _commandBuffer.GenerateCircularBuffer(finalMix.BufferOffset, sink, Constants.InvalidNodeId); } - private void GenerateDevice(DeviceSink sink, ref MixState finalMix) + private void GenerateDevice(DeviceSink sink, ref MixInfo finalMix) { - if (_commandBuffer.CommandList.SampleRate != 48000 && sink.UpsamplerState == null) + if (_commandBuffer.CommandList.SampleRate != 48000 && sink.UpsamplerInfo == null) { - sink.UpsamplerState = _rendererContext.UpsamplerManager.Allocate(); + sink.UpsamplerInfo = _rendererContext.UpsamplerManager.Allocate(); } bool useCustomDownMixingCommand = _rendererContext.ChannelCount == 2 && sink.Parameter.DownMixParameterEnabled; @@ -1216,11 +1219,11 @@ namespace Ryujinx.Audio.Renderer.Server CommandList commandList = _commandBuffer.CommandList; - if (sink.UpsamplerState != null) + if (sink.UpsamplerInfo != null) { _commandBuffer.GenerateUpsample( finalMix.BufferOffset, - sink.UpsamplerState, + sink.UpsamplerInfo, sink.Parameter.InputCount, sink.Parameter.Input.AsSpan(), commandList.BufferCount, @@ -1237,7 +1240,7 @@ namespace Ryujinx.Audio.Renderer.Server Constants.InvalidNodeId); } - private void GenerateSink(BaseSink sink, ref MixState finalMix) + private void GenerateSink(BaseSink sink, ref MixInfo finalMix) { bool performanceInitialized = false; @@ -1275,7 +1278,7 @@ namespace Ryujinx.Audio.Renderer.Server public void GenerateSinks() { - ref MixState finalMix = ref _mixContext.GetFinalState(); + ref MixInfo finalMix = ref _mixContext.GetFinalState(); for (int i = 0; i < _sinkContext.GetCount(); i++) { diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs index cff754b82..8abe81e1f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs @@ -194,5 +194,10 @@ namespace Ryujinx.Audio.Renderer.Server { return 0; } + + public uint Estimate(FillBufferCommand command) + { + return 0; + } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs index 1e9b6c53e..85e340393 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs @@ -486,5 +486,10 @@ namespace Ryujinx.Audio.Renderer.Server { return 0; } + + public uint Estimate(FillBufferCommand command) + { + return 0; + } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs index 0dbdcb9c9..96913e4b8 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs @@ -3,7 +3,7 @@ using Ryujinx.Audio.Renderer.Dsp.Command; using Ryujinx.Audio.Renderer.Parameter.Effect; using System; using System.Diagnostics; -using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using Ryujinx.Audio.Renderer.Parameter; namespace Ryujinx.Audio.Renderer.Server { @@ -650,5 +650,10 @@ namespace Ryujinx.Audio.Renderer.Server { return 0; } + + public virtual uint Estimate(FillBufferCommand command) + { + return 0; + } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs index fe6103f2b..906065792 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs @@ -286,5 +286,10 @@ namespace Ryujinx.Audio.Renderer.Server return 8683; } } + + public override uint Estimate(FillBufferCommand command) + { + return 0; + } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs index 77d9b5c29..6324689e4 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs @@ -174,6 +174,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect updateErrorInfo = new ErrorInfo(); } + + /// + /// Update the internal state from a user version 3 parameter. + /// + /// The possible that was generated. + /// The user parameter. + /// The mapper to use. + public virtual void Update(out ErrorInfo updateErrorInfo, in EffectInParameterVersion3 parameter, PoolMapper mapper) + { + Debug.Assert(IsTypeValid(in parameter)); + + updateErrorInfo = new ErrorInfo(); + } /// /// Get the work buffer DSP address at the given index. diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs index 3b3e1021c..f920c6873 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs @@ -1,4 +1,5 @@ using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Dsp; using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter.Effect; @@ -17,7 +18,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect /// /// The biquad filter parameter. /// - public BiquadFilterEffectParameter Parameter; + public BiquadFilterEffectParameter2 Parameter; /// /// The biquad filter state. @@ -29,7 +30,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect /// public BiquadFilterEffect() { - Parameter = new BiquadFilterEffectParameter(); + Parameter = new BiquadFilterEffectParameter2(); State = new BiquadFilterState[Constants.ChannelCountMax]; } @@ -44,6 +45,11 @@ namespace Ryujinx.Audio.Renderer.Server.Effect { Update(out updateErrorInfo, in parameter, mapper); } + + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion3 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, in parameter, mapper); + } public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter { @@ -51,7 +57,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect UpdateParameterBase(in parameter); - Parameter = MemoryMarshal.Cast(parameter.SpecificData)[0]; + if (typeof(T) == typeof(EffectInParameterVersion3)) + { + Parameter = MemoryMarshal.Cast(parameter.SpecificData)[0]; + } + else + { + BiquadFilterEffectParameter1 oldParameter = + MemoryMarshal.Cast(parameter.SpecificData)[0]; + Parameter = BiquadFilterHelper.ToBiquadFilterEffectParameter2(oldParameter); + } + IsEnabled = parameter.IsEnabled; updateErrorInfo = new BehaviourParameter.ErrorInfo(); diff --git a/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs b/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs index 9c4312ad6..360241278 100644 --- a/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs +++ b/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs @@ -38,5 +38,6 @@ namespace Ryujinx.Audio.Renderer.Server uint Estimate(CompressorCommand command); uint Estimate(BiquadFilterAndMixCommand command); uint Estimate(MultiTapBiquadFilterAndMixCommand command); + uint Estimate(FillBufferCommand command); } } diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs index a7ec4cf51..c037fbe32 100644 --- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs @@ -20,14 +20,14 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// public ulong Size; - private unsafe MemoryPoolState* _memoryPools; + private unsafe MemoryPoolInfo* _memoryPools; /// /// The forced DSP address of the region. /// public DspAddress ForceMappedDspAddress; - private readonly unsafe ref MemoryPoolState MemoryPoolState => ref *_memoryPools; + private readonly unsafe ref MemoryPoolInfo MemoryPoolInfo => ref *_memoryPools; public readonly unsafe bool HasMemoryPoolState => (IntPtr)_memoryPools != IntPtr.Zero; @@ -53,7 +53,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool return new AddressInfo { CpuAddress = cpuAddress, - _memoryPools = MemoryPoolState.Null, + _memoryPools = MemoryPoolInfo.Null, Size = size, ForceMappedDspAddress = 0, }; @@ -73,19 +73,19 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool unsafe { - _memoryPools = MemoryPoolState.Null; + _memoryPools = MemoryPoolInfo.Null; } } /// - /// Set the associated. + /// Set the associated. /// - /// The associated. - public void SetupMemoryPool(Span memoryPoolState) + /// The associated. + public void SetupMemoryPool(Span memoryPoolState) { unsafe { - fixed (MemoryPoolState* ptr = &MemoryMarshal.GetReference(memoryPoolState)) + fixed (MemoryPoolInfo* ptr = &MemoryMarshal.GetReference(memoryPoolState)) { SetupMemoryPool(ptr); } @@ -93,27 +93,27 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Set the associated. + /// Set the associated. /// - /// The associated. - public unsafe void SetupMemoryPool(MemoryPoolState* memoryPoolState) + /// The associated. + public unsafe void SetupMemoryPool(MemoryPoolInfo* memoryPoolState) { _memoryPools = memoryPoolState; } /// - /// Check if the is mapped. + /// Check if the is mapped. /// - /// Returns true if the is mapped. + /// Returns true if the is mapped. public readonly bool HasMappedMemoryPool() { - return HasMemoryPoolState && MemoryPoolState.IsMapped(); + return HasMemoryPoolState && MemoryPoolInfo.IsMapped(); } /// /// Get the DSP address associated to the . /// - /// If true, mark the as used. + /// If true, mark the as used. /// Returns the DSP address associated to the . public readonly DspAddress GetReference(bool markUsed) { @@ -124,10 +124,10 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool if (markUsed) { - MemoryPoolState.IsUsed = true; + MemoryPoolInfo.IsUsed = true; } - return MemoryPoolState.Translate(CpuAddress, Size); + return MemoryPoolInfo.Translate(CpuAddress, Size); } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolInfo.cs similarity index 73% rename from src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs rename to src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolInfo.cs index 91bd5dbf5..7f715d615 100644 --- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolInfo.cs @@ -9,62 +9,62 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// Server state for a memory pool. /// [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = Alignment)] - public struct MemoryPoolState + public struct MemoryPoolInfo { public const int Alignment = 0x10; /// - /// The location of the . + /// The location of the . /// public enum LocationType : uint { /// - /// located on the CPU side for user use. + /// located on the CPU side for user use. /// Cpu, /// - /// located on the DSP side for system use. + /// located on the DSP side for system use. /// Dsp, } /// - /// The CPU address associated to the . + /// The CPU address associated to the . /// public CpuAddress CpuAddress; /// - /// The DSP address associated to the . + /// The DSP address associated to the . /// public DspAddress DspAddress; /// - /// The size associated to the . + /// The size associated to the . /// public ulong Size; /// - /// The associated to the . + /// The associated to the . /// public LocationType Location; /// - /// Set to true if the is used. + /// Set to true if the is used. /// [MarshalAs(UnmanagedType.I1)] public bool IsUsed; - public static unsafe MemoryPoolState* Null => (MemoryPoolState*)IntPtr.Zero.ToPointer(); + public static unsafe MemoryPoolInfo* Null => (MemoryPoolInfo*)IntPtr.Zero.ToPointer(); /// - /// Create a new with the given . + /// Create a new with the given . /// /// The location type to use. - /// A new with the given . - public static MemoryPoolState Create(LocationType location) + /// A new with the given . + public static MemoryPoolInfo Create(LocationType location) { - return new MemoryPoolState + return new MemoryPoolInfo { CpuAddress = 0, DspAddress = 0, @@ -74,7 +74,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Set the and size of the . + /// Set the and size of the . /// /// The . /// The size. @@ -85,11 +85,11 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Check if the given and size is contains in the . + /// Check if the given and size is contains in the . /// /// The . /// The size. - /// True if the is contained inside the . + /// True if the is contained inside the . public readonly bool Contains(CpuAddress targetCpuAddress, ulong size) { if (CpuAddress <= targetCpuAddress && size + targetCpuAddress <= Size + CpuAddress) @@ -119,9 +119,9 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Is the mapped on the DSP? + /// Is the mapped on the DSP? /// - /// Returns true if the is mapped on the DSP. + /// Returns true if the is mapped on the DSP. public readonly bool IsMapped() { return DspAddress != 0; diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs index fe3ab74d7..f77cc2ab4 100644 --- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool const uint CurrentProcessPseudoHandle = 0xFFFF8001; /// - /// The result of . + /// The result of . /// public enum UpdateResult : uint { @@ -49,9 +49,9 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool private readonly uint _processHandle; /// - /// The that will be manipulated. + /// The that will be manipulated. /// - private readonly Memory _memoryPools; + private readonly Memory _memoryPools; /// /// If set to true, this will try to force map memory pool even if their state are considered invalid. @@ -67,7 +67,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool { _processHandle = processHandle; _isForceMapEnabled = isForceMapEnabled; - _memoryPools = Memory.Empty; + _memoryPools = Memory.Empty; } /// @@ -76,7 +76,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// The handle of the process owning the CPU memory manipulated. /// The user memory pools. /// If set to true, this will try to force map memory pool even if their state are considered invalid. - public PoolMapper(uint processHandle, Memory memoryPool, bool isForceMapEnabled) + public PoolMapper(uint processHandle, Memory memoryPool, bool isForceMapEnabled) { _processHandle = processHandle; _memoryPools = memoryPool; @@ -84,15 +84,15 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Initialize the for system use. + /// Initialize the for system use. /// - /// The for system use. + /// The for system use. /// The to assign. /// The size to assign. /// Returns true if mapping on the succeeded. - public bool InitializeSystemPool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size) + public bool InitializeSystemPool(ref MemoryPoolInfo memoryPool, CpuAddress cpuAddress, ulong size) { - if (memoryPool.Location != MemoryPoolState.LocationType.Dsp) + if (memoryPool.Location != MemoryPoolInfo.LocationType.Dsp) { return false; } @@ -101,13 +101,13 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Initialize the . + /// Initialize the . /// - /// The . + /// The . /// The to assign. /// The size to assign. /// Returns true if mapping on the succeeded. - public bool InitializePool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size) + public bool InitializePool(ref MemoryPoolInfo memoryPool, CpuAddress cpuAddress, ulong size) { memoryPool.SetCpuAddress(cpuAddress, size); @@ -115,18 +115,18 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Get the process handle associated to the . + /// Get the process handle associated to the . /// - /// The . - /// Returns the process handle associated to the . - public uint GetProcessHandle(ref MemoryPoolState memoryPool) + /// The . + /// Returns the process handle associated to the . + public uint GetProcessHandle(ref MemoryPoolInfo memoryPool) { - if (memoryPool.Location == MemoryPoolState.LocationType.Cpu) + if (memoryPool.Location == MemoryPoolInfo.LocationType.Cpu) { return CurrentProcessPseudoHandle; } - if (memoryPool.Location == MemoryPoolState.LocationType.Dsp) + if (memoryPool.Location == MemoryPoolInfo.LocationType.Dsp) { return _processHandle; } @@ -135,11 +135,11 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Map the on the . + /// Map the on the . /// - /// The to map. + /// The to map. /// Returns the DSP address mapped. - public DspAddress Map(ref MemoryPoolState memoryPool) + public DspAddress Map(ref MemoryPoolInfo memoryPool) { DspAddress result = AudioProcessorMemoryManager.Map(GetProcessHandle(ref memoryPool), memoryPool.CpuAddress, memoryPool.Size); @@ -152,11 +152,11 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Unmap the from the . + /// Unmap the from the . /// - /// The to unmap. + /// The to unmap. /// Returns true if unmapped. - public bool Unmap(ref MemoryPoolState memoryPool) + public bool Unmap(ref MemoryPoolInfo memoryPool) { if (memoryPool.IsUsed) { @@ -172,12 +172,12 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Find a associated to the region given. + /// Find a associated to the region given. /// /// The region . /// The region size. - /// Returns the found or if not found. - private Span FindMemoryPool(CpuAddress cpuAddress, ulong size) + /// Returns the found or if not found. + private Span FindMemoryPool(CpuAddress cpuAddress, ulong size) { if (!_memoryPools.IsEmpty && _memoryPools.Length > 0) { @@ -190,7 +190,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } } - return Span.Empty; + return Span.Empty; } /// @@ -201,7 +201,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool { if (_isForceMapEnabled) { - Span memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size); + Span memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size); if (!memoryPool.IsEmpty) { @@ -243,13 +243,13 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Update a using user parameters. + /// Update a using user parameters. /// - /// The to update. + /// The to update. /// Input user parameter. /// Output user parameter. /// Returns the of the operations performed. - public UpdateResult Update(ref MemoryPoolState memoryPool, in MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus) + public UpdateResult Update(ref MemoryPoolInfo memoryPool, in MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus) { MemoryPoolUserState inputState = inParameter.State; @@ -317,7 +317,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool if (_memoryPools.Length > 0) { - Span memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size); + Span memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size); if (!memoryPool.IsEmpty) { @@ -339,7 +339,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool { unsafe { - addressInfo.SetupMemoryPool(MemoryPoolState.Null); + addressInfo.SetupMemoryPool(MemoryPoolInfo.Null); } } @@ -347,12 +347,12 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } /// - /// Remove the usage flag from all the . + /// Remove the usage flag from all the . /// - /// The to reset. - public static void ClearUsageState(Memory memoryPool) + /// The to reset. + public static void ClearUsageState(Memory memoryPool) { - foreach (ref MemoryPoolState info in memoryPool.Span) + foreach (ref MemoryPoolInfo info in memoryPool.Span) { info.IsUsed = false; } diff --git a/src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs b/src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs index 8991ceaf9..cdb6511cb 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs @@ -17,12 +17,12 @@ namespace Ryujinx.Audio.Renderer.Server.Mix private uint _mixesCount; /// - /// Storage for . + /// Storage for . /// - private Memory _mixes; + private Memory _mixes; /// - /// Storage of the sorted indices to . + /// Storage of the sorted indices to . /// private Memory _sortedMixes; @@ -49,10 +49,10 @@ namespace Ryujinx.Audio.Renderer.Server.Mix /// Initialize the . /// /// The storage for sorted indices. - /// The storage of . + /// The storage of . /// The storage used for the . /// The storage used for the . - public void Initialize(Memory sortedMixes, Memory mixes, Memory nodeStatesWorkBuffer, Memory edgeMatrixWorkBuffer) + public void Initialize(Memory sortedMixes, Memory mixes, Memory nodeStatesWorkBuffer, Memory edgeMatrixWorkBuffer) { _mixesCount = (uint)mixes.Length; _mixes = mixes; @@ -82,30 +82,30 @@ namespace Ryujinx.Audio.Renderer.Server.Mix } /// - /// Get a reference to the final . + /// Get a reference to the final . /// - /// A reference to the final . - public ref MixState GetFinalState() + /// A reference to the final . + public ref MixInfo GetFinalState() { return ref GetState(Constants.FinalMixId); } /// - /// Get a reference to a at the given . + /// Get a reference to a at the given . /// /// The index to use. - /// A reference to a at the given . - public ref MixState GetState(int id) + /// A reference to a at the given . + public ref MixInfo GetState(int id) { return ref SpanIOHelper.GetFromMemory(_mixes, id, _mixesCount); } /// - /// Get a reference to a at the given of the sorted mix info. + /// Get a reference to a at the given of the sorted mix info. /// /// The index to use. - /// A reference to a at the given . - public ref MixState GetSortedState(int id) + /// A reference to a at the given . + public ref MixInfo GetSortedState(int id) { Debug.Assert(id >= 0 && id < _mixesCount); @@ -122,18 +122,18 @@ namespace Ryujinx.Audio.Renderer.Server.Mix } /// - /// Update the internal distance from the final mix value of every . + /// Update the internal distance from the final mix value of every . /// private void UpdateDistancesFromFinalMix() { - foreach (ref MixState mix in _mixes.Span) + foreach (ref MixInfo mix in _mixes.Span) { mix.ClearDistanceFromFinalMix(); } for (int i = 0; i < GetCount(); i++) { - ref MixState mix = ref GetState(i); + ref MixInfo mix = ref GetState(i); SetSortedState(i, i); @@ -149,13 +149,13 @@ namespace Ryujinx.Audio.Renderer.Server.Mix { if (mixId == Constants.UnusedMixId) { - distance = MixState.InvalidDistanceFromFinalMix; + distance = MixInfo.InvalidDistanceFromFinalMix; break; } - ref MixState distanceMix = ref GetState(mixId); + ref MixInfo distanceMix = ref GetState(mixId); - if (distanceMix.DistanceFromFinalMix != MixState.InvalidDistanceFromFinalMix) + if (distanceMix.DistanceFromFinalMix != MixInfo.InvalidDistanceFromFinalMix) { distance = distanceMix.DistanceFromFinalMix + 1; break; @@ -171,12 +171,12 @@ namespace Ryujinx.Audio.Renderer.Server.Mix if (distance > GetCount()) { - distance = MixState.InvalidDistanceFromFinalMix; + distance = MixInfo.InvalidDistanceFromFinalMix; } } else { - distance = MixState.InvalidDistanceFromFinalMix; + distance = MixInfo.InvalidDistanceFromFinalMix; } mix.DistanceFromFinalMix = distance; @@ -185,13 +185,13 @@ namespace Ryujinx.Audio.Renderer.Server.Mix } /// - /// Update the internal mix buffer offset of all . + /// Update the internal mix buffer offset of all . /// private void UpdateMixBufferOffset() { uint offset = 0; - foreach (ref MixState mix in _mixes.Span) + foreach (ref MixInfo mix in _mixes.Span) { mix.BufferOffset = offset; @@ -210,10 +210,10 @@ namespace Ryujinx.Audio.Renderer.Server.Mix Array.Sort(sortedMixesTemp, (a, b) => { - ref MixState stateA = ref GetState(a); - ref MixState stateB = ref GetState(b); + ref MixInfo infoA = ref GetState(a); + ref MixInfo infoB = ref GetState(b); - return stateB.DistanceFromFinalMix.CompareTo(stateA.DistanceFromFinalMix); + return infoB.DistanceFromFinalMix.CompareTo(infoA.DistanceFromFinalMix); }); sortedMixesTemp.AsSpan().CopyTo(_sortedMixes.Span); diff --git a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs b/src/Ryujinx.Audio/Renderer/Server/Mix/MixInfo.cs similarity index 93% rename from src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs rename to src/Ryujinx.Audio/Renderer/Server/Mix/MixInfo.cs index 5ba58ea5b..84798f982 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Mix/MixInfo.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix /// Server state for a mix. /// [StructLayout(LayoutKind.Sequential, Size = 0x940, Pack = Alignment)] - public struct MixState + public struct MixInfo { public const uint InvalidDistanceFromFinalMix = 0x80000000; @@ -136,11 +136,11 @@ namespace Ryujinx.Audio.Renderer.Server.Mix } /// - /// Create a new + /// Create a new /// /// - /// - public MixState(Memory effectProcessingOrderArray, ref BehaviourContext behaviourContext) : this() + /// + public MixInfo(Memory effectProcessingOrderArray, ref BehaviourInfo behaviourInfo) : this() { MixId = UnusedMixId; @@ -158,7 +158,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix EffectProcessingOrderArrayMaxCount = (uint)effectProcessingOrderArray.Length; - IsLongSizePreDelaySupported = behaviourContext.IsLongSizePreDelaySupported(); + IsLongSizePreDelaySupported = behaviourInfo.IsLongSizePreDelaySupported(); ClearEffectProcessingOrder(); } @@ -257,9 +257,9 @@ namespace Ryujinx.Audio.Renderer.Server.Mix /// The input parameter of the mix. /// The effect context. /// The splitter context. - /// The behaviour context. + /// The behaviour context. /// Return true if the mix was changed. - public bool Update(EdgeMatrix edgeMatrix, in MixParameter parameter, EffectContext effectContext, SplitterContext splitterContext, BehaviourContext behaviourContext) + public bool Update(EdgeMatrix edgeMatrix, in MixParameter parameter, EffectContext effectContext, SplitterContext splitterContext, BehaviourInfo behaviourInfo) { bool isDirty; @@ -271,7 +271,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix NodeId = parameter.NodeId; parameter.MixBufferVolume.CopyTo(MixBufferVolume); - if (behaviourContext.IsSplitterSupported()) + if (behaviourInfo.IsSplitterSupported()) { isDirty = UpdateConnection(edgeMatrix, in parameter, ref splitterContext); } @@ -279,10 +279,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix { isDirty = DestinationMixId != parameter.DestinationMixId; - if (DestinationMixId != parameter.DestinationMixId) - { - DestinationMixId = parameter.DestinationMixId; - } + DestinationMixId = parameter.DestinationMixId; DestinationSplitterId = UnusedSplitterId; } diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs index da5a0ad45..e430d99c3 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs @@ -10,11 +10,11 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// Get the required size for a single performance frame. /// /// The audio renderer configuration. - /// The behaviour context. + /// The behaviour context. /// The required size for a single performance frame. - public static ulong GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref AudioRendererConfiguration parameter, ref BehaviourContext behaviourContext) + public static ulong GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref AudioRendererConfiguration parameter, ref BehaviourInfo behaviourInfo) { - uint version = behaviourContext.GetPerformanceMetricsDataFormat(); + uint version = behaviourInfo.GetPerformanceMetricsDataFormat(); if (version == 2) { @@ -81,11 +81,11 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// /// The backing memory available for use by the manager. /// The audio renderer configuration. - /// The behaviour context; + /// The behaviour context; /// A new . - public static PerformanceManager Create(Memory performanceBuffer, ref AudioRendererConfiguration parameter, BehaviourContext behaviourContext) + public static PerformanceManager Create(Memory performanceBuffer, ref AudioRendererConfiguration parameter, BehaviourInfo behaviourInfo) { - uint version = behaviourContext.GetPerformanceMetricsDataFormat(); + uint version = behaviourInfo.GetPerformanceMetricsDataFormat(); return version switch { diff --git a/src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs b/src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs index 090850018..fa7a24d09 100644 --- a/src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs @@ -1,3 +1,4 @@ +using Ryujinx.Audio.Renderer.Server.Mix; using Ryujinx.Audio.Renderer.Server.Upsampler; using System; @@ -19,7 +20,7 @@ namespace Ryujinx.Audio.Renderer.Server /// /// The target channel count for sink. /// - /// See for usage. + /// See for usage. public uint ChannelCount; /// @@ -28,12 +29,12 @@ namespace Ryujinx.Audio.Renderer.Server public uint MixBufferCount; /// - /// Instance of the used to derive bug fixes and features of the current audio renderer revision. + /// Instance of the used to derive bug fixes and features of the current audio renderer revision. /// - public BehaviourContext BehaviourContext; + public BehaviourInfo BehaviourInfo; /// - /// Instance of the used for upsampling (see ) + /// Instance of the used for upsampling (see ) /// public UpsamplerManager UpsamplerManager; diff --git a/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs index afe2d4b1b..c590b69d9 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Audio.Renderer.Server.Sink /// The upsampler instance used by this sink. /// /// Null if no upsampling is needed. - public UpsamplerState UpsamplerState; + public UpsamplerInfo UpsamplerInfo; /// /// Create a new . @@ -40,9 +40,9 @@ namespace Ryujinx.Audio.Renderer.Server.Sink public override void CleanUp() { - UpsamplerState?.Release(); + UpsamplerInfo?.Release(); - UpsamplerState = null; + UpsamplerInfo = null; base.CleanUp(); } diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs index 6dddb4315..7d914e137 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs @@ -55,22 +55,27 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use. /// public bool IsSplitterPrevVolumeResetSupported { get; private set; } + + /// + /// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use. + /// + public bool IsBiquadFilterParameterFloatSupported { get; private set; } /// /// Initialize . /// - /// The behaviour context. + /// The behaviour context. /// The audio renderer configuration. /// The . /// Memory to store the biquad filtering state for splitters during processing. /// Return true if the initialization was successful. public bool Initialize( - ref BehaviourContext behaviourContext, + ref BehaviourInfo behaviourInfo, ref AudioRendererConfiguration parameter, WorkBufferAllocator workBufferAllocator, Memory splitterBqfStates) { - if (!behaviourContext.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0) + if (!behaviourInfo.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0) { Setup(Memory.Empty, Memory.Empty, Memory.Empty, false); @@ -94,7 +99,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter Memory splitterDestinationsV1 = Memory.Empty; Memory splitterDestinationsV2 = Memory.Empty; - if (!behaviourContext.IsBiquadFilterParameterForSplitterEnabled()) + if (!behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()) { Version = 1; @@ -144,11 +149,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } } - IsSplitterPrevVolumeResetSupported = behaviourContext.IsSplitterPrevVolumeResetSupported(); + IsSplitterPrevVolumeResetSupported = behaviourInfo.IsSplitterPrevVolumeResetSupported(); + IsBiquadFilterParameterFloatSupported = behaviourInfo.IsBiquadFilterParameterFloatSupported(); SplitterState.InitializeSplitters(splitters.Span); - Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed()); + Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourInfo.IsSplitterBugFixed()); return true; } @@ -157,16 +163,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Get the work buffer size while adding the size needed for splitter to operate. /// /// The current size. - /// The behaviour context. + /// The behaviour context. /// The renderer configuration. /// Return the new size taking splitter into account. - public static ulong GetWorkBufferSize(ulong size, ref BehaviourContext behaviourContext, ref AudioRendererConfiguration parameter) + public static ulong GetWorkBufferSize(ulong size, ref BehaviourInfo behaviourInfo, ref AudioRendererConfiguration parameter) { - if (behaviourContext.IsSplitterSupported()) + if (behaviourInfo.IsSplitterSupported()) { size = WorkBufferAllocator.GetTargetSize(size, parameter.SplitterCount, SplitterState.Alignment); - if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled()) + if (behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()) { size = WorkBufferAllocator.GetTargetSize(size, parameter.SplitterDestinationCount, SplitterDestinationVersion2.Alignment); } @@ -175,12 +181,10 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter size = WorkBufferAllocator.GetTargetSize(size, parameter.SplitterDestinationCount, SplitterDestinationVersion1.Alignment); } - if (behaviourContext.IsSplitterBugFixed()) + if (behaviourInfo.IsSplitterBugFixed()) { size = WorkBufferAllocator.GetTargetSize(size, parameter.SplitterDestinationCount, 0x10); } - - return size; } return size; @@ -227,7 +231,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter return 0; } - int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length; + int length; + + if (_splitterDestinationsV2.IsEmpty) + { + length = _splitterDestinationsV1.Length; + } + else + { + length = _splitterDestinationsV2.Length; + } return length / _splitters.Length; } @@ -278,8 +291,17 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter if (parameter.IsMagicValid()) { - int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length; + int length; + if (_splitterDestinationsV2.IsEmpty) + { + length = _splitterDestinationsV1.Length; + } + else + { + length = _splitterDestinationsV2.Length; + } + if (parameter.Id >= 0 && parameter.Id < length) { SplitterDestination destination = GetDestination(parameter.Id); @@ -315,9 +337,19 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } else if (Version == 2) { - if (!UpdateData(ref input)) + if (IsBiquadFilterParameterFloatSupported) { - break; + if (!UpdateData(ref input)) + { + break; + } + } + else + { + if (!UpdateData(ref input)) + { + break; + } } } else @@ -381,10 +413,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV1, id, (uint)_splitterDestinationsV1.Length)); } - else - { - return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV2, id, (uint)_splitterDestinationsV2.Length)); - } + + return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV2, id, (uint)_splitterDestinationsV2.Length)); } /// diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs index 1a46d41fd..1253e5d2c 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs @@ -31,15 +31,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { return 0; } - else - { - return _v1.Id; - } - } - else - { - return _v2.Id; + + return _v1.Id; } + + return _v2.Id; } } @@ -56,15 +52,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { return 0; } - else - { - return _v1.DestinationId; - } - } - else - { - return _v2.DestinationId; + + return _v1.DestinationId; } + + return _v2.DestinationId; } } @@ -82,15 +74,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { return Span.Empty; } - else - { - return _v1.MixBufferVolume; - } - } - else - { - return _v2.MixBufferVolume; + + return _v1.MixBufferVolume; } + + return _v2.MixBufferVolume; } } @@ -108,15 +96,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { return Span.Empty; } - else - { - return _v1.PreviousMixBufferVolume; - } - } - else - { - return _v2.PreviousMixBufferVolume; + + return _v1.PreviousMixBufferVolume; } + + return _v2.PreviousMixBufferVolume; } } @@ -135,15 +119,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { return new SplitterDestination(); } - else - { - return new SplitterDestination(ref _v1.Next); - } - } - else - { - return new SplitterDestination(ref _v2.Next); + + return new SplitterDestination(ref _v1.Next); } + + return new SplitterDestination(ref _v2.Next); } } } @@ -169,6 +149,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter _v2 = ref v2; } + /// /// Creates a new splitter destination wrapper for the splitter destination data. /// @@ -233,7 +214,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// True if the splitter destination is used and has a destination. public readonly bool IsConfigured() { - return Unsafe.IsNullRef(ref _v2) ? _v1.IsConfigured() : _v2.IsConfigured(); + if (Unsafe.IsNullRef(ref _v2)) + { + return _v1.IsConfigured(); + } + + return _v2.IsConfigured(); } /// @@ -243,7 +229,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// The volume for the given destination. public float GetMixVolume(int destinationIndex) { - return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolume(destinationIndex) : _v2.GetMixVolume(destinationIndex); + if (Unsafe.IsNullRef(ref _v2)) + { + return _v1.GetMixVolume(destinationIndex); + } + + return _v2.GetMixVolume(destinationIndex); } /// @@ -253,7 +244,12 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// The volume for the given destination. public float GetMixVolumePrev(int destinationIndex) { - return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolumePrev(destinationIndex) : _v2.GetMixVolumePrev(destinationIndex); + if (Unsafe.IsNullRef(ref _v2)) + { + return _v1.GetMixVolumePrev(destinationIndex); + } + + return _v2.GetMixVolumePrev(destinationIndex); } /// @@ -280,13 +276,13 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter if (Unsafe.IsNullRef(ref _v2)) { Debug.Assert(!Unsafe.IsNullRef(ref next._v1)); - + _v1.Link(ref next._v1); } else { Debug.Assert(!Unsafe.IsNullRef(ref next._v2)); - + _v2.Link(ref next._v2); } } @@ -308,6 +304,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// /// Checks if any biquad filter is enabled. + /// Virtual function at function table + 0x8. /// /// True if any biquad filter is enabled. public bool IsBiquadFilterEnabled() @@ -326,13 +323,14 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// /// Gets the biquad filter parameters. + /// /// Virtual function at function table + 0x10. /// /// Biquad filter index (0 or 1). /// Biquad filter parameters. - public ref BiquadFilterParameter GetBiquadFilterParameter(int index) + public ref BiquadFilterParameter2 GetBiquadFilterParameter(int index) { Debug.Assert(!Unsafe.IsNullRef(ref _v2)); - + return ref _v2.GetBiquadFilterParameter(index); } diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs index f1471a6d2..4db2f7ac1 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs @@ -1,3 +1,4 @@ +using Ryujinx.Audio.Renderer.Dsp; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; @@ -11,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// /// Server state for a splitter destination (version 2). /// - [StructLayout(LayoutKind.Sequential, Size = 0x110, Pack = Alignment)] + [StructLayout(LayoutKind.Sequential, Size = 0x128, Pack = Alignment)] public struct SplitterDestinationVersion2 { public const int Alignment = 0x10; @@ -78,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } } - private Array2 _biquadFilters; + private Array2 _biquadFilters; private Array2 _isPreviousBiquadFilterEnabled; @@ -109,7 +110,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter parameter.MixBufferVolume.CopyTo(MixBufferVolume); - _biquadFilters = parameter.BiquadFilters; + _biquadFilters = parameter.BiquadFilters2; bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed; if (resetPrevVolume) @@ -218,7 +219,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// True if any biquad filter is enabled. public bool IsBiquadFilterEnabled() { - Span biquadFiltersSpan = _biquadFilters.AsSpan(); + Span biquadFiltersSpan = _biquadFilters.AsSpan(); return biquadFiltersSpan[0].Enable || biquadFiltersSpan[1].Enable; } @@ -236,7 +237,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// /// Biquad filter index (0 or 1). /// Biquad filter parameters. - public ref BiquadFilterParameter GetBiquadFilterParameter(int index) + public ref BiquadFilterParameter2 GetBiquadFilterParameter(int index) { return ref _biquadFilters[index]; } diff --git a/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs b/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs index 4921fcf38..463a77040 100644 --- a/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs +++ b/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs @@ -27,39 +27,39 @@ namespace Ryujinx.Audio.Renderer.Server private Memory _output; private readonly uint _processHandle; - private BehaviourContext _behaviourContext; + private BehaviourInfo _behaviourInfo; private readonly ref readonly UpdateDataHeader _inputHeader; private readonly Memory _outputHeader; private readonly ref UpdateDataHeader OutputHeader => ref _outputHeader.Span[0]; - public StateUpdater(ReadOnlySequence input, Memory output, uint processHandle, BehaviourContext behaviourContext) + public StateUpdater(ReadOnlySequence input, Memory output, uint processHandle, BehaviourInfo behaviourInfo) { _inputReader = new SequenceReader(input); _output = output; _outputOrigin = _output; _processHandle = processHandle; - _behaviourContext = behaviourContext; + _behaviourInfo = behaviourInfo; _inputHeader = ref _inputReader.GetRefOrRefToCopy(out _); _outputHeader = SpanMemoryManager.Cast(_output[..Unsafe.SizeOf()]); - OutputHeader.Initialize(_behaviourContext.UserRevision); + OutputHeader.Initialize(_behaviourInfo.UserRevision); _output = _output[Unsafe.SizeOf()..]; } - public ResultCode UpdateBehaviourContext() + public ResultCode UpdateBehaviourInfo() { ref readonly BehaviourParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _); - if (!BehaviourContext.CheckValidRevision(parameter.UserRevision) || parameter.UserRevision != _behaviourContext.UserRevision) + if (!BehaviourInfo.CheckValidRevision(parameter.UserRevision) || parameter.UserRevision != _behaviourInfo.UserRevision) { return ResultCode.InvalidUpdateInfo; } - _behaviourContext.ClearError(); - _behaviourContext.UpdateFlags(parameter.Flags); + _behaviourInfo.ClearError(); + _behaviourInfo.UpdateFlags(parameter.Flags); if (_inputHeader.BehaviourSize != Unsafe.SizeOf()) { @@ -69,16 +69,16 @@ namespace Ryujinx.Audio.Renderer.Server return ResultCode.Success; } - public ResultCode UpdateMemoryPools(Span memoryPools) + public ResultCode UpdateMemoryPools(Span memoryPools) { - PoolMapper mapper = new(_processHandle, _behaviourContext.IsMemoryPoolForceMappingEnabled()); + PoolMapper mapper = new(_processHandle, _behaviourInfo.IsMemoryPoolForceMappingEnabled()); if (memoryPools.Length * Unsafe.SizeOf() != _inputHeader.MemoryPoolsSize) { return ResultCode.InvalidUpdateInfo; } - foreach (ref MemoryPoolState memoryPool in memoryPools) + foreach (ref MemoryPoolInfo memoryPool in memoryPools) { ref readonly MemoryPoolInParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _); @@ -125,10 +125,10 @@ namespace Ryujinx.Audio.Renderer.Server return ResultCode.Success; } - - public ResultCode UpdateVoices(VoiceContext context, PoolMapper mapper) + + public ResultCode UpdateVoices2(VoiceContext context, PoolMapper mapper) { - if (context.GetCount() * Unsafe.SizeOf() != _inputHeader.VoicesSize) + if (context.GetCount() * Unsafe.SizeOf() != _inputHeader.VoicesSize) { return ResultCode.InvalidUpdateInfo; } @@ -140,27 +140,27 @@ namespace Ryujinx.Audio.Renderer.Server // First make everything not in use. for (int i = 0; i < context.GetCount(); i++) { - ref VoiceState state = ref context.GetState(i); + ref VoiceInfo info = ref context.GetState(i); - state.InUse = false; + info.InUse = false; } - Memory[] voiceUpdateStatesArray = ArrayPool>.Shared.Rent(Constants.VoiceChannelCountMax); + Memory[] voiceStatesArray = ArrayPool>.Shared.Rent(Constants.VoiceChannelCountMax); - Span> voiceUpdateStates = voiceUpdateStatesArray.AsSpan(0, Constants.VoiceChannelCountMax); + Span> voiceStates = voiceStatesArray.AsSpan(0, Constants.VoiceChannelCountMax); // Start processing for (int i = 0; i < context.GetCount(); i++) { - ref readonly VoiceInParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _); + ref readonly VoiceInParameter2 parameter = ref _inputReader.GetRefOrRefToCopy(out _); - voiceUpdateStates.Fill(Memory.Empty); + voiceStates.Fill(Memory.Empty); ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef(ref _output)[0]; if (parameter.InUse) { - ref VoiceState currentVoiceState = ref context.GetState(i); + ref VoiceInfo currentVoiceInfo = ref context.GetState(i); Span channelResourceIdsSpan = parameter.ChannelResourceIds.AsSpan(); @@ -170,36 +170,123 @@ namespace Ryujinx.Audio.Renderer.Server Debug.Assert(channelId >= 0 && channelId < context.GetCount()); - voiceUpdateStates[channelResourceIndex] = context.GetUpdateStateForCpu(channelId); + voiceStates[channelResourceIndex] = context.GetUpdateStateForCpu(channelId); } if (parameter.IsNew) { - currentVoiceState.Initialize(); + currentVoiceInfo.Initialize(); } - currentVoiceState.UpdateParameters(out ErrorInfo updateParameterError, in parameter, mapper, ref _behaviourContext); + currentVoiceInfo.UpdateParameters2(out ErrorInfo updateParameterError, in parameter, mapper, ref _behaviourInfo); if (updateParameterError.ErrorCode != ResultCode.Success) { - _behaviourContext.AppendError(ref updateParameterError); + _behaviourInfo.AppendError(ref updateParameterError); } - currentVoiceState.UpdateWaveBuffers(out ErrorInfo[] waveBufferUpdateErrorInfos, in parameter, voiceUpdateStates, mapper, ref _behaviourContext); + currentVoiceInfo.UpdateWaveBuffers2(out ErrorInfo[] waveBufferUpdateErrorInfos, in parameter, voiceStates, mapper, ref _behaviourInfo); foreach (ref ErrorInfo errorInfo in waveBufferUpdateErrorInfos.AsSpan()) { if (errorInfo.ErrorCode != ResultCode.Success) { - _behaviourContext.AppendError(ref errorInfo); + _behaviourInfo.AppendError(ref errorInfo); } } - currentVoiceState.WriteOutStatus(ref outStatus, in parameter, voiceUpdateStates); + currentVoiceInfo.WriteOutStatus2(ref outStatus, in parameter, voiceStates); } } - ArrayPool>.Shared.Return(voiceUpdateStatesArray); + ArrayPool>.Shared.Return(voiceStatesArray); + + int currentOutputSize = _output.Length; + + OutputHeader.VoicesSize = (uint)(Unsafe.SizeOf() * context.GetCount()); + OutputHeader.TotalSize += OutputHeader.VoicesSize; + + Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.VoicesSize); + + _inputReader.SetConsumed(initialInputConsumed + _inputHeader.VoicesSize); + + return ResultCode.Success; + } + + public ResultCode UpdateVoices1(VoiceContext context, PoolMapper mapper) + { + if (context.GetCount() * Unsafe.SizeOf() != _inputHeader.VoicesSize) + { + return ResultCode.InvalidUpdateInfo; + } + + int initialOutputSize = _output.Length; + + long initialInputConsumed = _inputReader.Consumed; + + // First make everything not in use. + for (int i = 0; i < context.GetCount(); i++) + { + ref VoiceInfo info = ref context.GetState(i); + + info.InUse = false; + } + + Memory[] voiceStatesArray = ArrayPool>.Shared.Rent(Constants.VoiceChannelCountMax); + + Span> voiceStates = voiceStatesArray.AsSpan(0, Constants.VoiceChannelCountMax); + + // Start processing + for (int i = 0; i < context.GetCount(); i++) + { + ref readonly VoiceInParameter1 parameter = ref _inputReader.GetRefOrRefToCopy(out _); + + voiceStates.Fill(Memory.Empty); + + ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef(ref _output)[0]; + + if (parameter.InUse) + { + ref VoiceInfo currentVoiceInfo = ref context.GetState(i); + + Span channelResourceIdsSpan = parameter.ChannelResourceIds.AsSpan(); + + for (int channelResourceIndex = 0; channelResourceIndex < parameter.ChannelCount; channelResourceIndex++) + { + int channelId = channelResourceIdsSpan[channelResourceIndex]; + + Debug.Assert(channelId >= 0 && channelId < context.GetCount()); + + voiceStates[channelResourceIndex] = context.GetUpdateStateForCpu(channelId); + } + + if (parameter.IsNew) + { + currentVoiceInfo.Initialize(); + } + + currentVoiceInfo.UpdateParameters1(out ErrorInfo updateParameterError, in parameter, mapper, ref _behaviourInfo); + + if (updateParameterError.ErrorCode != ResultCode.Success) + { + _behaviourInfo.AppendError(ref updateParameterError); + } + + currentVoiceInfo.UpdateWaveBuffers1(out ErrorInfo[] waveBufferUpdateErrorInfos, in parameter, voiceStates, mapper, ref _behaviourInfo); + + foreach (ref ErrorInfo errorInfo in waveBufferUpdateErrorInfos.AsSpan()) + { + if (errorInfo.ErrorCode != ResultCode.Success) + { + _behaviourInfo.AppendError(ref errorInfo); + } + } + + currentVoiceInfo.WriteOutStatus1(ref outStatus, in parameter, voiceStates); + } + } + + ArrayPool>.Shared.Return(voiceStatesArray); int currentOutputSize = _output.Length; @@ -235,7 +322,12 @@ namespace Ryujinx.Audio.Renderer.Server public ResultCode UpdateEffects(EffectContext context, bool isAudioRendererActive, PoolMapper mapper) { - if (_behaviourContext.IsEffectInfoVersion2Supported()) + if (_behaviourInfo.IsBiquadFilterParameterFloatSupported()) + { + return UpdateEffectsVersion3(context, isAudioRendererActive, mapper); + } + + if (_behaviourInfo.IsEffectInfoVersion2Supported()) { return UpdateEffectsVersion2(context, isAudioRendererActive, mapper); } @@ -243,6 +335,60 @@ namespace Ryujinx.Audio.Renderer.Server return UpdateEffectsVersion1(context, isAudioRendererActive, mapper); } + public ResultCode UpdateEffectsVersion3(EffectContext context, bool isAudioRendererActive, PoolMapper mapper) + { + if (context.GetCount() * Unsafe.SizeOf() != _inputHeader.EffectsSize) + { + return ResultCode.InvalidUpdateInfo; + } + + int initialOutputSize = _output.Length; + + long initialInputConsumed = _inputReader.Consumed; + + for (int i = 0; i < context.GetCount(); i++) + { + ref readonly EffectInParameterVersion3 parameter = ref _inputReader.GetRefOrRefToCopy(out _); + + ref EffectOutStatusVersion2 outStatus = ref SpanIOHelper.GetWriteRef(ref _output)[0]; + + ref BaseEffect effect = ref context.GetEffect(i); + + if (!effect.IsTypeValid(in parameter)) + { + ResetEffect(ref effect, in parameter, mapper); + } + + effect.Update(out ErrorInfo updateErrorInfo, in parameter, mapper); + + if (updateErrorInfo.ErrorCode != ResultCode.Success) + { + _behaviourInfo.AppendError(ref updateErrorInfo); + } + + effect.StoreStatus(ref outStatus, isAudioRendererActive); + + if (parameter.IsNew) + { + effect.InitializeResultState(ref context.GetDspState(i)); + effect.InitializeResultState(ref context.GetState(i)); + } + + effect.UpdateResultState(ref outStatus.ResultState, ref context.GetState(i)); + } + + int currentOutputSize = _output.Length; + + OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf() * context.GetCount()); + OutputHeader.TotalSize += OutputHeader.EffectsSize; + + Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize); + + _inputReader.SetConsumed(initialInputConsumed + _inputHeader.EffectsSize); + + return ResultCode.Success; + } + public ResultCode UpdateEffectsVersion2(EffectContext context, bool isAudioRendererActive, PoolMapper mapper) { if (context.GetCount() * Unsafe.SizeOf() != _inputHeader.EffectsSize) @@ -271,7 +417,7 @@ namespace Ryujinx.Audio.Renderer.Server if (updateErrorInfo.ErrorCode != ResultCode.Success) { - _behaviourContext.AppendError(ref updateErrorInfo); + _behaviourInfo.AppendError(ref updateErrorInfo); } effect.StoreStatus(ref outStatus, isAudioRendererActive); @@ -325,7 +471,7 @@ namespace Ryujinx.Audio.Renderer.Server if (updateErrorInfo.ErrorCode != ResultCode.Success) { - _behaviourContext.AppendError(ref updateErrorInfo); + _behaviourInfo.AppendError(ref updateErrorInfo); } effect.StoreStatus(ref outStatus, isAudioRendererActive); @@ -384,7 +530,7 @@ namespace Ryujinx.Audio.Renderer.Server uint inputMixSize; uint inputSize = 0; - if (_behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()) + if (_behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()) { ref readonly MixInParameterDirtyOnlyUpdate parameter = ref _inputReader.GetRefOrRefToCopy(out _); @@ -423,12 +569,12 @@ namespace Ryujinx.Audio.Renderer.Server int mixId = i; - if (_behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()) + if (_behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()) { mixId = parameter.MixId; } - ref MixState mix = ref mixContext.GetState(mixId); + ref MixInfo mix = ref mixContext.GetState(mixId); if (parameter.IsUsed != mix.IsUsed) { @@ -444,13 +590,13 @@ namespace Ryujinx.Audio.Renderer.Server if (mix.IsUsed) { - isMixContextDirty |= mix.Update(mixContext.EdgeMatrix, in parameter, effectContext, splitterContext, _behaviourContext); + isMixContextDirty |= mix.Update(mixContext.EdgeMatrix, in parameter, effectContext, splitterContext, _behaviourInfo); } } if (isMixContextDirty) { - if (_behaviourContext.IsSplitterSupported() && splitterContext.UsingSplitter()) + if (_behaviourInfo.IsSplitterSupported() && splitterContext.UsingSplitter()) { if (!mixContext.Sort(splitterContext)) { @@ -507,7 +653,7 @@ namespace Ryujinx.Audio.Renderer.Server if (updateErrorInfo.ErrorCode != ResultCode.Success) { - _behaviourContext.AppendError(ref updateErrorInfo); + _behaviourInfo.AppendError(ref updateErrorInfo); } } @@ -555,7 +701,7 @@ namespace Ryujinx.Audio.Renderer.Server { ref BehaviourErrorInfoOutStatus outStatus = ref SpanIOHelper.GetWriteRef(ref _output)[0]; - _behaviourContext.CopyErrorInfo(outStatus.ErrorInfos.AsSpan(), out outStatus.ErrorInfosCount); + _behaviourInfo.CopyErrorInfo(outStatus.ErrorInfos.AsSpan(), out outStatus.ErrorInfosCount); OutputHeader.BehaviourSize = (uint)Unsafe.SizeOf(); OutputHeader.TotalSize += OutputHeader.BehaviourSize; diff --git a/src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs b/src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs index 46aae05ab..4b72274ce 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs @@ -1,7 +1,9 @@ +using Ryujinx.Audio.Renderer.Server.Voice; + namespace Ryujinx.Audio.Renderer.Server.Types { /// - /// The internal play state of a + /// The internal play state of a /// public enum PlayState { @@ -24,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server.Types /// /// /// This is changed to the state after command generation. - /// + /// /// Stopping, diff --git a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerInfo.cs similarity index 82% rename from src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs rename to src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerInfo.cs index 39a58c91a..d84d8fa70 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerInfo.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler /// /// Server state for a upsampling. /// - public class UpsamplerState + public class UpsamplerInfo { /// /// The output buffer containing the target samples. @@ -18,7 +18,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler public uint SampleCount { get; } /// - /// The index of the . (used to free it) + /// The index of the . (used to free it) /// private readonly int _index; @@ -43,13 +43,13 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler public UpsamplerBufferState[] BufferStates; /// - /// Create a new . + /// Create a new . /// /// The upsampler manager. - /// The index of the . (used to free it) + /// The index of the . (used to free it) /// The output buffer used to contain the target samples. /// The target sample count. - public UpsamplerState(UpsamplerManager manager, int index, Memory outputBuffer, uint sampleCount) + public UpsamplerInfo(UpsamplerManager manager, int index, Memory outputBuffer, uint sampleCount) { _manager = manager; _index = index; @@ -58,7 +58,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler } /// - /// Release the . + /// Release the . /// public void Release() { diff --git a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs index 8b3f39439..942345f8d 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler /// /// The upsamplers instances. /// - private readonly UpsamplerState[] _upsamplers; + private readonly UpsamplerInfo[] _upsamplers; /// /// The count of upsamplers. @@ -39,14 +39,14 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler _upSamplerWorkBuffer = upSamplerWorkBuffer; _count = count; - _upsamplers = new UpsamplerState[_count]; + _upsamplers = new UpsamplerInfo[_count]; } /// - /// Allocate a new . + /// Allocate a new . /// - /// A new or null if out of memory. - public UpsamplerState Allocate() + /// A new or null if out of memory. + public UpsamplerInfo Allocate() { int workBufferOffset = 0; @@ -56,7 +56,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler { if (_upsamplers[i] == null) { - _upsamplers[i] = new UpsamplerState(this, i, _upSamplerWorkBuffer.Slice(workBufferOffset, Constants.UpSampleEntrySize), Constants.TargetSampleCount); + _upsamplers[i] = new UpsamplerInfo(this, i, _upSamplerWorkBuffer.Slice(workBufferOffset, Constants.UpSampleEntrySize), Constants.TargetSampleCount); return _upsamplers[i]; } @@ -69,9 +69,9 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler } /// - /// Free a at the given index. + /// Free a at the given index. /// - /// The index of the to free. + /// The index of the to free. public void Free(int index) { lock (_lock) diff --git a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs index 827d24fe5..5cb334ddd 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs @@ -11,14 +11,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice public class VoiceContext { /// - /// Storage of the sorted indices to . + /// Storage of the sorted indices to . /// private Memory _sortedVoices; /// - /// Storage for . + /// Storage for . /// - private Memory _voices; + private Memory _voices; /// /// Storage for . @@ -26,27 +26,27 @@ namespace Ryujinx.Audio.Renderer.Server.Voice private Memory _voiceChannelResources; /// - /// Storage for that are used during audio renderer server updates. + /// Storage for that are used during audio renderer server updates. /// - private Memory _voiceUpdateStatesCpu; + private Memory _voiceStatesCpu; /// - /// Storage for for the . + /// Storage for for the . /// - private Memory _voiceUpdateStatesDsp; + private Memory _voiceStatesDsp; /// /// The total voice count. /// private uint _voiceCount; - public void Initialize(Memory sortedVoices, Memory voices, Memory voiceChannelResources, Memory voiceUpdateStatesCpu, Memory voiceUpdateStatesDsp, uint voiceCount) + public void Initialize(Memory sortedVoices, Memory voices, Memory voiceChannelResources, Memory voiceStatesCpu, Memory voiceStatesDsp, uint voiceCount) { _sortedVoices = sortedVoices; _voices = voices; _voiceChannelResources = voiceChannelResources; - _voiceUpdateStatesCpu = voiceUpdateStatesCpu; - _voiceUpdateStatesDsp = voiceUpdateStatesDsp; + _voiceStatesCpu = voiceStatesCpu; + _voiceStatesDsp = voiceStatesDsp; _voiceCount = voiceCount; } @@ -70,38 +70,38 @@ namespace Ryujinx.Audio.Renderer.Server.Voice } /// - /// Get a at the given . + /// Get a at the given . /// /// The index to use. - /// A at the given . - /// The returned should only be used when updating the server state. - public Memory GetUpdateStateForCpu(int id) + /// A at the given . + /// The returned should only be used when updating the server state. + public Memory GetUpdateStateForCpu(int id) { - return SpanIOHelper.GetMemory(_voiceUpdateStatesCpu, id, _voiceCount); + return SpanIOHelper.GetMemory(_voiceStatesCpu, id, _voiceCount); } /// - /// Get a at the given . + /// Get a at the given . /// /// The index to use. - /// A at the given . - /// The returned should only be used in the context of processing on the . - public Memory GetUpdateStateForDsp(int id) + /// A at the given . + /// The returned should only be used in the context of processing on the . + public Memory GetUpdateStateForDsp(int id) { - return SpanIOHelper.GetMemory(_voiceUpdateStatesDsp, id, _voiceCount); + return SpanIOHelper.GetMemory(_voiceStatesDsp, id, _voiceCount); } /// - /// Get a reference to a at the given . + /// Get a reference to a at the given . /// /// The index to use. - /// A reference to a at the given . - public ref VoiceState GetState(int id) + /// A reference to a at the given . + public ref VoiceInfo GetState(int id) { return ref SpanIOHelper.GetFromMemory(_voices, id, _voiceCount); } - public ref VoiceState GetSortedState(int id) + public ref VoiceInfo GetSortedState(int id) { Debug.Assert(id >= 0 && id < _voiceCount); @@ -113,7 +113,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// public void UpdateForCommandGeneration() { - _voiceUpdateStatesDsp.CopyTo(_voiceUpdateStatesCpu); + _voiceStatesDsp.CopyTo(_voiceStatesCpu); } /// @@ -130,14 +130,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice sortedVoicesTemp.Sort((a, b) => { - ref VoiceState aState = ref GetState(a); - ref VoiceState bState = ref GetState(b); + ref VoiceInfo aInfo = ref GetState(a); + ref VoiceInfo bInfo = ref GetState(b); - int result = aState.Priority.CompareTo(bState.Priority); + int result = aInfo.Priority.CompareTo(bInfo.Priority); if (result == 0) { - return aState.SortingOrder.CompareTo(bState.SortingOrder); + return aInfo.SortingOrder.CompareTo(bInfo.SortingOrder); } return result; diff --git a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceInfo.cs similarity index 66% rename from src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs rename to src/Ryujinx.Audio/Renderer/Server/Voice/VoiceInfo.cs index 9fcff199d..ba6158b71 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceInfo.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Dsp; using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Server.MemoryPool; @@ -9,13 +10,13 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; using static Ryujinx.Audio.Renderer.Common.BehaviourParameter; -using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter1; using PlayState = Ryujinx.Audio.Renderer.Server.Types.PlayState; namespace Ryujinx.Audio.Renderer.Server.Voice { [StructLayout(LayoutKind.Sequential, Pack = Alignment)] - public struct VoiceState + public struct VoiceInfo { public const int Alignment = 0x10; @@ -102,7 +103,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// /// Biquad filters to apply to the output of the voice. /// - public Array2 BiquadFilters; + public Array2 BiquadFilters; /// /// Total count of of the voice. @@ -185,7 +186,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice public Span BiquadFilterNeedInitialization => SpanHelpers.AsSpan(ref _biquadFilterNeedInitialization); /// - /// Initialize the . + /// Initialize the . /// public void Initialize() { @@ -215,7 +216,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice } /// - /// Initialize the in this . + /// Initialize the in this . /// private void InitializeWaveBuffers() { @@ -250,13 +251,13 @@ namespace Ryujinx.Audio.Renderer.Server.Voice { return MixId != Constants.UnusedMixId || SplitterId != Constants.UnusedSplitterId; } - + /// /// Indicate if the server voice information needs to be updated. /// /// The user parameter. /// Return true, if the server voice information needs to be updated. - private readonly bool ShouldUpdateParameters(in VoiceInParameter parameter) + private readonly bool ShouldUpdateParameters2(in VoiceInParameter2 parameter) { if (DataSourceStateAddressInfo.CpuAddress == parameter.DataSourceStateAddress) { @@ -268,14 +269,31 @@ namespace Ryujinx.Audio.Renderer.Server.Voice DataSourceStateUnmapped; } + /// + /// Indicate if the server voice information needs to be updated. + /// + /// The user parameter. + /// Return true, if the server voice information needs to be updated. + private readonly bool ShouldUpdateParameters1(in VoiceInParameter1 parameter) + { + if (DataSourceStateAddressInfo.CpuAddress == parameter.DataSourceStateAddress) + { + return DataSourceStateAddressInfo.Size != parameter.DataSourceStateSize; + } + + return DataSourceStateAddressInfo.CpuAddress != parameter.DataSourceStateAddress || + DataSourceStateAddressInfo.Size != parameter.DataSourceStateSize || + DataSourceStateUnmapped; + } + /// /// Update the internal state from a user parameter. /// /// The possible that was generated. /// The user parameter. /// The mapper to use. - /// The behaviour context. - public void UpdateParameters(out ErrorInfo outErrorInfo, in VoiceInParameter parameter, PoolMapper poolMapper, ref BehaviourContext behaviourContext) + /// The behaviour context. + public void UpdateParameters2(out ErrorInfo outErrorInfo, in VoiceInParameter2 parameter, PoolMapper poolMapper, ref BehaviourInfo behaviourInfo) { InUse = parameter.InUse; Id = parameter.Id; @@ -296,14 +314,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice WaveBuffersCount = parameter.WaveBuffersCount; WaveBuffersIndex = parameter.WaveBuffersIndex; - if (behaviourContext.IsFlushVoiceWaveBuffersSupported()) + if (behaviourInfo.IsFlushVoiceWaveBuffersSupported()) { FlushWaveBufferCount += parameter.FlushWaveBufferCount; } MixId = parameter.MixId; - if (behaviourContext.IsSplitterSupported()) + if (behaviourInfo.IsSplitterSupported()) { SplitterId = parameter.SplitterId; } @@ -316,7 +334,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice DecodingBehaviour behaviour = DecodingBehaviour.Default; - if (behaviourContext.IsDecodingBehaviourFlagSupported()) + if (behaviourInfo.IsDecodingBehaviourFlagSupported()) { behaviour = parameter.DecodingBehaviourFlags; } @@ -328,7 +346,78 @@ namespace Ryujinx.Audio.Renderer.Server.Voice VoiceDropFlag = false; } - if (ShouldUpdateParameters(in parameter)) + if (ShouldUpdateParameters2(in parameter)) + { + DataSourceStateUnmapped = !poolMapper.TryAttachBuffer(out outErrorInfo, ref DataSourceStateAddressInfo, parameter.DataSourceStateAddress, parameter.DataSourceStateSize); + } + else + { + outErrorInfo = new ErrorInfo(); + } + } + + /// + /// Update the internal state from a user parameter. + /// + /// The possible that was generated. + /// The user paramter2. + /// The mapper to use. + /// The behaviour context. + public void UpdateParameters1(out ErrorInfo outErrorInfo, in VoiceInParameter1 parameter, PoolMapper poolMapper, ref BehaviourInfo behaviourInfo) + { + InUse = parameter.InUse; + Id = parameter.Id; + NodeId = parameter.NodeId; + + UpdatePlayState(parameter.PlayState); + + SrcQuality = parameter.SrcQuality; + + Priority = parameter.Priority; + SortingOrder = parameter.SortingOrder; + SampleRate = parameter.SampleRate; + SampleFormat = parameter.SampleFormat; + ChannelsCount = parameter.ChannelCount; + Pitch = parameter.Pitch; + Volume = parameter.Volume; + BiquadFilters[0] = BiquadFilterHelper.ToBiquadFilterParameter2(parameter.BiquadFilters[0]); + BiquadFilters[1] = BiquadFilterHelper.ToBiquadFilterParameter2(parameter.BiquadFilters[1]); + WaveBuffersCount = parameter.WaveBuffersCount; + WaveBuffersIndex = parameter.WaveBuffersIndex; + + if (behaviourInfo.IsFlushVoiceWaveBuffersSupported()) + { + FlushWaveBufferCount += parameter.FlushWaveBufferCount; + } + + MixId = parameter.MixId; + + if (behaviourInfo.IsSplitterSupported()) + { + SplitterId = parameter.SplitterId; + } + else + { + SplitterId = Constants.UnusedSplitterId; + } + + parameter.ChannelResourceIds.AsSpan().CopyTo(ChannelResourceIds.AsSpan()); + + DecodingBehaviour behaviour = DecodingBehaviour.Default; + + if (behaviourInfo.IsDecodingBehaviourFlagSupported()) + { + behaviour = parameter.DecodingBehaviourFlags; + } + + DecodingBehaviour = behaviour; + + if (parameter.ResetVoiceDropFlag) + { + VoiceDropFlag = false; + } + + if (ShouldUpdateParameters1(in parameter)) { DataSourceStateUnmapped = !poolMapper.TryAttachBuffer(out outErrorInfo, ref DataSourceStateAddressInfo, parameter.DataSourceStateAddress, parameter.DataSourceStateSize); } @@ -375,14 +464,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice PlayState = newServerPlayState; } - + /// /// Write the status of the voice to the given user output. /// /// The given user output. /// The user parameter. - /// The voice states associated to the . - public void WriteOutStatus(ref VoiceOutStatus outStatus, in VoiceInParameter parameter, ReadOnlySpan> voiceUpdateStates) + /// The voice states associated to the . + public void WriteOutStatus2(ref VoiceOutStatus outStatus, in VoiceInParameter2 parameter, ReadOnlySpan> voiceStates) { #if DEBUG // Sanity check in debug mode of the internal state @@ -390,8 +479,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice { for (int i = 1; i < ChannelsCount; i++) { - ref VoiceUpdateState stateA = ref voiceUpdateStates[i - 1].Span[0]; - ref VoiceUpdateState stateB = ref voiceUpdateStates[i].Span[0]; + ref VoiceState stateA = ref voiceStates[i - 1].Span[0]; + ref VoiceState stateB = ref voiceStates[i].Span[0]; Debug.Assert(stateA.WaveBufferConsumed == stateB.WaveBufferConsumed); Debug.Assert(stateA.PlayedSampleCount == stateB.PlayedSampleCount); @@ -412,7 +501,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice } else { - ref VoiceUpdateState state = ref voiceUpdateStates[0].Span[0]; + ref VoiceState state = ref voiceStates[0].Span[0]; outStatus.VoiceDropFlag = VoiceDropFlag; outStatus.PlayedWaveBuffersCount = state.WaveBufferConsumed; @@ -421,19 +510,63 @@ namespace Ryujinx.Audio.Renderer.Server.Voice } /// - /// Update the internal state of all the of the . + /// Write the status of the voice to the given user output. + /// + /// The given user output. + /// The user parameter. + /// The voice states associated to the . + public void WriteOutStatus1(ref VoiceOutStatus outStatus, in VoiceInParameter1 parameter, ReadOnlySpan> voiceStates) + { +#if DEBUG + // Sanity check in debug mode of the internal state + if (!parameter.IsNew && !IsNew) + { + for (int i = 1; i < ChannelsCount; i++) + { + ref VoiceState stateA = ref voiceStates[i - 1].Span[0]; + ref VoiceState stateB = ref voiceStates[i].Span[0]; + + Debug.Assert(stateA.WaveBufferConsumed == stateB.WaveBufferConsumed); + Debug.Assert(stateA.PlayedSampleCount == stateB.PlayedSampleCount); + Debug.Assert(stateA.Offset == stateB.Offset); + Debug.Assert(stateA.WaveBufferIndex == stateB.WaveBufferIndex); + Debug.Assert(stateA.Fraction == stateB.Fraction); + Debug.Assert(stateA.IsWaveBufferValid.SequenceEqual(stateB.IsWaveBufferValid)); + } + } +#endif + if (parameter.IsNew || IsNew) + { + IsNew = true; + + outStatus.VoiceDropFlag = false; + outStatus.PlayedWaveBuffersCount = 0; + outStatus.PlayedSampleCount = 0; + } + else + { + ref VoiceState state = ref voiceStates[0].Span[0]; + + outStatus.VoiceDropFlag = VoiceDropFlag; + outStatus.PlayedWaveBuffersCount = state.WaveBufferConsumed; + outStatus.PlayedSampleCount = state.PlayedSampleCount; + } + } + + /// + /// Update the internal state of all the of the . /// /// An array of used to report errors when mapping any of the . /// The user parameter. - /// The voice states associated to the . + /// The voice states associated to the . /// The mapper to use. - /// The behaviour context. - public void UpdateWaveBuffers( + /// The behaviour context. + public void UpdateWaveBuffers2( out ErrorInfo[] errorInfos, - in VoiceInParameter parameter, - ReadOnlySpan> voiceUpdateStates, + in VoiceInParameter2 parameter, + ReadOnlySpan> voiceStates, PoolMapper mapper, - ref BehaviourContext behaviourContext) + ref BehaviourInfo behaviourInfo) { errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2]; @@ -443,23 +576,61 @@ namespace Ryujinx.Audio.Renderer.Server.Voice for (int i = 0; i < parameter.ChannelCount; i++) { - voiceUpdateStates[i].Span[0].IsWaveBufferValid.Clear(); + voiceStates[i].Span[0].IsWaveBufferValid.Clear(); } } - ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0]; + ref VoiceState voiceState = ref voiceStates[0].Span[0]; Span waveBuffersSpan = WaveBuffers.AsSpan(); Span pWaveBuffersSpan = parameter.WaveBuffers.AsSpan(); for (int i = 0; i < Constants.VoiceWaveBufferCount; i++) { - UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], mapper, ref behaviourContext); + UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo); } } /// - /// Update the internal state of one of the of the . + /// Update the internal state of all the of the . + /// + /// An array of used to report errors when mapping any of the . + /// The user parameter. + /// The voice states associated to the . + /// The mapper to use. + /// The behaviour context. + public void UpdateWaveBuffers1( + out ErrorInfo[] errorInfos, + in VoiceInParameter1 parameter, + ReadOnlySpan> voiceStates, + PoolMapper mapper, + ref BehaviourInfo behaviourInfo) + { + errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2]; + + if (parameter.IsNew) + { + InitializeWaveBuffers(); + + for (int i = 0; i < parameter.ChannelCount; i++) + { + voiceStates[i].Span[0].IsWaveBufferValid.Clear(); + } + } + + ref VoiceState voiceState = ref voiceStates[0].Span[0]; + + Span waveBuffersSpan = WaveBuffers.AsSpan(); + Span pWaveBuffersSpan = parameter.WaveBuffers.AsSpan(); + + for (int i = 0; i < Constants.VoiceWaveBufferCount; i++) + { + UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo); + } + } + + /// + /// Update the internal state of one of the of the . /// /// A used to report errors when mapping the . /// The to update. @@ -467,7 +638,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// The from the user input. /// If set to true, the server side wavebuffer is considered valid. /// The mapper to use. - /// The behaviour context. + /// The behaviour context. private void UpdateWaveBuffer( Span errorInfos, ref WaveBuffer waveBuffer, @@ -475,7 +646,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice SampleFormat sampleFormat, bool isValid, PoolMapper mapper, - ref BehaviourContext behaviourContext) + ref BehaviourInfo behaviourInfo) { if (!isValid && waveBuffer.IsSendToAudioProcessor && waveBuffer.BufferAddressInfo.CpuAddress != 0) { @@ -502,7 +673,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice errorInfos[0] = bufferInfoError; - if (sampleFormat == SampleFormat.Adpcm && behaviourContext.IsAdpcmLoopContextBugFixed() && inputWaveBuffer.ContextAddress != 0) + if (sampleFormat == SampleFormat.Adpcm && behaviourInfo.IsAdpcmLoopContextBugFixed() && inputWaveBuffer.ContextAddress != 0) { bool adpcmLoopContextMapped = mapper.TryAttachBuffer(out ErrorInfo adpcmLoopContextInfoError, ref waveBuffer.ContextAddressInfo, @@ -511,13 +682,13 @@ namespace Ryujinx.Audio.Renderer.Server.Voice errorInfos[1] = adpcmLoopContextInfoError; - if (adpcmLoopContextMapped) + if (!adpcmLoopContextMapped || BufferInfoUnmapped) { - BufferInfoUnmapped = DataSourceStateUnmapped; + BufferInfoUnmapped = true; } else { - BufferInfoUnmapped = true; + BufferInfoUnmapped = false; } } else @@ -534,7 +705,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice } /// - /// Reset the resources associated to this . + /// Reset the resources associated to this . /// /// The voice context. private void ResetResources(VoiceContext context) @@ -549,9 +720,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice Debug.Assert(voiceChannelResource.IsUsed); - Memory dspSharedState = context.GetUpdateStateForDsp(channelResourceId); + Memory dspSharedState = context.GetUpdateStateForDsp(channelResourceId); - MemoryMarshal.Cast(dspSharedState.Span).Clear(); + MemoryMarshal.Cast(dspSharedState.Span).Clear(); voiceChannelResource.UpdateState(); } @@ -561,9 +732,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// Flush a certain amount of . /// /// The amount of wavebuffer to flush. - /// The voice states associated to the . + /// The voice states associated to the . /// The channel count from user input. - private void FlushWaveBuffers(uint waveBufferCount, Memory[] voiceUpdateStates, uint channelCount) + private void FlushWaveBuffers(uint waveBufferCount, Memory[] voiceStates, uint channelCount) { uint waveBufferIndex = WaveBuffersIndex; @@ -575,12 +746,17 @@ namespace Ryujinx.Audio.Renderer.Server.Voice for (int j = 0; j < channelCount; j++) { - ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[j].Span[0]; - - voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount; - voiceUpdateState.WaveBufferConsumed++; - voiceUpdateState.IsWaveBufferValid[(int)waveBufferIndex] = false; + ref VoiceState voiceState = ref voiceStates[j].Span[0]; + + if (!waveBuffersSpan[(int)waveBufferIndex].IsSendToAudioProcessor || voiceState.IsWaveBufferValid[(int)waveBufferIndex]) + { + voiceState.WaveBufferIndex = (voiceState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount; + voiceState.WaveBufferConsumed++; + voiceState.IsWaveBufferValid[(int)waveBufferIndex] = false; + } } + + waveBuffersSpan[(int)waveBufferIndex].IsSendToAudioProcessor = true; waveBufferIndex = (waveBufferIndex + 1) % Constants.VoiceWaveBufferCount; } @@ -589,13 +765,13 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// /// Update the internal parameters for command generation. /// - /// The voice states associated to the . + /// The voice states associated to the . /// Return true if this voice should be played. - public bool UpdateParametersForCommandGeneration(Memory[] voiceUpdateStates) + public bool UpdateParametersForCommandGeneration(Memory[] voiceStates) { if (FlushWaveBufferCount != 0) { - FlushWaveBuffers(FlushWaveBufferCount, voiceUpdateStates, ChannelsCount); + FlushWaveBuffers(FlushWaveBufferCount, voiceStates, ChannelsCount); FlushWaveBufferCount = 0; } @@ -615,9 +791,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice { for (int y = 0; y < ChannelsCount; y++) { - Debug.Assert(!voiceUpdateStates[y].Span[0].IsWaveBufferValid[i]); + Debug.Assert(!voiceStates[y].Span[0].IsWaveBufferValid[i]); - voiceUpdateStates[y].Span[0].IsWaveBufferValid[i] = true; + voiceStates[y].Span[0].IsWaveBufferValid[i] = true; } waveBuffer.IsSendToAudioProcessor = true; @@ -626,11 +802,11 @@ namespace Ryujinx.Audio.Renderer.Server.Voice WasPlaying = false; - ref VoiceUpdateState primaryVoiceUpdateState = ref voiceUpdateStates[0].Span[0]; + ref VoiceState primaryVoiceState = ref voiceStates[0].Span[0]; - for (int i = 0; i < primaryVoiceUpdateState.IsWaveBufferValid.Length; i++) + for (int i = 0; i < primaryVoiceState.IsWaveBufferValid.Length; i++) { - if (primaryVoiceUpdateState.IsWaveBufferValid[i]) + if (primaryVoiceState.IsWaveBufferValid[i]) { return true; } @@ -649,27 +825,27 @@ namespace Ryujinx.Audio.Renderer.Server.Voice for (int j = 0; j < ChannelsCount; j++) { - ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[j].Span[0]; + ref VoiceState voiceState = ref voiceStates[j].Span[0]; - if (voiceUpdateState.IsWaveBufferValid[i]) + if (voiceState.IsWaveBufferValid[i]) { - voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount; - voiceUpdateState.WaveBufferConsumed++; + voiceState.WaveBufferIndex = (voiceState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount; + voiceState.WaveBufferConsumed++; } - voiceUpdateState.IsWaveBufferValid[i] = false; + voiceState.IsWaveBufferValid[i] = false; } } for (int i = 0; i < ChannelsCount; i++) { - ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[i].Span[0]; + ref VoiceState voiceState = ref voiceStates[i].Span[0]; - voiceUpdateState.Offset = 0; - voiceUpdateState.PlayedSampleCount = 0; - voiceUpdateState.Pitch.AsSpan().Clear(); - voiceUpdateState.Fraction = 0; - voiceUpdateState.LoopContext = new AdpcmLoopContext(); + voiceState.Offset = 0; + voiceState.PlayedSampleCount = 0; + voiceState.Pitch.AsSpan().Clear(); + voiceState.Fraction = 0; + voiceState.LoopContext = new AdpcmLoopContext(); } PlayState = PlayState.Stopped; @@ -715,16 +891,16 @@ namespace Ryujinx.Audio.Renderer.Server.Voice IsNew = false; } - Memory[] voiceUpdateStates = new Memory[Constants.VoiceChannelCountMax]; + Memory[] voiceStates = new Memory[Constants.VoiceChannelCountMax]; Span channelResourceIdsSpan = ChannelResourceIds.AsSpan(); for (int i = 0; i < ChannelsCount; i++) { - voiceUpdateStates[i] = context.GetUpdateStateForDsp(channelResourceIdsSpan[i]); + voiceStates[i] = context.GetUpdateStateForDsp(channelResourceIdsSpan[i]); } - return UpdateParametersForCommandGeneration(voiceUpdateStates); + return UpdateParametersForCommandGeneration(voiceStates); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs index 312075bc4..5a6aab495 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common ulong value = BinaryPrimitives.ReadUInt64LittleEndian(byteSpan); - return value; + return value << 1; } private static int GetSamplingNumberFieldOffset(ref T sampledDataStruct) where T : unmanaged, ISampledDataStruct diff --git a/src/Ryujinx.Horizon/Audio/AudioUserIpcServer.cs b/src/Ryujinx.Horizon/Audio/AudioUserIpcServer.cs index 20c824e1e..1de123f7f 100644 --- a/src/Ryujinx.Horizon/Audio/AudioUserIpcServer.cs +++ b/src/Ryujinx.Horizon/Audio/AudioUserIpcServer.cs @@ -33,11 +33,13 @@ namespace Ryujinx.Horizon.Audio AudioOutManager audioOutManager = new(_managers.AudioOutputManager); AudioInManager audioInManager = new(_managers.AudioInputManager); FinalOutputRecorderManager finalOutputRecorderManager = new(); + AudioSnoopManager audioSnoopManager = new(); _serverManager.RegisterObjectForServer(audioRendererManager, ServiceName.Encode("audren:u"), MaxSessionsCount); _serverManager.RegisterObjectForServer(audioOutManager, ServiceName.Encode("audout:u"), MaxSessionsCount); _serverManager.RegisterObjectForServer(audioInManager, ServiceName.Encode("audin:u"), MaxSessionsCount); _serverManager.RegisterObjectForServer(finalOutputRecorderManager, ServiceName.Encode("audrec:u"), MaxSessionsCount); + _serverManager.RegisterObjectForServer(audioSnoopManager, ServiceName.Encode("auddev"), MaxSessionsCount); } public void ServiceRequests() diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs index 2d3aa7ba9..e61b4e20f 100644 --- a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs @@ -23,10 +23,10 @@ namespace Ryujinx.Horizon.Sdk.Audio.Detail { _registry = registry; - BehaviourContext behaviourContext = new(); - behaviourContext.SetUserRevision((int)revision); + BehaviourInfo behaviourInfo = new(); + behaviourInfo.SetUserRevision((int)revision); - _isUsbDeviceSupported = behaviourContext.IsAudioUsbDeviceOutputSupported(); + _isUsbDeviceSupported = behaviourInfo.IsAudioUsbDeviceOutputSupported(); _sessions = registry.GetSessionByAppletResourceId(appletResourceId.Id); Os.CreateSystemEvent(out _audioEvent, EventClearMode.AutoClear, interProcess: true); diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs index 2b44276c8..48693fd14 100644 --- a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs @@ -60,7 +60,7 @@ namespace Ryujinx.Horizon.Sdk.Audio.Detail [CmifCommand(1)] public Result GetWorkBufferSize(out long workBufferSize, AudioRendererParameterInternal parameter) { - if (BehaviourContext.CheckValidRevision(parameter.Configuration.Revision)) + if (BehaviourInfo.CheckValidRevision(parameter.Configuration.Revision)) { workBufferSize = (long)Ryujinx.Audio.Renderer.Server.AudioRendererManager.GetWorkBufferSize(ref parameter.Configuration); @@ -72,7 +72,7 @@ namespace Ryujinx.Horizon.Sdk.Audio.Detail { workBufferSize = 0; - Logger.Warning?.Print(LogClass.ServiceAudio, $"Library Revision REV{BehaviourContext.GetRevisionNumber(parameter.Configuration.Revision)} is not supported!"); + Logger.Warning?.Print(LogClass.ServiceAudio, $"Library Revision REV{BehaviourInfo.GetRevisionNumber(parameter.Configuration.Revision)} is not supported!"); return AudioResult.UnsupportedRevision; } diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs index cf1fe3d1d..5eb31a14b 100644 --- a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs @@ -1,30 +1,64 @@ using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; namespace Ryujinx.Horizon.Sdk.Audio.Detail { partial class AudioSnoopManager : IAudioSnoopManager { + private byte[] _dspStatisticsParameter; + // Note: The interface changed completely on firmware 17.0.0, this implementation is for older firmware. - [CmifCommand(0)] + [CmifCommand(0)] // [6.0.0-16.1.0] public Result EnableDspUsageMeasurement() { return Result.Success; } - [CmifCommand(1)] + [CmifCommand(1)] // [6.0.0-16.1.0] public Result DisableDspUsageMeasurement() { return Result.Success; } - - [CmifCommand(6)] + + [CmifCommand(6)] // [6.0.0-16.1.0] public Result GetDspUsage(out uint usage) { usage = 0; return Result.Success; } + + [CmifCommand(0)] // 17.0.0+ + public Result GetDspStatistics(out uint statistics) => GetDspUsage(out statistics); + + [CmifCommand(1)] // 20.0.0+ + public Result GetAppletStateSummaries([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span summaries) + { + // Since we do not have any real applets, return empty state summaries. + summaries.Clear(); + + return Result.Success; + } + + [CmifCommand(2)] // 20.0.0+ + public Result SetDspStatisticsParameter([Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan parameter) + { + _dspStatisticsParameter = null; + _dspStatisticsParameter = new byte[0x100]; + parameter.CopyTo(_dspStatisticsParameter); + + return Result.Success; + } + + [CmifCommand(3)] // 20.0.0+ + public Result GetDspStatisticsParameter([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span parameter) + { + _dspStatisticsParameter.CopyTo(parameter); + + return Result.Success; + } } } diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs index 72853886a..9a52beb30 100644 --- a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs @@ -1,5 +1,6 @@ using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Sdk.Sf; +using System; namespace Ryujinx.Horizon.Sdk.Audio.Detail { @@ -8,5 +9,10 @@ namespace Ryujinx.Horizon.Sdk.Audio.Detail Result EnableDspUsageMeasurement(); Result DisableDspUsageMeasurement(); Result GetDspUsage(out uint usage); + + Result GetDspStatistics(out uint statistics); + Result GetAppletStateSummaries(Span summaries); + Result SetDspStatisticsParameter(ReadOnlySpan parameter); + Result GetDspStatisticsParameter(Span parameter); } } diff --git a/src/Ryujinx.Tests/Audio/Renderer/BiquadFilterParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/BiquadFilterParameterTests.cs index 617b52457..76e8a180a 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/BiquadFilterParameterTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/BiquadFilterParameterTests.cs @@ -9,7 +9,8 @@ namespace Ryujinx.Tests.Audio.Renderer [Test] public void EnsureTypeSize() { - Assert.AreEqual(0xC, Unsafe.SizeOf()); + Assert.AreEqual(0xC, Unsafe.SizeOf()); + Assert.AreEqual(0x18, Unsafe.SizeOf()); } } } diff --git a/src/Ryujinx.Tests/Audio/Renderer/Common/VoiceUpdateStateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Common/VoiceStateTests.cs similarity index 68% rename from src/Ryujinx.Tests/Audio/Renderer/Common/VoiceUpdateStateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Common/VoiceStateTests.cs index 7b09d18cc..eff982670 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Common/VoiceUpdateStateTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Common/VoiceStateTests.cs @@ -4,12 +4,12 @@ using System.Runtime.CompilerServices; namespace Ryujinx.Tests.Audio.Renderer.Common { - class VoiceUpdateStateTests + class VoiceStateTests { [Test] public void EnsureTypeSize() { - Assert.LessOrEqual(Unsafe.SizeOf(), 0x100); + Assert.LessOrEqual(Unsafe.SizeOf(), 0x100); } } } diff --git a/src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs index f393c971a..b774b74e1 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs @@ -8,19 +8,19 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp class ResamplerTests { [Test] - [TestCase(VoiceInParameter.SampleRateConversionQuality.Low)] - [TestCase(VoiceInParameter.SampleRateConversionQuality.Default)] - [TestCase(VoiceInParameter.SampleRateConversionQuality.High)] - public void TestResamplerConsistencyUpsampling(VoiceInParameter.SampleRateConversionQuality quality) + [TestCase(SampleRateConversionQuality.Low)] + [TestCase(SampleRateConversionQuality.Default)] + [TestCase(SampleRateConversionQuality.High)] + public void TestResamplerConsistencyUpsampling(SampleRateConversionQuality quality) { DoResamplingTest(44100, 48000, quality); } [Test] - [TestCase(VoiceInParameter.SampleRateConversionQuality.Low)] - [TestCase(VoiceInParameter.SampleRateConversionQuality.Default)] - [TestCase(VoiceInParameter.SampleRateConversionQuality.High)] - public void TestResamplerConsistencyDownsampling(VoiceInParameter.SampleRateConversionQuality quality) + [TestCase(SampleRateConversionQuality.Low)] + [TestCase(SampleRateConversionQuality.Default)] + [TestCase(SampleRateConversionQuality.High)] + public void TestResamplerConsistencyDownsampling(SampleRateConversionQuality quality) { DoResamplingTest(48000, 44100, quality); } @@ -32,7 +32,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp /// The input sample rate to test /// The output sample rate to test /// The resampler quality to use - private static void DoResamplingTest(int inputRate, int outputRate, VoiceInParameter.SampleRateConversionQuality quality) + private static void DoResamplingTest(int inputRate, int outputRate, SampleRateConversionQuality quality) { float inputSampleRate = inputRate; float outputSampleRate = outputRate; @@ -61,8 +61,8 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp float sumDifference = 0; int delay = quality switch { - VoiceInParameter.SampleRateConversionQuality.High => 3, - VoiceInParameter.SampleRateConversionQuality.Default => 1, + SampleRateConversionQuality.High => 3, + SampleRateConversionQuality.Default => 1, _ => 0, }; diff --git a/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameterTests.cs index 73c1ea9d3..d76478ed8 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameterTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameterTests.cs @@ -9,7 +9,8 @@ namespace Ryujinx.Tests.Audio.Renderer.Parameter.Effect [Test] public void EnsureTypeSize() { - Assert.AreEqual(0x18, Unsafe.SizeOf()); + Assert.AreEqual(0x18, Unsafe.SizeOf()); + Assert.AreEqual(0x24, Unsafe.SizeOf()); } } } diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/AddressInfoTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/AddressInfoTests.cs index 53a662584..6f855d3a4 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/AddressInfoTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/AddressInfoTests.cs @@ -16,8 +16,8 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestGetReference() { - MemoryPoolState[] memoryPoolState = new MemoryPoolState[1]; - memoryPoolState[0] = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); + MemoryPoolInfo[] memoryPoolState = new MemoryPoolInfo[1]; + memoryPoolState[0] = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu); memoryPoolState[0].SetCpuAddress(0x1000000, 0x10000); memoryPoolState[0].DspAddress = 0x4000000; diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs deleted file mode 100644 index 0b0ed7a54..000000000 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs +++ /dev/null @@ -1,413 +0,0 @@ -using NUnit.Framework; -using Ryujinx.Audio.Renderer.Server; - -namespace Ryujinx.Tests.Audio.Renderer.Server -{ - public class BehaviourContextTests - { - [Test] - public void TestCheckFeature() - { - int latestRevision = BehaviourContext.BaseRevisionMagic + BehaviourContext.LastRevision; - int previousRevision = BehaviourContext.BaseRevisionMagic + (BehaviourContext.LastRevision - 1); - int invalidRevision = BehaviourContext.BaseRevisionMagic + (BehaviourContext.LastRevision + 1); - - Assert.IsTrue(BehaviourContext.CheckFeatureSupported(latestRevision, latestRevision)); - Assert.IsFalse(BehaviourContext.CheckFeatureSupported(previousRevision, latestRevision)); - Assert.IsTrue(BehaviourContext.CheckFeatureSupported(latestRevision, previousRevision)); - // In case we get an invalid revision, this is supposed to auto default to REV1 internally.. idk what the hell Nintendo was thinking here.. - Assert.IsTrue(BehaviourContext.CheckFeatureSupported(invalidRevision, latestRevision)); - } - - [Test] - public void TestsMemoryPoolForceMappingEnabled() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision1); - - Assert.IsFalse(behaviourContext.IsMemoryPoolForceMappingEnabled()); - - behaviourContext.UpdateFlags(0x1); - - Assert.IsTrue(behaviourContext.IsMemoryPoolForceMappingEnabled()); - } - - [Test] - public void TestRevision1() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision1); - - Assert.IsFalse(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsFalse(behaviourContext.IsSplitterSupported()); - Assert.IsFalse(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsFalse(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsFalse(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsFalse(behaviourContext.IsSplitterBugFixed()); - Assert.IsFalse(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsFalse(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(1, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision2() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision2); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsFalse(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsFalse(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsFalse(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsFalse(behaviourContext.IsSplitterBugFixed()); - Assert.IsFalse(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsFalse(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(1, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision3() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision3); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsFalse(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsFalse(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsFalse(behaviourContext.IsSplitterBugFixed()); - Assert.IsFalse(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsFalse(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(1, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision4() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision4); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsFalse(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsFalse(behaviourContext.IsSplitterBugFixed()); - Assert.IsFalse(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsFalse(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.75f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(1, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision5() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision5); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision6() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision6); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsFalse(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision7() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision7); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsFalse(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision8() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision8); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsFalse(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision9() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision9); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision10() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision10); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(4, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision11() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision11); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision12() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision12); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - - [Test] - public void TestRevision13() - { - BehaviourContext behaviourContext = new(); - - behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision13); - - Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); - Assert.IsTrue(behaviourContext.IsSplitterSupported()); - Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); - Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); - Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); - Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); - Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); - Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); - Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); - Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); - Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); - Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); - Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); - Assert.IsTrue(behaviourContext.IsSplitterPrevVolumeResetSupported()); - - Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); - Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); - Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); - } - } -} diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourInfoTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourInfoTests.cs new file mode 100644 index 000000000..ac85e44b4 --- /dev/null +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourInfoTests.cs @@ -0,0 +1,413 @@ +using NUnit.Framework; +using Ryujinx.Audio.Renderer.Server; + +namespace Ryujinx.Tests.Audio.Renderer.Server +{ + public class BehaviourInfoTests + { + [Test] + public void TestCheckFeature() + { + int latestRevision = BehaviourInfo.BaseRevisionMagic + BehaviourInfo.LastRevision; + int previousRevision = BehaviourInfo.BaseRevisionMagic + (BehaviourInfo.LastRevision - 1); + int invalidRevision = BehaviourInfo.BaseRevisionMagic + (BehaviourInfo.LastRevision + 1); + + Assert.IsTrue(BehaviourInfo.CheckFeatureSupported(latestRevision, latestRevision)); + Assert.IsFalse(BehaviourInfo.CheckFeatureSupported(previousRevision, latestRevision)); + Assert.IsTrue(BehaviourInfo.CheckFeatureSupported(latestRevision, previousRevision)); + // In case we get an invalid revision, this is supposed to auto default to REV1 internally.. idk what the hell Nintendo was thinking here.. + Assert.IsTrue(BehaviourInfo.CheckFeatureSupported(invalidRevision, latestRevision)); + } + + [Test] + public void TestsMemoryPoolForceMappingEnabled() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision1); + + Assert.IsFalse(behaviourInfo.IsMemoryPoolForceMappingEnabled()); + + behaviourInfo.UpdateFlags(0x1); + + Assert.IsTrue(behaviourInfo.IsMemoryPoolForceMappingEnabled()); + } + + [Test] + public void TestRevision1() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision1); + + Assert.IsFalse(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsFalse(behaviourInfo.IsSplitterSupported()); + Assert.IsFalse(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsFalse(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsFalse(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsFalse(behaviourInfo.IsSplitterBugFixed()); + Assert.IsFalse(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsFalse(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsFalse(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsFalse(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsFalse(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsFalse(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.70f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(1, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(1, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision2() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision2); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsFalse(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsFalse(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsFalse(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsFalse(behaviourInfo.IsSplitterBugFixed()); + Assert.IsFalse(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsFalse(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsFalse(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsFalse(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsFalse(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsFalse(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.70f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(1, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(1, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision3() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision3); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsFalse(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsFalse(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsFalse(behaviourInfo.IsSplitterBugFixed()); + Assert.IsFalse(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsFalse(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsFalse(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsFalse(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsFalse(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsFalse(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.70f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(1, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(1, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision4() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision4); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsFalse(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsFalse(behaviourInfo.IsSplitterBugFixed()); + Assert.IsFalse(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsFalse(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsFalse(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsFalse(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsFalse(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsFalse(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.75f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(1, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(1, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision5() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision5); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourInfo.IsSplitterBugFixed()); + Assert.IsTrue(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsFalse(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsFalse(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsFalse(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsFalse(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(2, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision6() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision6); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourInfo.IsSplitterBugFixed()); + Assert.IsTrue(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsFalse(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsFalse(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsFalse(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsFalse(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(2, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision7() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision7); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourInfo.IsSplitterBugFixed()); + Assert.IsTrue(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsTrue(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsFalse(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsFalse(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsFalse(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(2, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision8() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision8); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourInfo.IsSplitterBugFixed()); + Assert.IsTrue(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsTrue(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsTrue(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsFalse(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsFalse(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(3, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision9() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision9); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourInfo.IsSplitterBugFixed()); + Assert.IsTrue(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsTrue(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsTrue(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsTrue(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsFalse(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(3, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision10() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision10); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourInfo.IsSplitterBugFixed()); + Assert.IsTrue(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsTrue(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsTrue(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsTrue(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsTrue(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsFalse(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(4, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision11() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision11); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourInfo.IsSplitterBugFixed()); + Assert.IsTrue(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsTrue(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsTrue(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsTrue(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsTrue(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsTrue(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsFalse(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(5, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision12() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision12); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourInfo.IsSplitterBugFixed()); + Assert.IsTrue(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsTrue(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsTrue(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsTrue(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsTrue(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsTrue(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(5, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision13() + { + BehaviourInfo behaviourInfo = new(); + + behaviourInfo.SetUserRevision(BehaviourInfo.BaseRevisionMagic + BehaviourInfo.Revision13); + + Assert.IsTrue(behaviourInfo.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourInfo.IsSplitterSupported()); + Assert.IsTrue(behaviourInfo.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourInfo.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourInfo.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourInfo.IsSplitterBugFixed()); + Assert.IsTrue(behaviourInfo.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourInfo.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsTrue(behaviourInfo.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsTrue(behaviourInfo.IsWaveBufferVersion2Supported()); + Assert.IsTrue(behaviourInfo.IsEffectInfoVersion2Supported()); + Assert.IsTrue(behaviourInfo.UseMultiTapBiquadFilterProcessing()); + Assert.IsTrue(behaviourInfo.IsNewEffectChannelMappingSupported()); + Assert.IsTrue(behaviourInfo.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsTrue(behaviourInfo.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourInfo.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(5, behaviourInfo.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourInfo.GetPerformanceMetricsDataFormat()); + } + } +} diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolStateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolInfoTests.cs similarity index 80% rename from src/Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolStateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolInfoTests.cs index c6a2e473e..612df4dc1 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolStateTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolInfoTests.cs @@ -4,18 +4,18 @@ using System.Runtime.CompilerServices; namespace Ryujinx.Tests.Audio.Renderer.Server { - class MemoryPoolStateTests + class MemoryPoolInfoTests { [Test] public void EnsureTypeSize() { - Assert.AreEqual(Unsafe.SizeOf(), 0x20); + Assert.AreEqual(Unsafe.SizeOf(), 0x20); } [Test] public void TestContains() { - MemoryPoolState memoryPool = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); + MemoryPoolInfo memoryPool = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu); memoryPool.SetCpuAddress(0x1000000, 0x1000); @@ -32,7 +32,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestTranslate() { - MemoryPoolState memoryPool = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); + MemoryPoolInfo memoryPool = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu); memoryPool.SetCpuAddress(0x1000000, 0x1000); @@ -48,7 +48,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestIsMapped() { - MemoryPoolState memoryPool = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); + MemoryPoolInfo memoryPool = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu); memoryPool.SetCpuAddress(0x1000000, 0x1000); diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/MixStateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/MixInfoTests.cs similarity index 73% rename from src/Ryujinx.Tests/Audio/Renderer/Server/MixStateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/MixInfoTests.cs index 6262913b4..dbdc5c343 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/MixStateTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/MixInfoTests.cs @@ -4,12 +4,12 @@ using System.Runtime.CompilerServices; namespace Ryujinx.Tests.Audio.Renderer.Server { - class MixStateTests + class MixInfoTests { [Test] public void EnsureTypeSize() { - Assert.AreEqual(0x940, Unsafe.SizeOf()); + Assert.AreEqual(0x940, Unsafe.SizeOf()); } } } diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs index d7879d627..4cf14a81f 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs @@ -16,8 +16,8 @@ namespace Ryujinx.Tests.Audio.Renderer.Server public void TestInitializeSystemPool() { PoolMapper poolMapper = new(DummyProcessHandle, true); - MemoryPoolState memoryPoolDsp = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); - MemoryPoolState memoryPoolCpu = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); + MemoryPoolInfo memoryPoolDsp = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Dsp); + MemoryPoolInfo memoryPoolCpu = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu); const CpuAddress CpuAddress = 0x20000; const DspAddress DspAddress = CpuAddress; // TODO: DSP LLE @@ -35,8 +35,8 @@ namespace Ryujinx.Tests.Audio.Renderer.Server public void TestGetProcessHandle() { PoolMapper poolMapper = new(DummyProcessHandle, true); - MemoryPoolState memoryPoolDsp = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); - MemoryPoolState memoryPoolCpu = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); + MemoryPoolInfo memoryPoolDsp = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Dsp); + MemoryPoolInfo memoryPoolCpu = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu); Assert.AreEqual(0xFFFF8001, poolMapper.GetProcessHandle(ref memoryPoolCpu)); Assert.AreEqual(DummyProcessHandle, poolMapper.GetProcessHandle(ref memoryPoolDsp)); @@ -46,8 +46,8 @@ namespace Ryujinx.Tests.Audio.Renderer.Server public void TestMappings() { PoolMapper poolMapper = new(DummyProcessHandle, true); - MemoryPoolState memoryPoolDsp = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); - MemoryPoolState memoryPoolCpu = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); + MemoryPoolInfo memoryPoolDsp = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Dsp); + MemoryPoolInfo memoryPoolCpu = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu); const CpuAddress CpuAddress = 0x20000; const DspAddress DspAddress = CpuAddress; // TODO: DSP LLE @@ -77,11 +77,11 @@ namespace Ryujinx.Tests.Audio.Renderer.Server const int MemoryPoolStateArraySize = 0x10; const CpuAddress CpuAddressRegionEnding = CpuAddress * MemoryPoolStateArraySize; - MemoryPoolState[] memoryPoolStateArray = new MemoryPoolState[MemoryPoolStateArraySize]; + MemoryPoolInfo[] memoryPoolStateArray = new MemoryPoolInfo[MemoryPoolStateArraySize]; for (int i = 0; i < memoryPoolStateArray.Length; i++) { - memoryPoolStateArray[i] = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); + memoryPoolStateArray[i] = MemoryPoolInfo.Create(MemoryPoolInfo.LocationType.Cpu); memoryPoolStateArray[i].SetCpuAddress(CpuAddress + (ulong)i * CpuSize, CpuSize); } diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs index 80b801336..e85111c9f 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server public void EnsureTypeSize() { Assert.AreEqual(0xE0, Unsafe.SizeOf()); - Assert.AreEqual(0x110, Unsafe.SizeOf()); + Assert.AreEqual(0x128, Unsafe.SizeOf()); } } } diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/VoiceStateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/VoiceInfoTests.cs similarity index 71% rename from src/Ryujinx.Tests/Audio/Renderer/Server/VoiceStateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/VoiceInfoTests.cs index dbd6eff8f..1382d693c 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/VoiceStateTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/VoiceInfoTests.cs @@ -4,12 +4,12 @@ using System.Runtime.CompilerServices; namespace Ryujinx.Tests.Audio.Renderer.Server { - class VoiceStateTests + class VoiceInfoTests { [Test] public void EnsureTypeSize() { - Assert.LessOrEqual(Unsafe.SizeOf(), 0x220); + Assert.LessOrEqual(Unsafe.SizeOf(), 0x238); } } } diff --git a/src/Ryujinx.Tests/Audio/Renderer/VoiceInParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/VoiceInParameterTests.cs index 239da195a..e91675563 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/VoiceInParameterTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/VoiceInParameterTests.cs @@ -9,7 +9,8 @@ namespace Ryujinx.Tests.Audio.Renderer [Test] public void EnsureTypeSize() { - Assert.AreEqual(0x170, Unsafe.SizeOf()); + Assert.AreEqual(0x170, Unsafe.SizeOf()); + Assert.AreEqual(0x188, Unsafe.SizeOf()); } } } diff --git a/src/Ryujinx.Tests/Memory/PartialUnmaps.cs b/src/Ryujinx.Tests/Memory/PartialUnmaps.cs index edda78560..d9df2200d 100644 --- a/src/Ryujinx.Tests/Memory/PartialUnmaps.cs +++ b/src/Ryujinx.Tests/Memory/PartialUnmaps.cs @@ -224,7 +224,7 @@ namespace Ryujinx.Tests.Memory ref var state = ref PartialUnmapState.GetRef(); - // Create some state to be used for managing the native writing loop. + // Create some info to be used for managing the native writing loop. int stateSize = Unsafe.SizeOf(); var statePtr = Marshal.AllocHGlobal(stateSize); Unsafe.InitBlockUnaligned((void*)statePtr, 0, (uint)stateSize);