aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Audio.Renderer/Server/Splitter/SplitterState.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Audio.Renderer/Server/Splitter/SplitterState.cs')
-rw-r--r--Ryujinx.Audio.Renderer/Server/Splitter/SplitterState.cs237
1 files changed, 237 insertions, 0 deletions
diff --git a/Ryujinx.Audio.Renderer/Server/Splitter/SplitterState.cs b/Ryujinx.Audio.Renderer/Server/Splitter/SplitterState.cs
new file mode 100644
index 00000000..f86e75dd
--- /dev/null
+++ b/Ryujinx.Audio.Renderer/Server/Splitter/SplitterState.cs
@@ -0,0 +1,237 @@
+//
+// Copyright (c) 2019-2020 Ryujinx
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+//
+using Ryujinx.Audio.Renderer.Parameter;
+using System;
+using System.Buffers;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Renderer.Server.Splitter
+{
+ /// <summary>
+ /// Server state for a splitter.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = Alignment)]
+ public struct SplitterState
+ {
+ public const int Alignment = 0x10;
+
+ /// <summary>
+ /// The unique id of this <see cref="SplitterState"/>.
+ /// </summary>
+ public int Id;
+
+ /// <summary>
+ /// Target sample rate to use on the splitter.
+ /// </summary>
+ public uint SampleRate;
+
+ /// <summary>
+ /// Count of splitter destinations (<see cref="SplitterDestination"/>).
+ /// </summary>
+ public int DestinationCount;
+
+ /// <summary>
+ /// Set to true if the splitter has a new connection.
+ /// </summary>
+ [MarshalAs(UnmanagedType.I1)]
+ public bool HasNewConnection;
+
+ /// <summary>
+ /// Linked list of <see cref="SplitterDestination"/>.
+ /// </summary>
+ private unsafe SplitterDestination* _destinationsData;
+
+ /// <summary>
+ /// Span to the first element of the linked list of <see cref="SplitterDestination"/>.
+ /// </summary>
+ public Span<SplitterDestination> Destinations
+ {
+ get
+ {
+ unsafe
+ {
+ return (IntPtr)_destinationsData != IntPtr.Zero ? new Span<SplitterDestination>(_destinationsData, 1) : Span<SplitterDestination>.Empty;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Create a new <see cref="SplitterState"/>.
+ /// </summary>
+ /// <param name="id">The unique id of this <see cref="SplitterState"/>.</param>
+ public SplitterState(int id) : this()
+ {
+ Id = id;
+ }
+
+ public Span<SplitterDestination> GetData(int index)
+ {
+ int i = 0;
+
+ Span<SplitterDestination> result = Destinations;
+
+ while (i < index)
+ {
+ if (result.IsEmpty)
+ {
+ break;
+ }
+
+ result = result[0].Next;
+ i++;
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Clear the new connection flag.
+ /// </summary>
+ public void ClearNewConnectionFlag()
+ {
+ HasNewConnection = false;
+ }
+
+ /// <summary>
+ /// Utility function to apply a given <see cref="SpanAction{T, TArg}"/> to all <see cref="Destinations"/>.
+ /// </summary>
+ /// <param name="action">The action to execute on each elements.</param>
+ private void ForEachDestination(SpanAction<SplitterDestination, int> action)
+ {
+ Span<SplitterDestination> temp = Destinations;
+
+ int i = 0;
+
+ while (true)
+ {
+ if (temp.IsEmpty)
+ {
+ break;
+ }
+
+ Span<SplitterDestination> next = temp[0].Next;
+
+ action.Invoke(temp, i++);
+
+ temp = next;
+ }
+ }
+
+ /// <summary>
+ /// Update the <see cref="SplitterState"/> from user parameter.
+ /// </summary>
+ /// <param name="context">The splitter context.</param>
+ /// <param name="parameter">The user parameter.</param>
+ /// <param name="input">The raw input data after the <paramref name="parameter"/>.</param>
+ public void Update(SplitterContext context, ref SplitterInParameter parameter, ReadOnlySpan<byte> input)
+ {
+ ClearLinks();
+
+ int destinationCount;
+
+ if (context.IsBugFixed)
+ {
+ destinationCount = parameter.DestinationCount;
+ }
+ else
+ {
+ destinationCount = Math.Min(context.GetDestinationCountPerStateForCompatibility(), parameter.DestinationCount);
+ }
+
+ if (destinationCount > 0)
+ {
+ ReadOnlySpan<int> destinationIds = MemoryMarshal.Cast<byte, int>(input);
+
+ Memory<SplitterDestination> destination = context.GetDestinationMemory(destinationIds[0]);
+
+ SetDestination(ref destination.Span[0]);
+
+ DestinationCount = destinationCount;
+
+ for (int i = 1; i < destinationCount; i++)
+ {
+ Memory<SplitterDestination> nextDestination = context.GetDestinationMemory(destinationIds[i]);
+
+ destination.Span[0].Link(ref nextDestination.Span[0]);
+ destination = nextDestination;
+ }
+ }
+
+ Debug.Assert(parameter.Id == Id);
+
+ if (parameter.Id == Id)
+ {
+ SampleRate = parameter.SampleRate;
+ HasNewConnection = true;
+ }
+ }
+
+ /// <summary>
+ /// Set the head of the linked list of <see cref="Destinations"/>.
+ /// </summary>
+ /// <param name="newValue">A reference to a <see cref="SplitterDestination"/>.</param>
+ public void SetDestination(ref SplitterDestination newValue)
+ {
+ unsafe
+ {
+ fixed (SplitterDestination* newValuePtr = &newValue)
+ {
+ _destinationsData = newValuePtr;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Update the internal state of this instance.
+ /// </summary>
+ public void UpdateInternalState()
+ {
+ ForEachDestination((destination, _) => destination[0].UpdateInternalState());
+ }
+
+ /// <summary>
+ /// Clear all links from the <see cref="Destinations"/>.
+ /// </summary>
+ public void ClearLinks()
+ {
+ ForEachDestination((destination, _) => destination[0].Unlink());
+
+ unsafe
+ {
+ _destinationsData = (SplitterDestination*)IntPtr.Zero;
+ }
+ }
+
+ /// <summary>
+ /// Initialize a given <see cref="Span{SplitterState}"/>.
+ /// </summary>
+ /// <param name="splitters">All the <see cref="SplitterState"/> to initialize.</param>
+ public static void InitializeSplitters(Span<SplitterState> splitters)
+ {
+ foreach (ref SplitterState splitter in splitters)
+ {
+ unsafe
+ {
+ splitter._destinationsData = (SplitterDestination*)IntPtr.Zero;
+ }
+
+ splitter.DestinationCount = 0;
+ }
+ }
+ }
+}