diff options
Diffstat (limited to 'Ryujinx.Audio/Renderer/Common')
18 files changed, 1320 insertions, 0 deletions
diff --git a/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs b/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs new file mode 100644 index 00000000..5d826569 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs @@ -0,0 +1,30 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Common +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct AuxiliaryBufferAddresses + { + public ulong SendBufferInfo; + public ulong SendBufferInfoBase; + public ulong ReturnBufferInfo; + public ulong ReturnBufferInfoBase; + } +} diff --git a/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs b/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs new file mode 100644 index 00000000..0de3d571 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs @@ -0,0 +1,67 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// Represents the input parameter for <see cref="Server.BehaviourContext"/>. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct BehaviourParameter + { + /// <summary> + /// The current audio renderer revision in use. + /// </summary> + public int UserRevision; + + /// <summary> + /// Reserved/padding. + /// </summary> + private uint _padding; + + /// <summary> + /// The flags given controlling behaviour of the audio renderer + /// </summary> + /// <remarks>See <see cref="Server.BehaviourContext.UpdateFlags(ulong)"/> and <see cref="Server.BehaviourContext.IsMemoryPoolForceMappingEnabled"/>.</remarks> + public ulong Flags; + + /// <summary> + /// Represents an error during <see cref="Server.AudioRenderSystem.Update(System.Memory{byte}, System.Memory{byte}, System.ReadOnlyMemory{byte})"/>. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ErrorInfo + { + /// <summary> + /// The error code to report. + /// </summary> + public ResultCode ErrorCode; + + /// <summary> + /// Reserved/padding. + /// </summary> + private uint _padding; + + /// <summary> + /// Extra information given with the <see cref="ResultCode"/> + /// </summary> + /// <remarks>This is usually used to report a faulting cpu address when a <see cref="Server.MemoryPool.MemoryPoolState"/> mapping fail.</remarks> + public ulong ExtraErrorInfo; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs b/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs new file mode 100644 index 00000000..7423eda4 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs @@ -0,0 +1,167 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Utils; +using Ryujinx.Common; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// Represents a adjacent matrix. + /// </summary> + /// <remarks>This is used for splitter routing.</remarks> + public class EdgeMatrix + { + /// <summary> + /// Backing <see cref="BitArray"/> used for node connections. + /// </summary> + private BitArray _storage; + + /// <summary> + /// The count of nodes of the current instance. + /// </summary> + private int _nodeCount; + + /// <summary> + /// Get the required work buffer size memory needed for the <see cref="EdgeMatrix"/>. + /// </summary> + /// <param name="nodeCount">The count of nodes.</param> + /// <returns>The size required for the given <paramref name="nodeCount"/>.</returns> + public static int GetWorkBufferSize(int nodeCount) + { + int size = BitUtils.AlignUp(nodeCount * nodeCount, Constants.BufferAlignment); + + return size / Unsafe.SizeOf<byte>(); + } + + /// <summary> + /// Initializes the <see cref="EdgeMatrix"/> instance with backing memory. + /// </summary> + /// <param name="edgeMatrixWorkBuffer">The backing memory.</param> + /// <param name="nodeCount">The count of nodes.</param> + public void Initialize(Memory<byte> edgeMatrixWorkBuffer, int nodeCount) + { + Debug.Assert(edgeMatrixWorkBuffer.Length >= GetWorkBufferSize(nodeCount)); + + _storage = new BitArray(edgeMatrixWorkBuffer); + + _nodeCount = nodeCount; + + _storage.Reset(); + } + + /// <summary> + /// Test if the bit at the given index is set. + /// </summary> + /// <param name="index">A bit index.</param> + /// <returns>Returns true if the bit at the given index is set</returns> + public bool Test(int index) + { + return _storage.Test(index); + } + + /// <summary> + /// Reset all bits in the storage. + /// </summary> + public void Reset() + { + _storage.Reset(); + } + + /// <summary> + /// Reset the bit at the given index. + /// </summary> + /// <param name="index">A bit index.</param> + public void Reset(int index) + { + _storage.Reset(index); + } + + /// <summary> + /// Set the bit at the given index. + /// </summary> + /// <param name="index">A bit index.</param> + public void Set(int index) + { + _storage.Set(index); + } + + /// <summary> + /// Connect a given source to a given destination. + /// </summary> + /// <param name="source">The source index.</param> + /// <param name="destination">The destination index.</param> + public void Connect(int source, int destination) + { + Debug.Assert(source < _nodeCount); + Debug.Assert(destination < _nodeCount); + + _storage.Set(_nodeCount * source + destination); + } + + /// <summary> + /// Check if the given source is connected to the given destination. + /// </summary> + /// <param name="source">The source index.</param> + /// <param name="destination">The destination index.</param> + /// <returns>Returns true if the given source is connected to the given destination.</returns> + public bool Connected(int source, int destination) + { + Debug.Assert(source < _nodeCount); + Debug.Assert(destination < _nodeCount); + + return _storage.Test(_nodeCount * source + destination); + } + + /// <summary> + /// Disconnect a given source from a given destination. + /// </summary> + /// <param name="source">The source index.</param> + /// <param name="destination">The destination index.</param> + public void Disconnect(int source, int destination) + { + Debug.Assert(source < _nodeCount); + Debug.Assert(destination < _nodeCount); + + _storage.Reset(_nodeCount * source + destination); + } + + /// <summary> + /// Remove all edges from a given source. + /// </summary> + /// <param name="source">The source index.</param> + public void RemoveEdges(int source) + { + for (int i = 0; i < _nodeCount; i++) + { + Disconnect(source, i); + } + } + + /// <summary> + /// Get the total node count. + /// </summary> + /// <returns>The total node count.</returns> + public int GetNodeCount() + { + return _nodeCount; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Common/EffectType.cs b/Ryujinx.Audio/Renderer/Common/EffectType.cs new file mode 100644 index 00000000..082f94f8 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/EffectType.cs @@ -0,0 +1,60 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// The type of an effect. + /// </summary> + public enum EffectType : byte + { + /// <summary> + /// Invalid effect. + /// </summary> + Invalid, + + /// <summary> + /// Effect applying additional mixing capability. + /// </summary> + BufferMix, + + /// <summary> + /// Effect applying custom user effect (via auxiliary buffers). + /// </summary> + AuxiliaryBuffer, + + /// <summary> + /// Effect applying a delay. + /// </summary> + Delay, + + /// <summary> + /// Effect applying a reverberation effect via a given preset. + /// </summary> + Reverb, + + /// <summary> + /// Effect applying a 3D reverberation effect via a given preset. + /// </summary> + Reverb3d, + + /// <summary> + /// Effect applying a biquad filter. + /// </summary> + BiquadFilter + } +} diff --git a/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs b/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs new file mode 100644 index 00000000..7f9765d9 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs @@ -0,0 +1,60 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// Represents the state of a memory pool. + /// </summary> + public enum MemoryPoolUserState : uint + { + /// <summary> + /// Invalid state. + /// </summary> + Invalid = 0, + + /// <summary> + /// The memory pool is new. (client side only) + /// </summary> + New = 1, + + /// <summary> + /// The user asked to detach the memory pool from the <see cref="Dsp.AudioProcessor"/>. + /// </summary> + RequestDetach = 2, + + /// <summary> + /// The memory pool is detached from the <see cref="Dsp.AudioProcessor"/>. + /// </summary> + Detached = 3, + + /// <summary> + /// The user asked to attach the memory pool to the <see cref="Dsp.AudioProcessor"/>. + /// </summary> + RequestAttach = 4, + + /// <summary> + /// The memory pool is attached to the <see cref="Dsp.AudioProcessor"/>. + /// </summary> + Attached = 5, + + /// <summary> + /// The memory pool is released. (client side only) + /// </summary> + Released = 6 + } +} diff --git a/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs b/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs new file mode 100644 index 00000000..9bf1f2d0 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs @@ -0,0 +1,45 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// Helper for manipulating node ids. + /// </summary> + public static class NodeIdHelper + { + /// <summary> + /// Get the type of a node from a given node id. + /// </summary> + /// <param name="nodeId">Id of the node.</param> + /// <returns>The type of the node.</returns> + public static NodeIdType GetType(int nodeId) + { + return (NodeIdType)(nodeId >> 28); + } + + /// <summary> + /// Get the base of a node from a given node id. + /// </summary> + /// <param name="nodeId">Id of the node.</param> + /// <returns>The base of the node.</returns> + public static int GetBase(int nodeId) + { + return (nodeId >> 16) & 0xFFF; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Common/NodeIdType.cs b/Ryujinx.Audio/Renderer/Common/NodeIdType.cs new file mode 100644 index 00000000..4a4796a9 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/NodeIdType.cs @@ -0,0 +1,50 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// The type of a node. + /// </summary> + public enum NodeIdType : byte + { + /// <summary> + /// Invalid node id. + /// </summary> + Invalid = 0, + + /// <summary> + /// Voice related node id. (data source, biquad filter, ...) + /// </summary> + Voice = 1, + + /// <summary> + /// Mix related node id. (mix, effects, splitters, ...) + /// </summary> + Mix = 2, + + /// <summary> + /// Sink related node id. (device & circular buffer sink) + /// </summary> + Sink = 3, + + /// <summary> + /// Performance monitoring related node id (performance commands) + /// </summary> + Performance = 15 + } +} diff --git a/Ryujinx.Audio/Renderer/Common/NodeStates.cs b/Ryujinx.Audio/Renderer/Common/NodeStates.cs new file mode 100644 index 00000000..5efd7767 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/NodeStates.cs @@ -0,0 +1,246 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Utils; +using System; +using System.Diagnostics; + +namespace Ryujinx.Audio.Renderer.Common +{ + public class NodeStates + { + private class Stack + { + private Memory<int> _storage; + private int _index; + + private int _nodeCount; + + public void Reset(Memory<int> storage, int nodeCount) + { + Debug.Assert(storage.Length * sizeof(int) >= CalcBufferSize(nodeCount)); + + _storage = storage; + _index = 0; + _nodeCount = nodeCount; + } + + public int GetCurrentCount() + { + return _index; + } + + public void Push(int data) + { + Debug.Assert(_index + 1 <= _nodeCount); + + _storage.Span[_index++] = data; + } + + public int Pop() + { + Debug.Assert(_index > 0); + + return _storage.Span[--_index]; + } + + public int Top() + { + return _storage.Span[_index - 1]; + } + + public static int CalcBufferSize(int nodeCount) + { + return nodeCount * sizeof(int); + } + } + + private int _nodeCount; + private EdgeMatrix _discovered; + private EdgeMatrix _finished; + private Memory<int> _resultArray; + private Stack _stack; + private int _tsortResultIndex; + + private enum NodeState : byte + { + Unknown, + Discovered, + Finished + } + + public NodeStates() + { + _stack = new Stack(); + _discovered = new EdgeMatrix(); + _finished = new EdgeMatrix(); + } + + public static int GetWorkBufferSize(int nodeCount) + { + return Stack.CalcBufferSize(nodeCount * nodeCount) + 0xC * nodeCount + 2 * EdgeMatrix.GetWorkBufferSize(nodeCount); + } + + public void Initialize(Memory<byte> nodeStatesWorkBuffer, int nodeCount) + { + int workBufferSize = GetWorkBufferSize(nodeCount); + + Debug.Assert(nodeStatesWorkBuffer.Length >= workBufferSize); + + _nodeCount = nodeCount; + + int edgeMatrixWorkBufferSize = EdgeMatrix.GetWorkBufferSize(nodeCount); + + _discovered.Initialize(nodeStatesWorkBuffer.Slice(0, edgeMatrixWorkBufferSize), nodeCount); + _finished.Initialize(nodeStatesWorkBuffer.Slice(edgeMatrixWorkBufferSize, edgeMatrixWorkBufferSize), nodeCount); + + nodeStatesWorkBuffer = nodeStatesWorkBuffer.Slice(edgeMatrixWorkBufferSize * 2); + + _resultArray = SpanMemoryManager<int>.Cast(nodeStatesWorkBuffer.Slice(0, sizeof(int) * nodeCount)); + + nodeStatesWorkBuffer = nodeStatesWorkBuffer.Slice(sizeof(int) * nodeCount); + + Memory<int> stackWorkBuffer = SpanMemoryManager<int>.Cast(nodeStatesWorkBuffer.Slice(0, Stack.CalcBufferSize(nodeCount * nodeCount))); + + _stack.Reset(stackWorkBuffer, nodeCount * nodeCount); + } + + private void Reset() + { + _discovered.Reset(); + _finished.Reset(); + _tsortResultIndex = 0; + _resultArray.Span.Fill(-1); + } + + private NodeState GetState(int index) + { + Debug.Assert(index < _nodeCount); + + if (_discovered.Test(index)) + { + Debug.Assert(!_finished.Test(index)); + + return NodeState.Discovered; + } + else if (_finished.Test(index)) + { + Debug.Assert(!_discovered.Test(index)); + + return NodeState.Finished; + } + + return NodeState.Unknown; + } + + private void SetState(int index, NodeState state) + { + switch (state) + { + case NodeState.Unknown: + _discovered.Reset(index); + _finished.Reset(index); + break; + case NodeState.Discovered: + _discovered.Set(index); + _finished.Reset(index); + break; + case NodeState.Finished: + _finished.Set(index); + _discovered.Reset(index); + break; + } + } + + private void PushTsortResult(int index) + { + Debug.Assert(index < _nodeCount); + + _resultArray.Span[_tsortResultIndex++] = index; + } + + public ReadOnlySpan<int> GetTsortResult() + { + return _resultArray.Span.Slice(0, _tsortResultIndex); + } + + public bool Sort(EdgeMatrix edgeMatrix) + { + Reset(); + + if (_nodeCount <= 0) + { + return true; + } + + for (int i = 0; i < _nodeCount; i++) + { + if (GetState(i) == NodeState.Unknown) + { + _stack.Push(i); + } + + while (_stack.GetCurrentCount() > 0) + { + int topIndex = _stack.Top(); + + NodeState topState = GetState(topIndex); + + if (topState == NodeState.Discovered) + { + SetState(topIndex, NodeState.Finished); + PushTsortResult(topIndex); + _stack.Pop(); + } + else if (topState == NodeState.Finished) + { + _stack.Pop(); + } + else + { + if (topState == NodeState.Unknown) + { + SetState(topIndex, NodeState.Discovered); + } + + for (int j = 0; j < edgeMatrix.GetNodeCount(); j++) + { + if (edgeMatrix.Connected(topIndex, j)) + { + NodeState jState = GetState(j); + + if (jState == NodeState.Unknown) + { + _stack.Push(j); + } + // Found a loop, reset and propagate rejection. + else if (jState == NodeState.Discovered) + { + Reset(); + + return false; + } + } + } + } + } + } + + return true; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs b/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs new file mode 100644 index 00000000..a92bd0cc --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs @@ -0,0 +1,34 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + public enum PerformanceDetailType : byte + { + Unknown, + PcmInt16, + Adpcm, + VolumeRamp, + BiquadFilter, + Mix, + Delay, + Aux, + Reverb, + Reverb3d, + PcmFloat + } +} diff --git a/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs b/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs new file mode 100644 index 00000000..0d9a547d --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs @@ -0,0 +1,28 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + public enum PerformanceEntryType : byte + { + Invalid, + Voice, + SubMix, + FinalMix, + Sink + } +} diff --git a/Ryujinx.Audio/Renderer/Common/PlayState.cs b/Ryujinx.Audio/Renderer/Common/PlayState.cs new file mode 100644 index 00000000..7771c154 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/PlayState.cs @@ -0,0 +1,40 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// Common play state. + /// </summary> + public enum PlayState : byte + { + /// <summary> + /// The user request the voice to be started. + /// </summary> + Start, + + /// <summary> + /// The user request the voice to be stopped. + /// </summary> + Stop, + + /// <summary> + /// The user request the voice to be paused. + /// </summary> + Pause + } +} diff --git a/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs b/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs new file mode 100644 index 00000000..a0f65e5e --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs @@ -0,0 +1,50 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// Early reverb reflection. + /// </summary> + public enum ReverbEarlyMode : uint + { + /// <summary> + /// Room early reflection. (small acoustic space, fast reflection) + /// </summary> + Room, + + /// <summary> + /// Chamber early reflection. (bigger than <see cref="Room"/>'s acoustic space, short reflection) + /// </summary> + Chamber, + + /// <summary> + /// Hall early reflection. (large acoustic space, warm reflection) + /// </summary> + Hall, + + /// <summary> + /// Cathedral early reflection. (very large acoustic space, pronounced bright reflection) + /// </summary> + Cathedral, + + /// <summary> + /// No early reflection. + /// </summary> + Disabled + } +} diff --git a/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs b/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs new file mode 100644 index 00000000..57760821 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs @@ -0,0 +1,55 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// Late reverb reflection. + /// </summary> + public enum ReverbLateMode : uint + { + /// <summary> + /// Room late reflection. (small acoustic space, fast reflection) + /// </summary> + Room, + + /// <summary> + /// Hall late reflection. (large acoustic space, warm reflection) + /// </summary> + Hall, + + /// <summary> + /// Classic plate late reflection. (clean distinctive reverb) + /// </summary> + Plate, + + /// <summary> + /// Cathedral late reflection. (very large acoustic space, pronounced bright reflection) + /// </summary> + Cathedral, + + /// <summary> + /// Do not apply any delay. (max delay) + /// </summary> + NoDelay, + + /// <summary> + /// Max delay. (used for delay line limits) + /// </summary> + Limit = NoDelay + } +} diff --git a/Ryujinx.Audio/Renderer/Common/SinkType.cs b/Ryujinx.Audio/Renderer/Common/SinkType.cs new file mode 100644 index 00000000..e1a35508 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/SinkType.cs @@ -0,0 +1,40 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// The type of a sink. + /// </summary> + public enum SinkType : byte + { + /// <summary> + /// The sink is in an invalid state. + /// </summary> + Invalid, + + /// <summary> + /// The sink is a device. + /// </summary> + Device, + + /// <summary> + /// The sink is a circular buffer. + /// </summary> + CircularBuffer + } +} diff --git a/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs b/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs new file mode 100644 index 00000000..ae516662 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs @@ -0,0 +1,50 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +using System.Runtime.CompilerServices; + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// Update data header used for input and output of <see cref="Server.AudioRenderSystem.Update(System.Memory{byte}, System.Memory{byte}, System.ReadOnlyMemory{byte})"/>. + /// </summary> + public struct UpdateDataHeader + { + public int Revision; + public uint BehaviourSize; + public uint MemoryPoolsSize; + public uint VoicesSize; + public uint VoiceResourcesSize; + public uint EffectsSize; + public uint MixesSize; + public uint SinksSize; + public uint PerformanceBufferSize; + public uint Unknown24; + public uint RenderInfoSize; + + private unsafe fixed int _reserved[4]; + + public uint TotalSize; + + public void Initialize(int revision) + { + Revision = revision; + + TotalSize = (uint)Unsafe.SizeOf<UpdateDataHeader>(); + } + } +} diff --git a/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs b/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs new file mode 100644 index 00000000..6cebad5e --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs @@ -0,0 +1,121 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Dsp.State; +using Ryujinx.Common.Memory; +using Ryujinx.Common.Utilities; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// Represent the update state of a voice. + /// </summary> + /// <remarks>This is shared between the server and audio processor.</remarks> + [StructLayout(LayoutKind.Sequential, Pack = Align)] + public struct VoiceUpdateState + { + public const int Align = 0x10; + public const int BiquadStateOffset = 0x0; + public const int BiquadStateSize = 0x10; + + /// <summary> + /// The state of the biquad filters of this voice. + /// </summary> + public Array2<BiquadFilterState> BiquadFilterState; + + /// <summary> + /// The total amount of samples that was played. + /// </summary> + /// <remarks>This is reset to 0 when a <see cref="WaveBuffer"/> finishes playing and <see cref="WaveBuffer.IsEndOfStream"/> is set.</remarks> + /// <remarks>This is reset to 0 when looping while <see cref="Parameter.VoiceInParameter.DecodingBehaviour.PlayedSampleCountResetWhenLooping"/> is set.</remarks> + public ulong PlayedSampleCount; + + /// <summary> + /// The current sample offset in the <see cref="WaveBuffer"/> pointed by <see cref="WaveBufferIndex"/>. + /// </summary> + public int Offset; + + /// <summary> + /// The current index of the <see cref="WaveBuffer"/> in use. + /// </summary> + public uint WaveBufferIndex; + + private WaveBufferValidArray _isWaveBufferValid; + + /// <summary> + /// The total amount of <see cref="WaveBuffer"/> consumed. + /// </summary> + public uint WaveBufferConsumed; + + /// <summary> + /// Pitch used for Sample Rate Conversion. + /// </summary> + public Array8<short> Pitch; + + public float Fraction; + + /// <summary> + /// The ADPCM loop context when <see cref="SampleFormat.Adpcm"/> is in use. + /// </summary> + public AdpcmLoopContext LoopContext; + + /// <summary> + /// The last samples after a mix ramp. + /// </summary> + /// <remarks>This is used for depop (to perform voice drop).</remarks> + public Array24<float> LastSamples; + + /// <summary> + /// The current count of loop performed. + /// </summary> + public int LoopCount; + + [StructLayout(LayoutKind.Sequential, Size = 1 * Constants.VoiceWaveBufferCount, Pack = 1)] + private struct WaveBufferValidArray { } + + /// <summary> + /// Contains information of <see cref="WaveBuffer"/> validity. + /// </summary> + public Span<bool> IsWaveBufferValid => SpanHelpers.AsSpan<WaveBufferValidArray, bool>(ref _isWaveBufferValid); + + /// <summary> + /// Mark the current <see cref="WaveBuffer"/> as played and switch to the next one. + /// </summary> + /// <param name="waveBuffer">The current <see cref="WaveBuffer"/></param> + /// <param name="waveBufferIndex">The wavebuffer index.</param> + /// <param name="waveBufferConsumed">The amount of wavebuffers consumed.</param> + /// <param name="playedSampleCount">The total count of sample played.</param> + public void MarkEndOfBufferWaveBufferProcessing(ref WaveBuffer waveBuffer, ref int waveBufferIndex, ref uint waveBufferConsumed, ref ulong playedSampleCount) + { + IsWaveBufferValid[waveBufferIndex++] = false; + LoopCount = 0; + waveBufferConsumed++; + + if (waveBufferIndex >= Constants.VoiceWaveBufferCount) + { + waveBufferIndex = 0; + } + + if (waveBuffer.IsEndOfStream) + { + playedSampleCount = 0; + } + } + } +} diff --git a/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs b/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs new file mode 100644 index 00000000..53906c0c --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs @@ -0,0 +1,99 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +using System.Runtime.InteropServices; + +using DspAddr = System.UInt64; + +namespace Ryujinx.Audio.Renderer.Common +{ + /// <summary> + /// A wavebuffer used for data source commands. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct WaveBuffer + { + /// <summary> + /// The DSP address of the sample data of the wavebuffer. + /// </summary> + public DspAddr Buffer; + + /// <summary> + /// The DSP address of the context of the wavebuffer. + /// </summary> + /// <remarks>Only used by <see cref="SampleFormat.Adpcm"/>.</remarks> + public DspAddr Context; + + /// <summary> + /// The size of the sample buffer data. + /// </summary> + public uint BufferSize; + + /// <summary> + /// The size of the context buffer. + /// </summary> + public uint ContextSize; + + /// <summary> + /// First sample to play on the wavebuffer. + /// </summary> + public uint StartSampleOffset; + + /// <summary> + /// Last sample to play on the wavebuffer. + /// </summary> + public uint EndSampleOffset; + + /// <summary> + /// First sample to play when looping the wavebuffer. + /// </summary> + /// <remarks> + /// If <see cref="LoopStartSampleOffset"/> or <see cref="LoopEndSampleOffset"/> is equal to zero,, it will default to <see cref="StartSampleOffset"/> and <see cref="EndSampleOffset"/>. + /// </remarks> + public uint LoopStartSampleOffset; + + /// <summary> + /// Last sample to play when looping the wavebuffer. + /// </summary> + /// <remarks> + /// If <see cref="LoopStartSampleOffset"/> or <see cref="LoopEndSampleOffset"/> is equal to zero, it will default to <see cref="StartSampleOffset"/> and <see cref="EndSampleOffset"/>. + /// </remarks> + public uint LoopEndSampleOffset; + + /// <summary> + /// The max loop count. + /// </summary> + public int LoopCount; + + /// <summary> + /// Set to true if the wavebuffer is looping. + /// </summary> + [MarshalAs(UnmanagedType.I1)] + public bool Looping; + + /// <summary> + /// Set to true if the wavebuffer is the end of stream. + /// </summary> + [MarshalAs(UnmanagedType.I1)] + public bool IsEndOfStream; + + /// <summary> + /// Padding/Reserved. + /// </summary> + private ushort _padding; + } +} diff --git a/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs b/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs new file mode 100644 index 00000000..2aff4eb0 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs @@ -0,0 +1,78 @@ +// +// 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 <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Utils; +using Ryujinx.Common; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Audio.Renderer.Common +{ + public class WorkBufferAllocator + { + public Memory<byte> BackingMemory { get; } + + public ulong Offset { get; private set; } + + public WorkBufferAllocator(Memory<byte> backingMemory) + { + BackingMemory = backingMemory; + } + + public Memory<byte> Allocate(ulong size, int align) + { + Debug.Assert(align != 0); + + if (size != 0) + { + ulong alignedOffset = BitUtils.AlignUp(Offset, align); + + if (alignedOffset + size <= (ulong)BackingMemory.Length) + { + Memory<byte> result = BackingMemory.Slice((int)alignedOffset, (int)size); + + Offset = alignedOffset + size; + + // Clear the memory to be sure that is does not contain any garbage. + result.Span.Fill(0); + + return result; + } + } + + return Memory<byte>.Empty; + } + + public Memory<T> Allocate<T>(ulong count, int align) where T: unmanaged + { + Memory<byte> allocatedMemory = Allocate((ulong)Unsafe.SizeOf<T>() * count, align); + + if (allocatedMemory.IsEmpty) + { + return Memory<T>.Empty; + } + + return SpanMemoryManager<T>.Cast(allocatedMemory); + } + + public static ulong GetTargetSize<T>(ulong currentSize, ulong count, int align) where T: unmanaged + { + return BitUtils.AlignUp(currentSize, align) + (ulong)Unsafe.SizeOf<T>() * count; + } + } +} |
