aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Audio/Renderer/Server/Splitter
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2024-05-17 16:46:43 -0300
committerGitHub <noreply@github.com>2024-05-17 16:46:43 -0300
commit4d84df94873a070f6f5c199438f957b24d8cf8a9 (patch)
tree3e4b4cc9585526c63f3a9fdb3a150bfd721a5030 /src/Ryujinx.Audio/Renderer/Server/Splitter
parent9ec8b2c01a0b00f3a33d9a23b9f5ff3758520484 (diff)
Update audio renderer to REV12: Add support for splitter biquad filter (#6813)
* Update audio renderer to REV12: Add support for splitter biquad filter * Formatting * Official names * Update BiquadFilterState size + other fixes * Update tests * Update comment for version 2 * Size test for SplitterDestinationVersion2 * Should use Volume1 if no ramp
Diffstat (limited to 'src/Ryujinx.Audio/Renderer/Server/Splitter')
-rw-r--r--src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs218
-rw-r--r--src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs339
-rw-r--r--src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs206
-rw-r--r--src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs250
-rw-r--r--src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs74
5 files changed, 926 insertions, 161 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>
diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs
index 1faf7921..36dfa5e4 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs
@@ -1,115 +1,198 @@
using Ryujinx.Audio.Renderer.Parameter;
-using Ryujinx.Common.Utilities;
using System;
using System.Diagnostics;
-using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Server.Splitter
{
/// <summary>
/// Server state for a splitter destination.
/// </summary>
- [StructLayout(LayoutKind.Sequential, Size = 0xE0, Pack = Alignment)]
- public struct SplitterDestination
+ public ref struct SplitterDestination
{
- public const int Alignment = 0x10;
+ private ref SplitterDestinationVersion1 _v1;
+ private ref SplitterDestinationVersion2 _v2;
/// <summary>
- /// The unique id of this <see cref="SplitterDestination"/>.
+ /// Checks if the splitter destination data reference is null.
/// </summary>
- public int Id;
+ public bool IsNull => Unsafe.IsNullRef(ref _v1) && Unsafe.IsNullRef(ref _v2);
/// <summary>
- /// The mix to output the result of the splitter.
- /// </summary>
- public int DestinationId;
-
- /// <summary>
- /// Mix buffer volumes storage.
- /// </summary>
- private MixArray _mix;
- private MixArray _previousMix;
-
- /// <summary>
- /// Pointer to the next linked element.
- /// </summary>
- private unsafe SplitterDestination* _next;
-
- /// <summary>
- /// Set to true if in use.
+ /// The splitter unique id.
/// </summary>
- [MarshalAs(UnmanagedType.I1)]
- public bool IsUsed;
+ public int Id
+ {
+ get
+ {
+ if (Unsafe.IsNullRef(ref _v2))
+ {
+ if (Unsafe.IsNullRef(ref _v1))
+ {
+ return 0;
+ }
+ else
+ {
+ return _v1.Id;
+ }
+ }
+ else
+ {
+ return _v2.Id;
+ }
+ }
+ }
/// <summary>
- /// Set to true if the internal state need to be updated.
+ /// The mix to output the result of the splitter.
/// </summary>
- [MarshalAs(UnmanagedType.I1)]
- public bool NeedToUpdateInternalState;
-
- [StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax, Pack = 1)]
- private struct MixArray { }
+ public int DestinationId
+ {
+ get
+ {
+ if (Unsafe.IsNullRef(ref _v2))
+ {
+ if (Unsafe.IsNullRef(ref _v1))
+ {
+ return 0;
+ }
+ else
+ {
+ return _v1.DestinationId;
+ }
+ }
+ else
+ {
+ return _v2.DestinationId;
+ }
+ }
+ }
/// <summary>
/// Mix buffer volumes.
/// </summary>
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
- public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _mix);
+ public Span<float> MixBufferVolume
+ {
+ get
+ {
+ if (Unsafe.IsNullRef(ref _v2))
+ {
+ if (Unsafe.IsNullRef(ref _v1))
+ {
+ return Span<float>.Empty;
+ }
+ else
+ {
+ return _v1.MixBufferVolume;
+ }
+ }
+ else
+ {
+ return _v2.MixBufferVolume;
+ }
+ }
+ }
/// <summary>
/// Previous mix buffer volumes.
/// </summary>
/// <remarks>Used when a splitter id is specified in the mix.</remarks>
- public Span<float> PreviousMixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _previousMix);
+ public Span<float> PreviousMixBufferVolume
+ {
+ get
+ {
+ if (Unsafe.IsNullRef(ref _v2))
+ {
+ if (Unsafe.IsNullRef(ref _v1))
+ {
+ return Span<float>.Empty;
+ }
+ else
+ {
+ return _v1.PreviousMixBufferVolume;
+ }
+ }
+ else
+ {
+ return _v2.PreviousMixBufferVolume;
+ }
+ }
+ }
/// <summary>
- /// Get the <see cref="Span{SplitterDestination}"/> of the next element or <see cref="Span{SplitterDestination}.Empty"/> if not present.
+ /// Get the <see cref="SplitterDestination"/> of the next element or null if not present.
/// </summary>
- public readonly Span<SplitterDestination> Next
+ public readonly SplitterDestination Next
{
get
{
unsafe
{
- return _next != null ? new Span<SplitterDestination>(_next, 1) : Span<SplitterDestination>.Empty;
+ if (Unsafe.IsNullRef(ref _v2))
+ {
+ if (Unsafe.IsNullRef(ref _v1))
+ {
+ return new SplitterDestination();
+ }
+ else
+ {
+ return new SplitterDestination(ref _v1.Next);
+ }
+ }
+ else
+ {
+ return new SplitterDestination(ref _v2.Next);
+ }
}
}
}
/// <summary>
- /// Create a new <see cref="SplitterDestination"/>.
+ /// Creates a new splitter destination wrapper for the version 1 splitter destination data.
/// </summary>
- /// <param name="id">The unique id of this <see cref="SplitterDestination"/>.</param>
- public SplitterDestination(int id) : this()
+ /// <param name="v1">Version 1 splitter destination data</param>
+ public SplitterDestination(ref SplitterDestinationVersion1 v1)
{
- Id = id;
- DestinationId = Constants.UnusedMixId;
-
- ClearVolumes();
+ _v1 = ref v1;
+ _v2 = ref Unsafe.NullRef<SplitterDestinationVersion2>();
}
/// <summary>
- /// Update the <see cref="SplitterDestination"/> from user parameter.
+ /// Creates a new splitter destination wrapper for the version 2 splitter destination data.
/// </summary>
- /// <param name="parameter">The user parameter.</param>
- public void Update(SplitterDestinationInParameter parameter)
+ /// <param name="v2">Version 2 splitter destination data</param>
+ public SplitterDestination(ref SplitterDestinationVersion2 v2)
{
- Debug.Assert(Id == parameter.Id);
-
- if (parameter.IsMagicValid() && Id == parameter.Id)
- {
- DestinationId = parameter.DestinationId;
- parameter.MixBufferVolume.CopyTo(MixBufferVolume);
-
- if (!IsUsed && parameter.IsUsed)
- {
- MixBufferVolume.CopyTo(PreviousMixBufferVolume);
+ _v1 = ref Unsafe.NullRef<SplitterDestinationVersion1>();
+ _v2 = ref v2;
+ }
- NeedToUpdateInternalState = false;
- }
+ /// <summary>
+ /// Creates a new splitter destination wrapper for the splitter destination data.
+ /// </summary>
+ /// <param name="v1">Version 1 splitter destination data</param>
+ /// <param name="v2">Version 2 splitter destination data</param>
+ public unsafe SplitterDestination(SplitterDestinationVersion1* v1, SplitterDestinationVersion2* v2)
+ {
+ _v1 = ref Unsafe.AsRef<SplitterDestinationVersion1>(v1);
+ _v2 = ref Unsafe.AsRef<SplitterDestinationVersion2>(v2);
+ }
- IsUsed = parameter.IsUsed;
+ /// <summary>
+ /// Update the splitter destination data from user parameter.
+ /// </summary>
+ /// <param name="parameter">The user parameter.</param>
+ public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
+ {
+ if (Unsafe.IsNullRef(ref _v2))
+ {
+ _v1.Update(parameter);
+ }
+ else
+ {
+ _v2.Update(parameter);
}
}
@@ -118,12 +201,14 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public void UpdateInternalState()
{
- if (IsUsed && NeedToUpdateInternalState)
+ if (Unsafe.IsNullRef(ref _v2))
{
- MixBufferVolume.CopyTo(PreviousMixBufferVolume);
+ _v1.UpdateInternalState();
+ }
+ else
+ {
+ _v2.UpdateInternalState();
}
-
- NeedToUpdateInternalState = false;
}
/// <summary>
@@ -131,16 +216,23 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public void MarkAsNeedToUpdateInternalState()
{
- NeedToUpdateInternalState = true;
+ if (Unsafe.IsNullRef(ref _v2))
+ {
+ _v1.MarkAsNeedToUpdateInternalState();
+ }
+ else
+ {
+ _v2.MarkAsNeedToUpdateInternalState();
+ }
}
/// <summary>
- /// Return true if the <see cref="SplitterDestination"/> is used and has a destination.
+ /// Return true if the splitter destination is used and has a destination.
/// </summary>
- /// <returns>True if the <see cref="SplitterDestination"/> is used and has a destination.</returns>
+ /// <returns>True if the splitter destination is used and has a destination.</returns>
public readonly bool IsConfigured()
{
- return IsUsed && DestinationId != Constants.UnusedMixId;
+ return Unsafe.IsNullRef(ref _v2) ? _v1.IsConfigured() : _v2.IsConfigured();
}
/// <summary>
@@ -150,9 +242,17 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <returns>The volume for the given destination.</returns>
public float GetMixVolume(int destinationIndex)
{
- Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
+ return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolume(destinationIndex) : _v2.GetMixVolume(destinationIndex);
+ }
- return MixBufferVolume[destinationIndex];
+ /// <summary>
+ /// Get the previous volume for a given destination.
+ /// </summary>
+ /// <param name="destinationIndex">The destination index to use.</param>
+ /// <returns>The volume for the given destination.</returns>
+ public float GetMixVolumePrev(int destinationIndex)
+ {
+ return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolumePrev(destinationIndex) : _v2.GetMixVolumePrev(destinationIndex);
}
/// <summary>
@@ -160,22 +260,33 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public void ClearVolumes()
{
- MixBufferVolume.Clear();
- PreviousMixBufferVolume.Clear();
+ if (Unsafe.IsNullRef(ref _v2))
+ {
+ _v1.ClearVolumes();
+ }
+ else
+ {
+ _v2.ClearVolumes();
+ }
}
/// <summary>
- /// Link the next element to the given <see cref="SplitterDestination"/>.
+ /// Link the next element to the given splitter destination.
/// </summary>
- /// <param name="next">The given <see cref="SplitterDestination"/> to link.</param>
- public void Link(ref SplitterDestination next)
+ /// <param name="next">The given splitter destination to link.</param>
+ public void Link(SplitterDestination next)
{
- unsafe
+ if (Unsafe.IsNullRef(ref _v2))
{
- fixed (SplitterDestination* nextPtr = &next)
- {
- _next = nextPtr;
- }
+ 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);
}
}
@@ -184,10 +295,74 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public void Unlink()
{
- unsafe
+ if (Unsafe.IsNullRef(ref _v2))
+ {
+ _v1.Unlink();
+ }
+ else
+ {
+ _v2.Unlink();
+ }
+ }
+
+ /// <summary>
+ /// Checks if any biquad filter is enabled.
+ /// </summary>
+ /// <returns>True if any biquad filter is enabled.</returns>
+ public bool IsBiquadFilterEnabled()
+ {
+ return !Unsafe.IsNullRef(ref _v2) && _v2.IsBiquadFilterEnabled();
+ }
+
+ /// <summary>
+ /// Checks if any biquad filter was previously enabled.
+ /// </summary>
+ /// <returns>True if any biquad filter was previously enabled.</returns>
+ public bool IsBiquadFilterEnabledPrev()
+ {
+ return !Unsafe.IsNullRef(ref _v2) && _v2.IsBiquadFilterEnabledPrev();
+ }
+
+ /// <summary>
+ /// Gets the biquad filter parameters.
+ /// </summary>
+ /// <param name="index">Biquad filter index (0 or 1).</param>
+ /// <returns>Biquad filter parameters.</returns>
+ public ref BiquadFilterParameter GetBiquadFilterParameter(int index)
+ {
+ Debug.Assert(!Unsafe.IsNullRef(ref _v2));
+
+ return ref _v2.GetBiquadFilterParameter(index);
+ }
+
+ /// <summary>
+ /// Checks if any biquad filter was previously enabled.
+ /// </summary>
+ /// <param name="index">Biquad filter index (0 or 1).</param>
+ public void UpdateBiquadFilterEnabledPrev(int index)
+ {
+ if (!Unsafe.IsNullRef(ref _v2))
{
- _next = null;
+ _v2.UpdateBiquadFilterEnabledPrev(index);
}
}
+
+ /// <summary>
+ /// Get the reference for the version 1 splitter destination data, or null if version 2 is being used or the destination is null.
+ /// </summary>
+ /// <returns>Reference for the version 1 splitter destination data.</returns>
+ public ref SplitterDestinationVersion1 GetV1RefOrNull()
+ {
+ return ref _v1;
+ }
+
+ /// <summary>
+ /// Get the reference for the version 2 splitter destination data, or null if version 1 is being used or the destination is null.
+ /// </summary>
+ /// <returns>Reference for the version 2 splitter destination data.</returns>
+ public ref SplitterDestinationVersion2 GetV2RefOrNull()
+ {
+ return ref _v2;
+ }
}
}
diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs
new file mode 100644
index 00000000..5d2b8fb0
--- /dev/null
+++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs
@@ -0,0 +1,206 @@
+using Ryujinx.Audio.Renderer.Parameter;
+using Ryujinx.Common.Utilities;
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Server.Splitter
+{
+ /// <summary>
+ /// Server state for a splitter destination (version 1).
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Size = 0xE0, Pack = Alignment)]
+ public struct SplitterDestinationVersion1
+ {
+ public const int Alignment = 0x10;
+
+ /// <summary>
+ /// The unique id of this <see cref="SplitterDestinationVersion1"/>.
+ /// </summary>
+ public int Id;
+
+ /// <summary>
+ /// The mix to output the result of the splitter.
+ /// </summary>
+ public int DestinationId;
+
+ /// <summary>
+ /// Mix buffer volumes storage.
+ /// </summary>
+ private MixArray _mix;
+ private MixArray _previousMix;
+
+ /// <summary>
+ /// Pointer to the next linked element.
+ /// </summary>
+ private unsafe SplitterDestinationVersion1* _next;
+
+ /// <summary>
+ /// Set to true if in use.
+ /// </summary>
+ [MarshalAs(UnmanagedType.I1)]
+ public bool IsUsed;
+
+ /// <summary>
+ /// Set to true if the internal state need to be updated.
+ /// </summary>
+ [MarshalAs(UnmanagedType.I1)]
+ public bool NeedToUpdateInternalState;
+
+ [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
+ private struct MixArray { }
+
+ /// <summary>
+ /// Mix buffer volumes.
+ /// </summary>
+ /// <remarks>Used when a splitter id is specified in the mix.</remarks>
+ public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _mix);
+
+ /// <summary>
+ /// Previous mix buffer volumes.
+ /// </summary>
+ /// <remarks>Used when a splitter id is specified in the mix.</remarks>
+ public Span<float> PreviousMixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _previousMix);
+
+ /// <summary>
+ /// Get the reference of the next element or null if not present.
+ /// </summary>
+ public readonly ref SplitterDestinationVersion1 Next
+ {
+ get
+ {
+ unsafe
+ {
+ return ref Unsafe.AsRef<SplitterDestinationVersion1>(_next);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Create a new <see cref="SplitterDestinationVersion1"/>.
+ /// </summary>
+ /// <param name="id">The unique id of this <see cref="SplitterDestinationVersion1"/>.</param>
+ public SplitterDestinationVersion1(int id) : this()
+ {
+ Id = id;
+ DestinationId = Constants.UnusedMixId;
+
+ ClearVolumes();
+ }
+
+ /// <summary>
+ /// Update the <see cref="SplitterDestinationVersion1"/> from user parameter.
+ /// </summary>
+ /// <param name="parameter">The user parameter.</param>
+ public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
+ {
+ Debug.Assert(Id == parameter.Id);
+
+ if (parameter.IsMagicValid() && Id == parameter.Id)
+ {
+ DestinationId = parameter.DestinationId;
+
+ parameter.MixBufferVolume.CopyTo(MixBufferVolume);
+
+ if (!IsUsed && parameter.IsUsed)
+ {
+ MixBufferVolume.CopyTo(PreviousMixBufferVolume);
+
+ NeedToUpdateInternalState = false;
+ }
+
+ IsUsed = parameter.IsUsed;
+ }
+ }
+
+ /// <summary>
+ /// Update the internal state of the instance.
+ /// </summary>
+ public void UpdateInternalState()
+ {
+ if (IsUsed && NeedToUpdateInternalState)
+ {
+ MixBufferVolume.CopyTo(PreviousMixBufferVolume);
+ }
+
+ NeedToUpdateInternalState = false;
+ }
+
+ /// <summary>
+ /// Set the update internal state marker.
+ /// </summary>
+ public void MarkAsNeedToUpdateInternalState()
+ {
+ NeedToUpdateInternalState = true;
+ }
+
+ /// <summary>
+ /// Return true if the <see cref="SplitterDestinationVersion1"/> is used and has a destination.
+ /// </summary>
+ /// <returns>True if the <see cref="SplitterDestinationVersion1"/> is used and has a destination.</returns>
+ public readonly bool IsConfigured()
+ {
+ return IsUsed && DestinationId != Constants.UnusedMixId;
+ }
+
+ /// <summary>
+ /// Get the volume for a given destination.
+ /// </summary>
+ /// <param name="destinationIndex">The destination index to use.</param>
+ /// <returns>The volume for the given destination.</returns>
+ public float GetMixVolume(int destinationIndex)
+ {
+ Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
+
+ return MixBufferVolume[destinationIndex];
+ }
+
+ /// <summary>
+ /// Get the previous volume for a given destination.
+ /// </summary>
+ /// <param name="destinationIndex">The destination index to use.</param>
+ /// <returns>The volume for the given destination.</returns>
+ public float GetMixVolumePrev(int destinationIndex)
+ {
+ Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
+
+ return PreviousMixBufferVolume[destinationIndex];
+ }
+
+ /// <summary>
+ /// Clear the volumes.
+ /// </summary>
+ public void ClearVolumes()
+ {
+ MixBufferVolume.Clear();
+ PreviousMixBufferVolume.Clear();
+ }
+
+ /// <summary>
+ /// Link the next element to the given <see cref="SplitterDestinationVersion1"/>.
+ /// </summary>
+ /// <param name="next">The given <see cref="SplitterDestinationVersion1"/> to link.</param>
+ public void Link(ref SplitterDestinationVersion1 next)
+ {
+ unsafe
+ {
+ fixed (SplitterDestinationVersion1* nextPtr = &next)
+ {
+ _next = nextPtr;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Remove the link to the next element.
+ /// </summary>
+ public void Unlink()
+ {
+ unsafe
+ {
+ _next = null;
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs
new file mode 100644
index 00000000..f9487909
--- /dev/null
+++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs
@@ -0,0 +1,250 @@
+using Ryujinx.Audio.Renderer.Parameter;
+using Ryujinx.Common.Memory;
+using Ryujinx.Common.Utilities;
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Server.Splitter
+{
+ /// <summary>
+ /// Server state for a splitter destination (version 2).
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Size = 0x110, Pack = Alignment)]
+ public struct SplitterDestinationVersion2
+ {
+ public const int Alignment = 0x10;
+
+ /// <summary>
+ /// The unique id of this <see cref="SplitterDestinationVersion2"/>.
+ /// </summary>
+ public int Id;
+
+ /// <summary>
+ /// The mix to output the result of the splitter.
+ /// </summary>
+ public int DestinationId;
+
+ /// <summary>
+ /// Mix buffer volumes storage.
+ /// </summary>
+ private MixArray _mix;
+ private MixArray _previousMix;
+
+ /// <summary>
+ /// Pointer to the next linked element.
+ /// </summary>
+ private unsafe SplitterDestinationVersion2* _next;
+
+ /// <summary>
+ /// Set to true if in use.
+ /// </summary>
+ [MarshalAs(UnmanagedType.I1)]
+ public bool IsUsed;
+
+ /// <summary>
+ /// Set to true if the internal state need to be updated.
+ /// </summary>
+ [MarshalAs(UnmanagedType.I1)]
+ public bool NeedToUpdateInternalState;
+
+ [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
+ private struct MixArray { }
+
+ /// <summary>
+ /// Mix buffer volumes.
+ /// </summary>
+ /// <remarks>Used when a splitter id is specified in the mix.</remarks>
+ public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _mix);
+
+ /// <summary>
+ /// Previous mix buffer volumes.
+ /// </summary>
+ /// <remarks>Used when a splitter id is specified in the mix.</remarks>
+ public Span<float> PreviousMixBufferVolume => SpanHelpers.AsSpan<MixArray, float>(ref _previousMix);
+
+ /// <summary>
+ /// Get the reference of the next element or null if not present.
+ /// </summary>
+ public readonly ref SplitterDestinationVersion2 Next
+ {
+ get
+ {
+ unsafe
+ {
+ return ref Unsafe.AsRef<SplitterDestinationVersion2>(_next);
+ }
+ }
+ }
+
+ private Array2<BiquadFilterParameter> _biquadFilters;
+
+ private Array2<bool> _isPreviousBiquadFilterEnabled;
+
+ /// <summary>
+ /// Create a new <see cref="SplitterDestinationVersion2"/>.
+ /// </summary>
+ /// <param name="id">The unique id of this <see cref="SplitterDestinationVersion2"/>.</param>
+ public SplitterDestinationVersion2(int id) : this()
+ {
+ Id = id;
+ DestinationId = Constants.UnusedMixId;
+
+ ClearVolumes();
+ }
+
+ /// <summary>
+ /// Update the <see cref="SplitterDestinationVersion2"/> from user parameter.
+ /// </summary>
+ /// <param name="parameter">The user parameter.</param>
+ public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
+ {
+ Debug.Assert(Id == parameter.Id);
+
+ if (parameter.IsMagicValid() && Id == parameter.Id)
+ {
+ DestinationId = parameter.DestinationId;
+
+ parameter.MixBufferVolume.CopyTo(MixBufferVolume);
+
+ _biquadFilters = parameter.BiquadFilters;
+
+ if (!IsUsed && parameter.IsUsed)
+ {
+ MixBufferVolume.CopyTo(PreviousMixBufferVolume);
+
+ NeedToUpdateInternalState = false;
+ }
+
+ IsUsed = parameter.IsUsed;
+ }
+ }
+
+ /// <summary>
+ /// Update the internal state of the instance.
+ /// </summary>
+ public void UpdateInternalState()
+ {
+ if (IsUsed && NeedToUpdateInternalState)
+ {
+ MixBufferVolume.CopyTo(PreviousMixBufferVolume);
+ }
+
+ NeedToUpdateInternalState = false;
+ }
+
+ /// <summary>
+ /// Set the update internal state marker.
+ /// </summary>
+ public void MarkAsNeedToUpdateInternalState()
+ {
+ NeedToUpdateInternalState = true;
+ }
+
+ /// <summary>
+ /// Return true if the <see cref="SplitterDestinationVersion2"/> is used and has a destination.
+ /// </summary>
+ /// <returns>True if the <see cref="SplitterDestinationVersion2"/> is used and has a destination.</returns>
+ public readonly bool IsConfigured()
+ {
+ return IsUsed && DestinationId != Constants.UnusedMixId;
+ }
+
+ /// <summary>
+ /// Get the volume for a given destination.
+ /// </summary>
+ /// <param name="destinationIndex">The destination index to use.</param>
+ /// <returns>The volume for the given destination.</returns>
+ public float GetMixVolume(int destinationIndex)
+ {
+ Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
+
+ return MixBufferVolume[destinationIndex];
+ }
+
+ /// <summary>
+ /// Get the previous volume for a given destination.
+ /// </summary>
+ /// <param name="destinationIndex">The destination index to use.</param>
+ /// <returns>The volume for the given destination.</returns>
+ public float GetMixVolumePrev(int destinationIndex)
+ {
+ Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
+
+ return PreviousMixBufferVolume[destinationIndex];
+ }
+
+ /// <summary>
+ /// Clear the volumes.
+ /// </summary>
+ public void ClearVolumes()
+ {
+ MixBufferVolume.Clear();
+ PreviousMixBufferVolume.Clear();
+ }
+
+ /// <summary>
+ /// Link the next element to the given <see cref="SplitterDestinationVersion2"/>.
+ /// </summary>
+ /// <param name="next">The given <see cref="SplitterDestinationVersion2"/> to link.</param>
+ public void Link(ref SplitterDestinationVersion2 next)
+ {
+ unsafe
+ {
+ fixed (SplitterDestinationVersion2* nextPtr = &next)
+ {
+ _next = nextPtr;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Remove the link to the next element.
+ /// </summary>
+ public void Unlink()
+ {
+ unsafe
+ {
+ _next = null;
+ }
+ }
+
+ /// <summary>
+ /// Checks if any biquad filter is enabled.
+ /// </summary>
+ /// <returns>True if any biquad filter is enabled.</returns>
+ public bool IsBiquadFilterEnabled()
+ {
+ return _biquadFilters[0].Enable || _biquadFilters[1].Enable;
+ }
+
+ /// <summary>
+ /// Checks if any biquad filter was previously enabled.
+ /// </summary>
+ /// <returns>True if any biquad filter was previously enabled.</returns>
+ public bool IsBiquadFilterEnabledPrev()
+ {
+ return _isPreviousBiquadFilterEnabled[0];
+ }
+
+ /// <summary>
+ /// Gets the biquad filter parameters.
+ /// </summary>
+ /// <param name="index">Biquad filter index (0 or 1).</param>
+ /// <returns>Biquad filter parameters.</returns>
+ public ref BiquadFilterParameter GetBiquadFilterParameter(int index)
+ {
+ return ref _biquadFilters[index];
+ }
+
+ /// <summary>
+ /// Checks if any biquad filter was previously enabled.
+ /// </summary>
+ /// <param name="index">Biquad filter index (0 or 1).</param>
+ public void UpdateBiquadFilterEnabledPrev(int index)
+ {
+ _isPreviousBiquadFilterEnabled[index] = _biquadFilters[index].Enable;
+ }
+ }
+}
diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs
index 944f092d..3e7dce55 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs
@@ -15,6 +15,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
public const int Alignment = 0x10;
+ private delegate void SplitterDestinationAction(SplitterDestination destination, int index);
+
/// <summary>
/// The unique id of this <see cref="SplitterState"/>.
/// </summary>
@@ -26,7 +28,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
public uint SampleRate;
/// <summary>
- /// Count of splitter destinations (<see cref="SplitterDestination"/>).
+ /// Count of splitter destinations.
/// </summary>
public int DestinationCount;
@@ -37,20 +39,25 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
public bool HasNewConnection;
/// <summary>
- /// Linked list of <see cref="SplitterDestination"/>.
+ /// Linked list of <see cref="SplitterDestinationVersion1"/>.
+ /// </summary>
+ private unsafe SplitterDestinationVersion1* _destinationDataV1;
+
+ /// <summary>
+ /// Linked list of <see cref="SplitterDestinationVersion2"/>.
/// </summary>
- private unsafe SplitterDestination* _destinationsData;
+ private unsafe SplitterDestinationVersion2* _destinationDataV2;
/// <summary>
- /// Span to the first element of the linked list of <see cref="SplitterDestination"/>.
+ /// First element of the linked list of splitter destinations data.
/// </summary>
- public readonly Span<SplitterDestination> Destinations
+ public readonly SplitterDestination Destination
{
get
{
unsafe
{
- return (IntPtr)_destinationsData != IntPtr.Zero ? new Span<SplitterDestination>(_destinationsData, 1) : Span<SplitterDestination>.Empty;
+ return new SplitterDestination(_destinationDataV1, _destinationDataV2);
}
}
}
@@ -64,20 +71,20 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
Id = id;
}
- public readonly Span<SplitterDestination> GetData(int index)
+ public readonly SplitterDestination GetData(int index)
{
int i = 0;
- Span<SplitterDestination> result = Destinations;
+ SplitterDestination result = Destination;
while (i < index)
{
- if (result.IsEmpty)
+ if (result.IsNull)
{
break;
}
- result = result[0].Next;
+ result = result.Next;
i++;
}
@@ -93,25 +100,25 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
}
/// <summary>
- /// Utility function to apply a given <see cref="SpanAction{T, TArg}"/> to all <see cref="Destinations"/>.
+ /// Utility function to apply an action to all <see cref="Destination"/>.
/// </summary>
/// <param name="action">The action to execute on each elements.</param>
- private readonly void ForEachDestination(SpanAction<SplitterDestination, int> action)
+ private readonly void ForEachDestination(SplitterDestinationAction action)
{
- Span<SplitterDestination> temp = Destinations;
+ SplitterDestination temp = Destination;
int i = 0;
while (true)
{
- if (temp.IsEmpty)
+ if (temp.IsNull)
{
break;
}
- Span<SplitterDestination> next = temp[0].Next;
+ SplitterDestination next = temp.Next;
- action.Invoke(temp, i++);
+ action(temp, i++);
temp = next;
}
@@ -142,9 +149,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
input.ReadLittleEndian(out int destinationId);
- Memory<SplitterDestination> destination = context.GetDestinationMemory(destinationId);
+ SplitterDestination destination = context.GetDestination(destinationId);
- SetDestination(ref destination.Span[0]);
+ SetDestination(destination);
DestinationCount = destinationCount;
@@ -152,9 +159,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
input.ReadLittleEndian(out destinationId);
- Memory<SplitterDestination> nextDestination = context.GetDestinationMemory(destinationId);
+ SplitterDestination nextDestination = context.GetDestination(destinationId);
- destination.Span[0].Link(ref nextDestination.Span[0]);
+ destination.Link(nextDestination);
destination = nextDestination;
}
}
@@ -174,16 +181,21 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
}
/// <summary>
- /// Set the head of the linked list of <see cref="Destinations"/>.
+ /// Set the head of the linked list of <see cref="Destination"/>.
/// </summary>
- /// <param name="newValue">A reference to a <see cref="SplitterDestination"/>.</param>
- public void SetDestination(ref SplitterDestination newValue)
+ /// <param name="newValue">New destination value.</param>
+ public void SetDestination(SplitterDestination newValue)
{
unsafe
{
- fixed (SplitterDestination* newValuePtr = &newValue)
+ fixed (SplitterDestinationVersion1* newValuePtr = &newValue.GetV1RefOrNull())
+ {
+ _destinationDataV1 = newValuePtr;
+ }
+
+ fixed (SplitterDestinationVersion2* newValuePtr = &newValue.GetV2RefOrNull())
{
- _destinationsData = newValuePtr;
+ _destinationDataV2 = newValuePtr;
}
}
}
@@ -193,19 +205,20 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary>
public readonly void UpdateInternalState()
{
- ForEachDestination((destination, _) => destination[0].UpdateInternalState());
+ ForEachDestination((destination, _) => destination.UpdateInternalState());
}
/// <summary>
- /// Clear all links from the <see cref="Destinations"/>.
+ /// Clear all links from the <see cref="Destination"/>.
/// </summary>
public void ClearLinks()
{
- ForEachDestination((destination, _) => destination[0].Unlink());
+ ForEachDestination((destination, _) => destination.Unlink());
unsafe
{
- _destinationsData = (SplitterDestination*)IntPtr.Zero;
+ _destinationDataV1 = null;
+ _destinationDataV2 = null;
}
}
@@ -219,7 +232,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
unsafe
{
- splitter._destinationsData = (SplitterDestination*)IntPtr.Zero;
+ splitter._destinationDataV1 = null;
+ splitter._destinationDataV2 = null;
}
splitter.DestinationCount = 0;