//
// Copyright (c) 2019-2021 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 .
//
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
{
///
/// Mix context.
///
public class MixContext
{
///
/// The total mix count.
///
private uint _mixesCount;
///
/// Storage for .
///
private Memory _mixes;
///
/// Storage of the sorted indices to .
///
private Memory _sortedMixes;
///
/// Graph state.
///
public NodeStates NodeStates { get; }
///
/// The instance of the adjacent matrix.
///
public EdgeMatrix EdgeMatrix { get; }
///
/// Create a new instance of .
///
public MixContext()
{
NodeStates = new NodeStates();
EdgeMatrix = new EdgeMatrix();
}
///
/// Initialize the .
///
/// The storage for sorted indices.
/// The storage of .
/// The storage used for the .
/// The storage used for the .
public void Initialize(Memory sortedMixes, Memory mixes, Memory nodeStatesWorkBuffer, Memory 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);
}
}
///
/// Associate the given to a given .
///
/// The sorted id.
/// The index to associate.
private void SetSortedState(int id, int targetIndex)
{
_sortedMixes.Span[id] = targetIndex;
}
///
/// Get a reference to the final .
///
/// A reference to the final .
public ref MixState GetFinalState()
{
return ref GetState(Constants.FinalMixId);
}
///
/// Get a reference to a at the given .
///
/// The index to use.
/// A reference to a at the given .
public ref MixState GetState(int id)
{
return ref SpanIOHelper.GetFromMemory(_mixes, id, _mixesCount);
}
///
/// Get a reference to a at the given of the sorted mix info.
///
/// The index to use.
/// A reference to a at the given .
public ref MixState GetSortedState(int id)
{
Debug.Assert(id >= 0 && id < _mixesCount);
return ref GetState(_sortedMixes.Span[id]);
}
///
/// Get the total mix count.
///
/// The total mix count.
public uint GetCount()
{
return _mixesCount;
}
///
/// Update the internal distance from the final mix value of every .
///
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 != Constants.FinalMixId)
{
int mixId = mix.MixId;
for (distance = 0; distance < GetCount(); distance++)
{
if (mixId == Constants.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 == Constants.FinalMixId)
{
break;
}
}
if (distance > GetCount())
{
distance = MixState.InvalidDistanceFromFinalMix;
}
}
else
{
distance = MixState.InvalidDistanceFromFinalMix;
}
mix.DistanceFromFinalMix = distance;
}
}
}
///
/// Update the internal mix buffer offset of all .
///
private void UpdateMixBufferOffset()
{
uint offset = 0;
foreach (ref MixState mix in _mixes.Span)
{
mix.BufferOffset = offset;
offset += mix.BufferCount;
}
}
///
/// Sort the mixes using distance from the final mix.
///
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();
}
///
/// Sort the mixes and splitters using an adjacency matrix.
///
/// The used.
/// Return true, if no errors in the graph were detected.
public bool Sort(SplitterContext splitterContext)
{
if (splitterContext.UsingSplitter())
{
bool isValid = NodeStates.Sort(EdgeMatrix);
if (isValid)
{
ReadOnlySpan 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;
}
}
}
}