aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs')
-rw-r--r--Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs276
1 files changed, 276 insertions, 0 deletions
diff --git a/Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs b/Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs
new file mode 100644
index 00000000..46c244bb
--- /dev/null
+++ b/Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs
@@ -0,0 +1,276 @@
+//
+// 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.Common;
+using Ryujinx.Audio.Renderer.Server.Splitter;
+using Ryujinx.Audio.Renderer.Utils;
+using System;
+using System.Diagnostics;
+
+namespace Ryujinx.Audio.Renderer.Server.Mix
+{
+ /// <summary>
+ /// Mix context.
+ /// </summary>
+ public class MixContext
+ {
+ /// <summary>
+ /// The total mix count.
+ /// </summary>
+ private uint _mixesCount;
+
+ /// <summary>
+ /// Storage for <see cref="MixState"/>.
+ /// </summary>
+ private Memory<MixState> _mixes;
+
+ /// <summary>
+ /// Storage of the sorted indices to <see cref="MixState"/>.
+ /// </summary>
+ private Memory<int> _sortedMixes;
+
+ /// <summary>
+ /// Graph state.
+ /// </summary>
+ public NodeStates NodeStates { get; }
+
+ /// <summary>
+ /// The instance of the adjacent matrix.
+ /// </summary>
+ public EdgeMatrix EdgeMatrix { get; }
+
+ /// <summary>
+ /// Create a new instance of <see cref="MixContext"/>.
+ /// </summary>
+ public MixContext()
+ {
+ NodeStates = new NodeStates();
+ EdgeMatrix = new EdgeMatrix();
+ }
+
+ /// <summary>
+ /// Initialize the <see cref="MixContext"/>.
+ /// </summary>
+ /// <param name="sortedMixes">The storage for sorted indices.</param>
+ /// <param name="mixes">The storage of <see cref="MixState"/>.</param>
+ /// <param name="nodeStatesWorkBuffer">The storage used for the <see cref="NodeStates"/>.</param>
+ /// <param name="edgeMatrixWorkBuffer">The storage used for the <see cref="EdgeMatrix"/>.</param>
+ public void Initialize(Memory<int> sortedMixes, Memory<MixState> mixes, Memory<byte> nodeStatesWorkBuffer, Memory<byte> edgeMatrixWorkBuffer)
+ {
+ _mixesCount = (uint)mixes.Length;
+ _mixes = mixes;
+ _sortedMixes = sortedMixes;
+
+ if (!nodeStatesWorkBuffer.IsEmpty && !edgeMatrixWorkBuffer.IsEmpty)
+ {
+ NodeStates.Initialize(nodeStatesWorkBuffer, mixes.Length);
+ EdgeMatrix.Initialize(edgeMatrixWorkBuffer, mixes.Length);
+ }
+
+ int sortedId = 0;
+ for (int i = 0; i < _mixes.Length; i++)
+ {
+ SetSortedState(sortedId++, i);
+ }
+ }
+
+ /// <summary>
+ /// Associate the given <paramref name="targetIndex"/> to a given <paramref cref="id"/>.
+ /// </summary>
+ /// <param name="id">The sorted id.</param>
+ /// <param name="targetIndex">The index to associate.</param>
+ private void SetSortedState(int id, int targetIndex)
+ {
+ _sortedMixes.Span[id] = targetIndex;
+ }
+
+ /// <summary>
+ /// Get a reference to the final <see cref="MixState"/>.
+ /// </summary>
+ /// <returns>A reference to the final <see cref="MixState"/>.</returns>
+ public ref MixState GetFinalState()
+ {
+ return ref GetState(RendererConstants.FinalMixId);
+ }
+
+ /// <summary>
+ /// Get a reference to a <see cref="MixState"/> at the given <paramref name="id"/>.
+ /// </summary>
+ /// <param name="id">The index to use.</param>
+ /// <returns>A reference to a <see cref="MixState"/> at the given <paramref name="id"/>.</returns>
+ public ref MixState GetState(int id)
+ {
+ return ref SpanIOHelper.GetFromMemory(_mixes, id, _mixesCount);
+ }
+
+ /// <summary>
+ /// Get a reference to a <see cref="MixState"/> at the given <paramref name="id"/> of the sorted mix info.
+ /// </summary>
+ /// <param name="id">The index to use.</param>
+ /// <returns>A reference to a <see cref="MixState"/> at the given <paramref name="id"/>.</returns>
+ public ref MixState GetSortedState(int id)
+ {
+ Debug.Assert(id >= 0 && id < _mixesCount);
+
+ return ref GetState(_sortedMixes.Span[id]);
+ }
+
+ /// <summary>
+ /// Get the total mix count.
+ /// </summary>
+ /// <returns>The total mix count.</returns>
+ public uint GetCount()
+ {
+ return _mixesCount;
+ }
+
+ /// <summary>
+ /// Update the internal distance from the final mix value of every <see cref="MixState"/>.
+ /// </summary>
+ private void UpdateDistancesFromFinalMix()
+ {
+ foreach (ref MixState mix in _mixes.Span)
+ {
+ mix.ClearDistanceFromFinalMix();
+ }
+
+ for (int i = 0; i < GetCount(); i++)
+ {
+ ref MixState mix = ref GetState(i);
+
+ SetSortedState(i, i);
+
+ if (mix.IsUsed)
+ {
+ uint distance;
+
+ if (mix.MixId != RendererConstants.FinalMixId)
+ {
+ int mixId = mix.MixId;
+
+ for (distance = 0; distance < GetCount(); distance++)
+ {
+ if (mixId == RendererConstants.UnusedMixId)
+ {
+ distance = MixState.InvalidDistanceFromFinalMix;
+ break;
+ }
+
+ ref MixState distanceMix = ref GetState(mixId);
+
+ if (distanceMix.DistanceFromFinalMix != MixState.InvalidDistanceFromFinalMix)
+ {
+ distance = distanceMix.DistanceFromFinalMix + 1;
+ break;
+ }
+
+ mixId = distanceMix.DestinationMixId;
+
+ if (mixId == RendererConstants.FinalMixId)
+ {
+ break;
+ }
+ }
+
+ if (distance > GetCount())
+ {
+ distance = MixState.InvalidDistanceFromFinalMix;
+ }
+ }
+ else
+ {
+ distance = MixState.InvalidDistanceFromFinalMix;
+ }
+
+ mix.DistanceFromFinalMix = distance;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Update the internal mix buffer offset of all <see cref="MixState"/>.
+ /// </summary>
+ private void UpdateMixBufferOffset()
+ {
+ uint offset = 0;
+
+ foreach (ref MixState mix in _mixes.Span)
+ {
+ mix.BufferOffset = offset;
+
+ offset += mix.BufferCount;
+ }
+ }
+
+ /// <summary>
+ /// Sort the mixes using distance from the final mix.
+ /// </summary>
+ public void Sort()
+ {
+ UpdateDistancesFromFinalMix();
+
+ int[] sortedMixesTemp = _sortedMixes.Slice(0, (int)GetCount()).ToArray();
+
+ Array.Sort(sortedMixesTemp, (a, b) =>
+ {
+ ref MixState stateA = ref GetState(a);
+ ref MixState stateB = ref GetState(b);
+
+ return stateB.DistanceFromFinalMix.CompareTo(stateA.DistanceFromFinalMix);
+ });
+
+ sortedMixesTemp.AsSpan().CopyTo(_sortedMixes.Span);
+
+ UpdateMixBufferOffset();
+ }
+
+ /// <summary>
+ /// Sort the mixes and splitters using an adjacency matrix.
+ /// </summary>
+ /// <param name="splitterContext">The <see cref="SplitterContext"/> used.</param>
+ /// <returns>Return true, if no errors in the graph were detected.</returns>
+ public bool Sort(SplitterContext splitterContext)
+ {
+ if (splitterContext.UsingSplitter())
+ {
+ bool isValid = NodeStates.Sort(EdgeMatrix);
+
+ if (isValid)
+ {
+ ReadOnlySpan<int> sortedMixesIndex = NodeStates.GetTsortResult();
+
+ int id = 0;
+
+ for (int i = sortedMixesIndex.Length - 1; i >= 0; i--)
+ {
+ SetSortedState(id++, sortedMixesIndex[i]);
+ }
+
+ UpdateMixBufferOffset();
+ }
+
+ return isValid;
+ }
+ else
+ {
+ UpdateMixBufferOffset();
+
+ return true;
+ }
+ }
+ }
+}