diff options
Diffstat (limited to 'src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs')
-rw-r--r-- | src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs | 218 |
1 files changed, 169 insertions, 49 deletions
diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs index 3efa783c..a7b82a6b 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs @@ -1,4 +1,5 @@ using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Utils; using Ryujinx.Common; @@ -16,14 +17,34 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter public class SplitterContext { /// <summary> + /// Amount of biquad filter states per splitter destination. + /// </summary> + public const int BqfStatesPerDestination = 4; + + /// <summary> /// Storage for <see cref="SplitterState"/>. /// </summary> private Memory<SplitterState> _splitters; /// <summary> - /// Storage for <see cref="SplitterDestination"/>. + /// Storage for <see cref="SplitterDestinationVersion1"/>. + /// </summary> + private Memory<SplitterDestinationVersion1> _splitterDestinationsV1; + + /// <summary> + /// Storage for <see cref="SplitterDestinationVersion2"/>. + /// </summary> + private Memory<SplitterDestinationVersion2> _splitterDestinationsV2; + + /// <summary> + /// Splitter biquad filtering states. + /// </summary> + private Memory<BiquadFilterState> _splitterBqfStates; + + /// <summary> + /// Version of the splitter context that is being used, currently can be 1 or 2. /// </summary> - private Memory<SplitterDestination> _splitterDestinations; + public int Version { get; private set; } /// <summary> /// If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, in SplitterInParameter, ref SequenceReader{byte})"/>. @@ -36,12 +57,17 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// <param name="behaviourContext">The behaviour context.</param> /// <param name="parameter">The audio renderer configuration.</param> /// <param name="workBufferAllocator">The <see cref="WorkBufferAllocator"/>.</param> + /// <param name="splitterBqfStates">Memory to store the biquad filtering state for splitters during processing.</param> /// <returns>Return true if the initialization was successful.</returns> - public bool Initialize(ref BehaviourContext behaviourContext, ref AudioRendererConfiguration parameter, WorkBufferAllocator workBufferAllocator) + public bool Initialize( + ref BehaviourContext behaviourContext, + ref AudioRendererConfiguration parameter, + WorkBufferAllocator workBufferAllocator, + Memory<BiquadFilterState> splitterBqfStates) { if (!behaviourContext.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0) { - Setup(Memory<SplitterState>.Empty, Memory<SplitterDestination>.Empty, false); + Setup(Memory<SplitterState>.Empty, Memory<SplitterDestinationVersion1>.Empty, Memory<SplitterDestinationVersion2>.Empty, false); return true; } @@ -60,23 +86,62 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter splitter = new SplitterState(splitterId++); } - Memory<SplitterDestination> splitterDestinations = workBufferAllocator.Allocate<SplitterDestination>(parameter.SplitterDestinationCount, - SplitterDestination.Alignment); + Memory<SplitterDestinationVersion1> splitterDestinationsV1 = Memory<SplitterDestinationVersion1>.Empty; + Memory<SplitterDestinationVersion2> splitterDestinationsV2 = Memory<SplitterDestinationVersion2>.Empty; - if (splitterDestinations.IsEmpty) + if (!behaviourContext.IsBiquadFilterParameterForSplitterEnabled()) { - return false; - } + Version = 1; + + splitterDestinationsV1 = workBufferAllocator.Allocate<SplitterDestinationVersion1>(parameter.SplitterDestinationCount, + SplitterDestinationVersion1.Alignment); + + if (splitterDestinationsV1.IsEmpty) + { + return false; + } - int splitterDestinationId = 0; - foreach (ref SplitterDestination data in splitterDestinations.Span) + int splitterDestinationId = 0; + foreach (ref SplitterDestinationVersion1 data in splitterDestinationsV1.Span) + { + data = new SplitterDestinationVersion1(splitterDestinationId++); + } + } + else { - data = new SplitterDestination(splitterDestinationId++); + Version = 2; + + splitterDestinationsV2 = workBufferAllocator.Allocate<SplitterDestinationVersion2>(parameter.SplitterDestinationCount, + SplitterDestinationVersion2.Alignment); + + if (splitterDestinationsV2.IsEmpty) + { + return false; + } + + int splitterDestinationId = 0; + foreach (ref SplitterDestinationVersion2 data in splitterDestinationsV2.Span) + { + data = new SplitterDestinationVersion2(splitterDestinationId++); + } + + if (parameter.SplitterDestinationCount > 0) + { + // Official code stores it in the SplitterDestinationVersion2 struct, + // but we don't to avoid using unsafe code. + + splitterBqfStates.Span.Clear(); + _splitterBqfStates = splitterBqfStates; + } + else + { + _splitterBqfStates = Memory<BiquadFilterState>.Empty; + } } SplitterState.InitializeSplitters(splitters.Span); - Setup(splitters, splitterDestinations, behaviourContext.IsSplitterBugFixed()); + Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed()); return true; } @@ -93,7 +158,15 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter if (behaviourContext.IsSplitterSupported()) { size = WorkBufferAllocator.GetTargetSize<SplitterState>(size, parameter.SplitterCount, SplitterState.Alignment); - size = WorkBufferAllocator.GetTargetSize<SplitterDestination>(size, parameter.SplitterDestinationCount, SplitterDestination.Alignment); + + if (behaviourContext.IsBiquadFilterParameterForSplitterEnabled()) + { + size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion2>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion2.Alignment); + } + else + { + size = WorkBufferAllocator.GetTargetSize<SplitterDestinationVersion1>(size, parameter.SplitterDestinationCount, SplitterDestinationVersion1.Alignment); + } if (behaviourContext.IsSplitterBugFixed()) { @@ -110,12 +183,18 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Setup the <see cref="SplitterContext"/> instance. /// </summary> /// <param name="splitters">The <see cref="SplitterState"/> storage.</param> - /// <param name="splitterDestinations">The <see cref="SplitterDestination"/> storage.</param> + /// <param name="splitterDestinationsV1">The <see cref="SplitterDestinationVersion1"/> storage.</param> + /// <param name="splitterDestinationsV2">The <see cref="SplitterDestinationVersion2"/> storage.</param> /// <param name="isBugFixed">If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, in SplitterInParameter, ref SequenceReader{byte})"/>.</param> - private void Setup(Memory<SplitterState> splitters, Memory<SplitterDestination> splitterDestinations, bool isBugFixed) + private void Setup( + Memory<SplitterState> splitters, + Memory<SplitterDestinationVersion1> splitterDestinationsV1, + Memory<SplitterDestinationVersion2> splitterDestinationsV2, + bool isBugFixed) { _splitters = splitters; - _splitterDestinations = splitterDestinations; + _splitterDestinationsV1 = splitterDestinationsV1; + _splitterDestinationsV2 = splitterDestinationsV2; IsBugFixed = isBugFixed; } @@ -141,7 +220,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter return 0; } - return _splitterDestinations.Length / _splitters.Length; + int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length; + + return length / _splitters.Length; } /// <summary> @@ -178,7 +259,39 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } /// <summary> - /// Update one or multiple <see cref="SplitterDestination"/> from user parameters. + /// Update one splitter destination data from user parameters. + /// </summary> + /// <param name="input">The raw data after the splitter header.</param> + /// <returns>True if the update was successful, false otherwise</returns> + private bool UpdateData<T>(ref SequenceReader<byte> input) where T : unmanaged, ISplitterDestinationInParameter + { + ref readonly T parameter = ref input.GetRefOrRefToCopy<T>(out _); + + Debug.Assert(parameter.IsMagicValid()); + + if (parameter.IsMagicValid()) + { + int length = _splitterDestinationsV2.IsEmpty ? _splitterDestinationsV1.Length : _splitterDestinationsV2.Length; + + if (parameter.Id >= 0 && parameter.Id < length) + { + SplitterDestination destination = GetDestination(parameter.Id); + + destination.Update(parameter); + } + + return true; + } + else + { + input.Rewind(Unsafe.SizeOf<T>()); + + return false; + } + } + + /// <summary> + /// Update one or multiple splitter destination data from user parameters. /// </summary> /// <param name="inputHeader">The splitter header.</param> /// <param name="input">The raw data after the splitter header.</param> @@ -186,23 +299,23 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { for (int i = 0; i < inputHeader.SplitterDestinationCount; i++) { - ref readonly SplitterDestinationInParameter parameter = ref input.GetRefOrRefToCopy<SplitterDestinationInParameter>(out _); - - Debug.Assert(parameter.IsMagicValid()); - - if (parameter.IsMagicValid()) + if (Version == 1) { - if (parameter.Id >= 0 && parameter.Id < _splitterDestinations.Length) + if (!UpdateData<SplitterDestinationInParameterVersion1>(ref input)) { - ref SplitterDestination destination = ref GetDestination(parameter.Id); - - destination.Update(parameter); + break; + } + } + else if (Version == 2) + { + if (!UpdateData<SplitterDestinationInParameterVersion2>(ref input)) + { + break; } } else { - input.Rewind(Unsafe.SizeOf<SplitterDestinationInParameter>()); - break; + Debug.Fail($"Invalid splitter context version {Version}."); } } } @@ -214,7 +327,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// <returns>Return true if the update was successful.</returns> public bool Update(ref SequenceReader<byte> input) { - if (_splitterDestinations.IsEmpty || _splitters.IsEmpty) + if (!UsingSplitter()) { return true; } @@ -251,32 +364,29 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } /// <summary> - /// Get a reference to a <see cref="SplitterDestination"/> at the given <paramref name="id"/>. - /// </summary> - /// <param name="id">The index to use.</param> - /// <returns>A reference to a <see cref="SplitterDestination"/> at the given <paramref name="id"/>.</returns> - public ref SplitterDestination GetDestination(int id) - { - return ref SpanIOHelper.GetFromMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length); - } - - /// <summary> - /// Get a <see cref="Memory{SplitterDestination}"/> at the given <paramref name="id"/>. + /// Get a reference to the splitter destination data at the given <paramref name="id"/>. /// </summary> /// <param name="id">The index to use.</param> - /// <returns>A <see cref="Memory{SplitterDestination}"/> at the given <paramref name="id"/>.</returns> - public Memory<SplitterDestination> GetDestinationMemory(int id) + /// <returns>A reference to the splitter destination data at the given <paramref name="id"/>.</returns> + public SplitterDestination GetDestination(int id) { - return SpanIOHelper.GetMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length); + if (_splitterDestinationsV2.IsEmpty) + { + return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV1, id, (uint)_splitterDestinationsV1.Length)); + } + else + { + return new SplitterDestination(ref SpanIOHelper.GetFromMemory(_splitterDestinationsV2, id, (uint)_splitterDestinationsV2.Length)); + } } /// <summary> - /// Get a <see cref="Span{SplitterDestination}"/> in the <see cref="SplitterState"/> at <paramref name="id"/> and pass <paramref name="destinationId"/> to <see cref="SplitterState.GetData(int)"/>. + /// Get a <see cref="SplitterDestination"/> in the <see cref="SplitterState"/> at <paramref name="id"/> and pass <paramref name="destinationId"/> to <see cref="SplitterState.GetData(int)"/>. /// </summary> /// <param name="id">The index to use to get the <see cref="SplitterState"/>.</param> /// <param name="destinationId">The index of the <see cref="SplitterDestination"/>.</param> - /// <returns>A <see cref="Span{SplitterDestination}"/>.</returns> - public Span<SplitterDestination> GetDestination(int id, int destinationId) + /// <returns>A <see cref="SplitterDestination"/>.</returns> + public SplitterDestination GetDestination(int id, int destinationId) { ref SplitterState splitter = ref GetState(id); @@ -284,12 +394,22 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } /// <summary> + /// Gets the biquad filter state for a given splitter destination. + /// </summary> + /// <param name="destination">The splitter destination.</param> + /// <returns>Biquad filter state for the specified destination.</returns> + public Memory<BiquadFilterState> GetBiquadFilterState(SplitterDestination destination) + { + return _splitterBqfStates.Slice(destination.Id * BqfStatesPerDestination, BqfStatesPerDestination); + } + + /// <summary> /// Return true if the audio renderer has any splitters. /// </summary> /// <returns>True if the audio renderer has any splitters.</returns> public bool UsingSplitter() { - return !_splitters.IsEmpty && !_splitterDestinations.IsEmpty; + return !_splitters.IsEmpty && (!_splitterDestinationsV1.IsEmpty || !_splitterDestinationsV2.IsEmpty); } /// <summary> |