diff options
Diffstat (limited to 'Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs')
-rw-r--r-- | Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs | 276 |
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; + } + } + } +} |