From f17b772c56cf73ac539b4c8c47e0a7c8f29dae5a Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Fri, 20 Sep 2019 01:49:05 +0200
Subject: [PATCH] audren: Fix AudioRenderer implementation (#773)

* Fix AudioRenderer implementation

According to RE:
- `GetAudioRendererWorkBufferSize` is updated and improved to support `REV7`
- `RequestUpdateAudioRenderer` is updated to `REV7` too

Should improve results on recent game and close #718 and #707

* Fix NodeStates.GetWorkBufferSize

* Use BitUtils instead of IntUtils

* Nits
---
 .../AudioRendererCommon.cs                    |   9 ++
 .../AudioRendererManager/BehaviorInfo.cs      |  30 +++++
 .../AudioRendererManager/CommandGenerator.cs  |  16 +++
 .../Audio/AudioRendererManager/EdgeMatrix.cs  |  19 +++
 .../AudioRendererManager/IAudioRenderer.cs    |  35 ++++--
 .../Audio/AudioRendererManager/NodeStates.cs  |  19 +++
 .../PerformanceManager.cs                     |  30 +++++
 .../AudioRendererManager/SplitterContext.cs   |  25 ++++
 .../AudioRendererManager/Types/AudioConsts.cs |   8 --
 .../Types/AudioRendererConsts.cs              |  17 +++
 .../Types/AudioRendererParameter.cs           |   4 +-
 .../AudioRendererManager/Types/SupportTags.cs |  11 ++
 .../Types/UpdateDataHeader.cs                 |   2 +-
 .../AudioRendererManager/VoiceContext.cs      |  12 +-
 .../Services/Audio/IAudioRendererManager.cs   | 117 ++++++------------
 15 files changed, 244 insertions(+), 110 deletions(-)
 create mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/AudioRendererCommon.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/BehaviorInfo.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/CommandGenerator.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EdgeMatrix.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/NodeStates.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/PerformanceManager.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/SplitterContext.cs
 delete mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioConsts.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererConsts.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/SupportTags.cs

diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/AudioRendererCommon.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/AudioRendererCommon.cs
new file mode 100644
index 0000000000..c884b46507
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/AudioRendererCommon.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
+{
+    static class AudioRendererCommon
+    {
+        public static bool CheckValidRevision(AudioRendererParameter parameters)      => GetRevisionVersion(parameters.Revision) <= AudioRendererConsts.Revision;
+        public static bool CheckFeatureSupported(int revision, int supportedRevision) => revision >= supportedRevision;
+        public static int  GetRevisionVersion(int revision)                           => (revision - AudioRendererConsts.Rev0Magic) >> 24;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/BehaviorInfo.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/BehaviorInfo.cs
new file mode 100644
index 0000000000..461e433707
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/BehaviorInfo.cs
@@ -0,0 +1,30 @@
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
+{
+    class BehaviorInfo
+    {
+        private const int _revision = AudioRendererConsts.Revision;
+
+        private int _userRevision = 0;
+
+        public BehaviorInfo()
+        {
+            /* TODO: this class got a size of 0xC0
+                     0x00 - uint - Internal Revision
+                     0x04 - uint - User Revision
+                     0x08 - ... unknown ...
+            */
+        }
+
+        public bool IsSplitterSupported()                  => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.Splitter);
+        public bool IsSplitterBugFixed()                   => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.SplitterBugFix);
+        public bool IsVariadicCommandBufferSizeSupported() => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.VariadicCommandBufferSize);
+        public bool IsElapsedFrameCountSupported()         => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.ElapsedFrameCount);
+
+        public int GetPerformanceMetricsDataFormat() => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.PerformanceMetricsDataFormatVersion2) ? 2 : 1;
+
+        public void SetUserLibRevision(int revision)
+        {
+            _userRevision = AudioRendererCommon.GetRevisionVersion(revision);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/CommandGenerator.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/CommandGenerator.cs
new file mode 100644
index 0000000000..b09b990b60
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/CommandGenerator.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
+{
+    static class CommandGenerator
+    {
+        public static long CalculateCommandBufferSize(AudioRendererParameter parameters)
+        {
+            return parameters.EffectCount                  * 0x840  +
+                   parameters.SubMixCount                  * 0x5A38 +
+                   parameters.SinkCount                    * 0x148  +
+                   parameters.SplitterDestinationDataCount * 0x540  +
+                   (parameters.SplitterCount * 0x68 + 0x2E0) * parameters.VoiceCount +
+                   ((parameters.VoiceCount + parameters.SubMixCount + parameters.EffectCount + parameters.SinkCount + 0x65) << 6) +
+                   0x3F8;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EdgeMatrix.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EdgeMatrix.cs
new file mode 100644
index 0000000000..3f87ae04f1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EdgeMatrix.cs
@@ -0,0 +1,19 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
+{
+    static class EdgeMatrix
+    {
+        public static int GetWorkBufferSize(int totalMixCount)
+        {
+            int size = BitUtils.AlignUp(totalMixCount * totalMixCount, AudioRendererConsts.BufferAlignment);
+
+            if (size < 0)
+            {
+                size |= 7;
+            }
+
+            return size / 8;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/IAudioRenderer.cs
index 975992aa9e..0075fd5f0a 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/IAudioRenderer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/IAudioRenderer.cs
@@ -51,8 +51,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
             _params   = Params;
 
             _track = audioOut.OpenTrack(
-                AudioConsts.HostSampleRate,
-                AudioConsts.HostChannelsCount,
+                AudioRendererConsts.HostSampleRate,
+                AudioRendererConsts.HostChannelsCount,
                 AudioCallback);
 
             _memoryPools = CreateArray<MemoryPoolContext>(Params.EffectCount + Params.VoiceCount * 4);
@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
         // GetMixBufferCount() -> u32
         public ResultCode GetMixBufferCount(ServiceCtx context)
         {
-            context.ResponseData.Write(_params.MixCount);
+            context.ResponseData.Write(_params.SubMixCount);
 
             return ResultCode.Success;
         }
@@ -145,6 +145,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
 
             UpdateDataHeader inputHeader = reader.Read<UpdateDataHeader>();
 
+            BehaviorInfo behaviorInfo = new BehaviorInfo();
+
+            behaviorInfo.SetUserLibRevision(inputHeader.Revision);
+
             reader.Read<BehaviorIn>(inputHeader.BehaviorSize);
 
             MemoryPoolIn[] memoryPoolsIn = reader.Read<MemoryPoolIn>(inputHeader.MemoryPoolSize);
@@ -207,20 +211,27 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
 
             int updateHeaderSize = Marshal.SizeOf<UpdateDataHeader>();
 
-            outputHeader.Revision               = IAudioRendererManager.RevMagic;
+            outputHeader.Revision               = AudioRendererConsts.RevMagic;
             outputHeader.BehaviorSize           = 0xb0;
             outputHeader.MemoryPoolSize         = (_params.EffectCount + _params.VoiceCount * 4) * 0x10;
             outputHeader.VoiceSize              = _params.VoiceCount  * 0x10;
             outputHeader.EffectSize             = _params.EffectCount * 0x10;
             outputHeader.SinkSize               = _params.SinkCount   * 0x20;
             outputHeader.PerformanceManagerSize = 0x10;
-            outputHeader.TotalSize              = updateHeaderSize             +
-                                                  outputHeader.BehaviorSize    +
-                                                  outputHeader.MemoryPoolSize +
-                                                  outputHeader.VoiceSize      +
-                                                  outputHeader.EffectSize     +
-                                                  outputHeader.SinkSize       +
-                                                  outputHeader.PerformanceManagerSize;
+
+            if (behaviorInfo.IsElapsedFrameCountSupported())
+            {
+                outputHeader.ElapsedFrameCountInfoSize = 0x10;
+            }
+
+            outputHeader.TotalSize = updateHeaderSize                    +
+                                     outputHeader.BehaviorSize           +
+                                     outputHeader.MemoryPoolSize         +
+                                     outputHeader.VoiceSize              +
+                                     outputHeader.EffectSize             +
+                                     outputHeader.SinkSize               +
+                                     outputHeader.PerformanceManagerSize +
+                                     outputHeader.ElapsedFrameCountInfoSize;
 
             writer.Write(outputHeader);
 
@@ -305,7 +316,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
 
         private void AppendMixedBuffer(long tag)
         {
-            int[] mixBuffer = new int[MixBufferSamplesCount * AudioConsts.HostChannelsCount];
+            int[] mixBuffer = new int[MixBufferSamplesCount * AudioRendererConsts.HostChannelsCount];
 
             foreach (VoiceContext voice in _voices)
             {
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/NodeStates.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/NodeStates.cs
new file mode 100644
index 0000000000..7ae9aeea50
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/NodeStates.cs
@@ -0,0 +1,19 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
+{
+    static class NodeStates
+    {
+        public static long GetWorkBufferSize(int totalMixCount)
+        {
+            int size = BitUtils.AlignUp(totalMixCount, AudioRendererConsts.BufferAlignment);
+
+            if (size < 0)
+            {
+                size |= 7;
+            }
+
+            return 4 * (totalMixCount * totalMixCount) + 12 * totalMixCount + 2 * (size / 8);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/PerformanceManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/PerformanceManager.cs
new file mode 100644
index 0000000000..1b8c8a7cc1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/PerformanceManager.cs
@@ -0,0 +1,30 @@
+using Ryujinx.Common.Logging;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
+{
+    static class PerformanceManager
+    {
+        public static long GetRequiredBufferSizeForPerformanceMetricsPerFrame(BehaviorInfo behaviorInfo, AudioRendererParameter parameters)
+        {
+            int performanceMetricsDataFormat = behaviorInfo.GetPerformanceMetricsDataFormat();
+
+            if (performanceMetricsDataFormat == 2)
+            {
+                return 24 * (parameters.VoiceCount  + 
+                             parameters.EffectCount + 
+                             parameters.SubMixCount + 
+                             parameters.SinkCount   + 1) + 0x990;
+            }
+
+            if (performanceMetricsDataFormat != 1)
+            {
+                Logger.PrintWarning(LogClass.ServiceAudio, $"PerformanceMetricsDataFormat: {performanceMetricsDataFormat} is not supported!");
+            }
+
+            return (((parameters.VoiceCount  + 
+                      parameters.EffectCount +
+                      parameters.SubMixCount + 
+                      parameters.SinkCount   + 1) << 32) >> 0x1C) + 0x658;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/SplitterContext.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/SplitterContext.cs
new file mode 100644
index 0000000000..e46af4432d
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/SplitterContext.cs
@@ -0,0 +1,25 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
+{
+    class SplitterContext
+    {
+        public static long CalcWorkBufferSize(BehaviorInfo behaviorInfo, AudioRendererParameter parameters)
+        {
+            if (!behaviorInfo.IsSplitterSupported())
+            {
+                return 0;
+            }
+
+            long size = parameters.SplitterDestinationDataCount * 0xE0 +
+                        parameters.SplitterCount                * 0x20;
+
+            if (!behaviorInfo.IsSplitterBugFixed())
+            {
+                size += BitUtils.AlignUp(4 * parameters.SplitterDestinationDataCount, 16);
+            }
+
+            return size;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioConsts.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioConsts.cs
deleted file mode 100644
index f3b6995c7b..0000000000
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioConsts.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
-{
-    static class AudioConsts
-    {
-        public const int HostSampleRate    = 48000;
-        public const int HostChannelsCount = 2;
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererConsts.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererConsts.cs
new file mode 100644
index 0000000000..7e8b89bd23
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererConsts.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
+{
+    static class AudioRendererConsts
+    {
+        // Revision Consts
+        public const int Revision  = 7;
+        public const int Rev0Magic = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('0' << 24);
+        public const int RevMagic  = Rev0Magic + (Revision << 24);
+
+        // Misc Consts
+        public const int BufferAlignment = 0x40;
+
+        // Host Consts
+        public const int HostSampleRate    = 48000;
+        public const int HostChannelsCount = 2;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererParameter.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererParameter.cs
index 9772f786ac..2cfbc4b70e 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererParameter.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererParameter.cs
@@ -7,8 +7,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
     {
         public int SampleRate;
         public int SampleCount;
-        public int Unknown8;
-        public int MixCount;
+        public int MixBufferCount;
+        public int SubMixCount;
         public int VoiceCount;
         public int SinkCount;
         public int EffectCount;
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/SupportTags.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/SupportTags.cs
new file mode 100644
index 0000000000..a418d89e64
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/SupportTags.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
+{
+    static class SupportTags
+    {
+        public const int Splitter                             = 2;
+        public const int SplitterBugFix                       = 5;
+        public const int PerformanceMetricsDataFormatVersion2 = 5;
+        public const int VariadicCommandBufferSize            = 5;
+        public const int ElapsedFrameCount                    = 5;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/UpdateDataHeader.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/UpdateDataHeader.cs
index b1f1498430..1c5b2903ff 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/UpdateDataHeader.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/UpdateDataHeader.cs
@@ -12,7 +12,7 @@
         public int SinkSize;
         public int PerformanceManagerSize;
         public int Unknown24;
-        public int Unknown28;
+        public int ElapsedFrameCountInfoSize;
         public int Unknown2C;
         public int Unknown30;
         public int Unknown34;
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/VoiceContext.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/VoiceContext.cs
index c9fb850289..4bf15a59e9 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/VoiceContext.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/VoiceContext.cs
@@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
 
             int maxSize = _samples.Length - _offset;
 
-            int size = maxSamples * AudioConsts.HostChannelsCount;
+            int size = maxSamples * AudioRendererConsts.HostChannelsCount;
 
             if (size > maxSize)
             {
@@ -96,7 +96,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
 
             Array.Copy(_samples, _offset, output, 0, size);
 
-            samplesCount = size / AudioConsts.HostChannelsCount;
+            samplesCount = size / AudioRendererConsts.HostChannelsCount;
 
             _outStatus.PlayedSamplesCount += samplesCount;
 
@@ -140,7 +140,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
             {
                 int samplesCount = (int)(wb.Size / (sizeof(short) * ChannelsCount));
 
-                _samples = new int[samplesCount * AudioConsts.HostChannelsCount];
+                _samples = new int[samplesCount * AudioRendererConsts.HostChannelsCount];
 
                 if (ChannelsCount == 1)
                 {
@@ -171,19 +171,19 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
                 throw new InvalidOperationException();
             }
 
-            if (SampleRate != AudioConsts.HostSampleRate)
+            if (SampleRate != AudioRendererConsts.HostSampleRate)
             {
                 // TODO: We should keep the frames being discarded (see the 4 below)
                 // on a buffer and include it on the next samples buffer, to allow
                 // the resampler to do seamless interpolation between wave buffers.
-                int samplesCount = _samples.Length / AudioConsts.HostChannelsCount;
+                int samplesCount = _samples.Length / AudioRendererConsts.HostChannelsCount;
 
                 samplesCount = Math.Max(samplesCount - 4, 0);
 
                 _samples = Resampler.Resample2Ch(
                     _samples,
                     SampleRate,
-                    AudioConsts.HostSampleRate,
+                    AudioRendererConsts.HostSampleRate,
                     samplesCount,
                     ref _resamplerFracPart);
             }
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs
index de1c35b5cb..563ba7e151 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs
@@ -1,22 +1,13 @@
 using Ryujinx.Audio;
+using Ryujinx.Common;
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager;
-using Ryujinx.HLE.Utilities;
 
 namespace Ryujinx.HLE.HOS.Services.Audio
 {
     [Service("audren:u")]
     class IAudioRendererManager : IpcService
     {
-        private const int Rev0Magic = ('R' << 0)  |
-                                      ('E' << 8)  |
-                                      ('V' << 16) |
-                                      ('0' << 24);
-
-        private const int Rev = 5;
-
-        public const int RevMagic = Rev0Magic + (Rev << 24);
-
         public IAudioRendererManager(ServiceCtx context) { }
 
         [Command(0)]
@@ -41,69 +32,57 @@ namespace Ryujinx.HLE.HOS.Services.Audio
         // GetWorkBufferSize(nn::audio::detail::AudioRendererParameterInternal) -> u64
         public ResultCode GetAudioRendererWorkBufferSize(ServiceCtx context)
         {
-            AudioRendererParameter Params = GetAudioRendererParameter(context);
+            AudioRendererParameter parameters = GetAudioRendererParameter(context);
 
-            int revision = (Params.Revision - Rev0Magic) >> 24;
-
-            if (revision <= Rev)
+            if (AudioRendererCommon.CheckValidRevision(parameters))
             {
-                bool isSplitterSupported                  = revision >= 3;
-                bool isVariadicCommandBufferSizeSupported = revision >= 5;
-                
+                BehaviorInfo behaviorInfo = new BehaviorInfo();
+
+                behaviorInfo.SetUserLibRevision(parameters.Revision);
+
                 long size;
 
-                size  = IntUtils.AlignUp(Params.Unknown8 * 4, 64);
-                size += Params.MixCount * 0x400;
-                size += (Params.MixCount + 1) * 0x940;
-                size += Params.VoiceCount * 0x3F0;
-                size += IntUtils.AlignUp((Params.MixCount + 1) * 8, 16);
-                size += IntUtils.AlignUp(Params.VoiceCount * 8, 16);
-                size += IntUtils.AlignUp(
-                    ((Params.SinkCount + Params.MixCount) * 0x3C0 + Params.SampleCount * 4) *
-                    (Params.Unknown8 + 6), 64);
-                size += (Params.SinkCount + Params.MixCount) * 0x2C0;
-                size += (Params.EffectCount + Params.VoiceCount * 4) * 0x30 + 0x50;
+                int totalMixCount = parameters.SubMixCount + 1;
 
-                if (isSplitterSupported)
+                size = BitUtils.AlignUp(parameters.MixBufferCount * 4, AudioRendererConsts.BufferAlignment) +
+                       parameters.SubMixCount * 0x400 +
+                       totalMixCount          * 0x940 +
+                       parameters.VoiceCount  * 0x3F0 +
+                       BitUtils.AlignUp(totalMixCount * 8, 16) +
+                       BitUtils.AlignUp(parameters.VoiceCount * 8, 16) +
+                       BitUtils.AlignUp(((parameters.SinkCount + parameters.SubMixCount) * 0x3C0 + parameters.SampleCount * 4) *
+                                         (parameters.MixBufferCount + 6), AudioRendererConsts.BufferAlignment) +
+                       (parameters.SinkCount + parameters.SubMixCount) * 0x2C0 +
+                       (parameters.EffectCount + parameters.VoiceCount * 4) * 0x30 + 
+                       0x50;
+
+                if (behaviorInfo.IsSplitterSupported())
                 {
-                    size += IntUtils.AlignUp((
-                        NodeStatesGetWorkBufferSize(Params.MixCount + 1) +
-                        EdgeMatrixGetWorkBufferSize(Params.MixCount + 1)), 16);
-
-                    size += Params.SplitterDestinationDataCount * 0xE0;
-                    size += Params.SplitterCount * 0x20;
-                    size += IntUtils.AlignUp(Params.SplitterDestinationDataCount * 4, 16);
+                    size += BitUtils.AlignUp(NodeStates.GetWorkBufferSize(totalMixCount) + EdgeMatrix.GetWorkBufferSize(totalMixCount), 16);
                 }
 
-                size = Params.EffectCount * 0x4C0 +
-                       Params.SinkCount * 0x170 +
-                       Params.VoiceCount * 0x100 +
-                       IntUtils.AlignUp(size, 64) + 0x40;
+                size = parameters.SinkCount                            * 0x170 +
+                       (parameters.SinkCount + parameters.SubMixCount) * 0x280 +
+                       parameters.EffectCount                          * 0x4C0 +
+                       ((size + SplitterContext.CalcWorkBufferSize(behaviorInfo, parameters) + 0x30 * parameters.EffectCount + (4 * parameters.VoiceCount) + 0x8F) & ~0x3FL) +
+                       ((parameters.VoiceCount << 8) | 0x40);
 
-                if (Params.PerformanceManagerCount >= 1)
+                if (parameters.PerformanceManagerCount >= 1)
                 {
-                    size += (((Params.EffectCount +
-                               Params.SinkCount +
-                               Params.VoiceCount +
-                               Params.MixCount + 1) * 16 + 0x658) *
-                               (Params.PerformanceManagerCount + 1) + 0x13F) & ~0x3FL;
+                    size += (PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(behaviorInfo, parameters) * 
+                            (parameters.PerformanceManagerCount + 1) + 0xFF) & ~0x3FL;
                 }
 
-                if (isVariadicCommandBufferSizeSupported)
+                if (behaviorInfo.IsVariadicCommandBufferSizeSupported())
                 {
-                    size += Params.EffectCount * 0x840 + 
-                            Params.MixCount * 0x5A38 +
-                            Params.SinkCount * 0x148 +
-                            Params.SplitterDestinationDataCount * 0x540 +
-                            Params.VoiceCount * (Params.SplitterCount * 0x68 + 0x2E0) +
-                            ((Params.VoiceCount + Params.MixCount + Params.EffectCount + Params.SinkCount + 0x65) << 6) + 0x3F8 + 0x7E;
+                    size += CommandGenerator.CalculateCommandBufferSize(parameters) + 0x7E;
                 }
                 else
                 {
                     size += 0x1807E;
                 }
 
-                size = size & ~0xFFFL;
+                size = BitUtils.AlignUp(size, 0x1000);
 
                 context.ResponseData.Write(size);
 
@@ -115,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
             {
                 context.ResponseData.Write(0L);
 
-                Logger.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!");
+                Logger.PrintWarning(LogClass.ServiceAudio, $"Library Revision REV{AudioRendererCommon.GetRevisionVersion(parameters.Revision)} is not supported!");
 
                 return ResultCode.UnsupportedRevision;
             }
@@ -127,8 +106,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio
             {
                 SampleRate                   = context.RequestData.ReadInt32(),
                 SampleCount                  = context.RequestData.ReadInt32(),
-                Unknown8                     = context.RequestData.ReadInt32(),
-                MixCount                     = context.RequestData.ReadInt32(),
+                MixBufferCount               = context.RequestData.ReadInt32(),
+                SubMixCount                  = context.RequestData.ReadInt32(),
                 VoiceCount                   = context.RequestData.ReadInt32(),
                 SinkCount                    = context.RequestData.ReadInt32(),
                 EffectCount                  = context.RequestData.ReadInt32(),
@@ -143,30 +122,6 @@ namespace Ryujinx.HLE.HOS.Services.Audio
             return Params;
         }
 
-        private static int NodeStatesGetWorkBufferSize(int value)
-        {
-            int result = IntUtils.AlignUp(value, 64);
-
-            if (result < 0)
-            {
-                result |= 7;
-            }
-
-            return 4 * (value * value) + 0x12 * value + 2 * (result / 8);
-        }
-
-        private static int EdgeMatrixGetWorkBufferSize(int value)
-        {
-            int result = IntUtils.AlignUp(value * value, 64);
-
-            if (result < 0)
-            {
-                result |= 7;
-            }
-
-            return result / 8;
-        }
-
         [Command(2)]
         // GetAudioDeviceService(nn::applet::AppletResourceUserId) -> object<nn::audio::detail::IAudioDevice>
         public ResultCode GetAudioDeviceService(ServiceCtx context)