aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Audio/Renderer/Server
diff options
context:
space:
mode:
authorMary <me@thog.eu>2021-09-19 12:29:19 +0200
committerGitHub <noreply@github.com>2021-09-19 12:29:19 +0200
commite17eb7bfafdd95084baea8e9f3dc77ee3f755347 (patch)
tree4982e2593a279c9e2c4906ead4d1764a9ddadb54 /Ryujinx.Audio/Renderer/Server
parentfe9d5a1981cfe43c4535b7473064c9858addb3b5 (diff)
amadeus: Update to REV10 (#2654)
* amadeus: Update to REV10 This implements all the changes made with REV10 on 13.0.0. * Address Ack's comment * Address gdkchan's comment
Diffstat (limited to 'Ryujinx.Audio/Renderer/Server')
-rw-r--r--Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs3
-rw-r--r--Ryujinx.Audio/Renderer/Server/BehaviourContext.cs26
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandBuffer.cs44
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandGenerator.cs87
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs10
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs10
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs15
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs68
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs12
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs2
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs99
-rw-r--r--Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs2
-rw-r--r--Ryujinx.Audio/Renderer/Server/StateUpdater.cs3
13 files changed, 358 insertions, 23 deletions
diff --git a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
index afbe56a6..277c2474 100644
--- a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
+++ b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
@@ -360,6 +360,9 @@ namespace Ryujinx.Audio.Renderer.Server
case 3:
_commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion3(_sampleCount, _mixBufferCount);
break;
+ case 4:
+ _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion4(_sampleCount, _mixBufferCount);
+ break;
default:
throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourContext.GetCommandProcessingTimeEstimatorVersion()}.");
}
diff --git a/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
index ed1f402e..d3a65b72 100644
--- a/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
@@ -98,9 +98,19 @@ namespace Ryujinx.Audio.Renderer.Server
public const int Revision9 = 9 << 24;
/// <summary>
+ /// REV10:
+ /// Added Bluetooth audio device support and removed the unused "GetAudioSystemMasterVolumeSetting" audio device API.
+ /// A new effect was added: Capture. This effect allows the client side to capture audio buffers of a mix.
+ /// A new command was added for double biquad filters on voices. This is implemented using a direct form 1 (instead of the usual direct form 2).
+ /// A new version of the command estimator was added to support the new commands.
+ /// </summary>
+ /// <remarks>This was added in system update 13.0.0</remarks>
+ public const int Revision10 = 10 << 24;
+
+ /// <summary>
/// Last revision supported by the implementation.
/// </summary>
- public const int LastRevision = Revision9;
+ public const int LastRevision = Revision10;
/// <summary>
/// Target revision magic supported by the implementation.
@@ -348,11 +358,25 @@ namespace Ryujinx.Audio.Renderer.Server
}
/// <summary>
+ /// Check if the audio renderer should use an optimized Biquad Filter (Direct Form 1) in case of two biquad filters are defined on a voice.
+ /// </summary>
+ /// <returns>True if the audio renderer should use the optimization.</returns>
+ public bool IsBiquadFilterGroupedOptimizationSupported()
+ {
+ return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10);
+ }
+
+ /// <summary>
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
/// </summary>
/// <returns>The version of the <see cref="ICommandProcessingTimeEstimator"/>.</returns>
public int GetCommandProcessingTimeEstimatorVersion()
{
+ if (CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10))
+ {
+ return 4;
+ }
+
if (CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision8))
{
return 3;
diff --git a/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs
index deb20f3d..0e74c301 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs
@@ -24,6 +24,7 @@ using Ryujinx.Audio.Renderer.Server.Performance;
using Ryujinx.Audio.Renderer.Server.Sink;
using Ryujinx.Audio.Renderer.Server.Upsampler;
using Ryujinx.Audio.Renderer.Server.Voice;
+using Ryujinx.Common.Memory;
using System;
using CpuAddress = System.UInt64;
@@ -221,6 +222,25 @@ namespace Ryujinx.Audio.Renderer.Server
}
/// <summary>
+ /// Create a new <see cref="GroupedBiquadFilterCommand"/>.
+ /// </summary>
+ /// <param name="baseIndex">The base index of the input and output buffer.</param>
+ /// <param name="filters">The biquad filter parameters.</param>
+ /// <param name="biquadFilterStatesMemory">The biquad states.</param>
+ /// <param name="inputBufferOffset">The input buffer offset.</param>
+ /// <param name="outputBufferOffset">The output buffer offset.</param>
+ /// <param name="isInitialized">Set to true if the biquad filter state is initialized.</param>
+ /// <param name="nodeId">The node id associated to this command.</param>
+ public void GenerateGroupedBiquadFilter(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId)
+ {
+ GroupedBiquadFilterCommand command = new GroupedBiquadFilterCommand(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId);
+
+ command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
+
+ AddCommand(command);
+ }
+
+ /// <summary>
/// Generate a new <see cref="MixRampGroupedCommand"/>.
/// </summary>
/// <param name="mixBufferCount">The mix buffer count.</param>
@@ -441,6 +461,30 @@ namespace Ryujinx.Audio.Renderer.Server
}
/// <summary>
+ /// Generate a new <see cref="CaptureBufferCommand"/>.
+ /// </summary>
+ /// <param name="bufferOffset">The target buffer offset.</param>
+ /// <param name="inputBufferOffset">The input buffer offset.</param>
+ /// <param name="sendBufferInfo">The capture state.</param>
+ /// <param name="isEnabled">Set to true if the effect should be active.</param>
+ /// <param name="countMax">The limit of the circular buffer.</param>
+ /// <param name="outputBuffer">The guest address of the output buffer.</param>
+ /// <param name="updateCount">The count to add on the offset after write operations.</param>
+ /// <param name="writeOffset">The write offset.</param>
+ /// <param name="nodeId">The node id associated to this command.</param>
+ public void GenerateCaptureEffect(uint bufferOffset, byte inputBufferOffset, ulong sendBufferInfo, bool isEnabled, uint countMax, CpuAddress outputBuffer, uint updateCount, uint writeOffset, int nodeId)
+ {
+ if (sendBufferInfo != 0)
+ {
+ CaptureBufferCommand command = new CaptureBufferCommand(bufferOffset, inputBufferOffset, sendBufferInfo, isEnabled, countMax, outputBuffer, updateCount, writeOffset, nodeId);
+
+ command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
+
+ AddCommand(command);
+ }
+ }
+
+ /// <summary>
/// Generate a new <see cref="VolumeCommand"/>.
/// </summary>
/// <param name="volume">The target volume to apply.</param>
diff --git a/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs
index 01e7c927..8e4ecd25 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs
@@ -151,23 +151,35 @@ namespace Ryujinx.Audio.Renderer.Server
private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory<VoiceUpdateState> state, int baseIndex, int bufferOffset, int nodeId)
{
- for (int i = 0; i < voiceState.BiquadFilters.Length; i++)
+ bool supportsOptimizedPath = _rendererContext.BehaviourContext.IsBiquadFilterGroupedOptimizationSupported();
+
+ if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable)
{
- ref BiquadFilterParameter filter = ref voiceState.BiquadFilters[i];
+ Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount);
+ Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
- if (filter.Enable)
+ _commandBuffer.GenerateGroupedBiquadFilter(baseIndex, voiceState.BiquadFilters.ToSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
+ }
+ else
+ {
+ for (int i = 0; i < voiceState.BiquadFilters.Length; i++)
{
- Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount);
+ ref BiquadFilterParameter filter = ref voiceState.BiquadFilters[i];
- Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
+ if (filter.Enable)
+ {
+ Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount);
- _commandBuffer.GenerateBiquadFilter(baseIndex,
- ref filter,
- stateMemory.Slice(i, 1),
- bufferOffset,
- bufferOffset,
- !voiceState.BiquadFilterNeedInitialization[i],
- nodeId);
+ Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
+
+ _commandBuffer.GenerateBiquadFilter(baseIndex,
+ ref filter,
+ stateMemory.Slice(i, 1),
+ bufferOffset,
+ bufferOffset,
+ !voiceState.BiquadFilterNeedInitialization[i],
+ nodeId);
+ }
}
}
}
@@ -443,7 +455,7 @@ namespace Ryujinx.Audio.Renderer.Server
uint updateCount;
- if ((channelIndex - 1) != 0)
+ if (channelIndex != 1)
{
updateCount = 0;
}
@@ -556,6 +568,52 @@ namespace Ryujinx.Audio.Renderer.Server
}
}
+ private void GenerateCaptureEffect(uint bufferOffset, CaptureBufferEffect effect, int nodeId)
+ {
+ Debug.Assert(effect.Type == EffectType.CaptureBuffer);
+
+ if (effect.IsEnabled)
+ {
+ effect.GetWorkBuffer(0);
+ }
+
+ if (effect.State.SendBufferInfoBase != 0)
+ {
+ int i = 0;
+ uint writeOffset = 0;
+
+ for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
+ {
+ uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
+
+ uint updateCount;
+
+ if (channelIndex != 1)
+ {
+ updateCount = 0;
+ }
+ else
+ {
+ updateCount = newUpdateCount;
+ }
+
+ _commandBuffer.GenerateCaptureEffect(bufferOffset,
+ effect.Parameter.Input[i],
+ effect.State.SendBufferInfo,
+ effect.IsEnabled,
+ effect.Parameter.BufferStorageSize,
+ effect.State.SendBufferInfoBase,
+ updateCount,
+ writeOffset,
+ nodeId);
+
+ writeOffset = newUpdateCount;
+
+ i++;
+ }
+ }
+ }
+
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
{
int nodeId = mix.NodeId;
@@ -597,6 +655,9 @@ namespace Ryujinx.Audio.Renderer.Server
case EffectType.Limiter:
GenerateLimiterEffect(mix.BufferOffset, (LimiterEffect)effect, nodeId, effectId);
break;
+ case EffectType.CaptureBuffer:
+ GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
+ break;
default:
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
}
diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs
index feb3706f..6821cccf 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs
@@ -186,5 +186,15 @@ namespace Ryujinx.Audio.Renderer.Server
{
return 0;
}
+
+ public uint Estimate(GroupedBiquadFilterCommand command)
+ {
+ return 0;
+ }
+
+ public uint Estimate(CaptureBufferCommand command)
+ {
+ return 0;
+ }
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs
index 227f3c81..daf50de9 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs
@@ -550,5 +550,15 @@ namespace Ryujinx.Audio.Renderer.Server
{
return 0;
}
+
+ public uint Estimate(GroupedBiquadFilterCommand command)
+ {
+ return 0;
+ }
+
+ public uint Estimate(CaptureBufferCommand command)
+ {
+ return 0;
+ }
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs
index e00fcf7b..75d3d05b 100644
--- a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs
@@ -16,7 +16,6 @@
//
using Ryujinx.Audio.Common;
-using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp.Command;
using Ryujinx.Audio.Renderer.Parameter.Effect;
using System;
@@ -30,8 +29,8 @@ namespace Ryujinx.Audio.Renderer.Server
/// </summary>
public class CommandProcessingTimeEstimatorVersion3 : ICommandProcessingTimeEstimator
{
- private uint _sampleCount;
- private uint _bufferCount;
+ protected uint _sampleCount;
+ protected uint _bufferCount;
public CommandProcessingTimeEstimatorVersion3(uint sampleCount, uint bufferCount)
{
@@ -755,5 +754,15 @@ namespace Ryujinx.Audio.Renderer.Server
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
}
}
+
+ public virtual uint Estimate(GroupedBiquadFilterCommand command)
+ {
+ return 0;
+ }
+
+ public virtual uint Estimate(CaptureBufferCommand command)
+ {
+ return 0;
+ }
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs
new file mode 100644
index 00000000..ea11f69c
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs
@@ -0,0 +1,68 @@
+//
+// 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.Common;
+using Ryujinx.Audio.Renderer.Dsp.Command;
+using Ryujinx.Audio.Renderer.Parameter.Effect;
+using System;
+using System.Diagnostics;
+using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter;
+
+namespace Ryujinx.Audio.Renderer.Server
+{
+ /// <summary>
+ /// <see cref="ICommandProcessingTimeEstimator"/> version 4. (added with REV10)
+ /// </summary>
+ public class CommandProcessingTimeEstimatorVersion4 : CommandProcessingTimeEstimatorVersion3
+ {
+ public CommandProcessingTimeEstimatorVersion4(uint sampleCount, uint bufferCount) : base(sampleCount, bufferCount) { }
+
+ public override uint Estimate(GroupedBiquadFilterCommand command)
+ {
+ Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
+
+ if (_sampleCount == 160)
+ {
+ return (uint)7424.5f;
+ }
+
+ return (uint)9730.4f;
+ }
+
+ public override uint Estimate(CaptureBufferCommand command)
+ {
+ Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
+
+ if (_sampleCount == 160)
+ {
+ if (command.Enabled)
+ {
+ return (uint)435.2f;
+ }
+
+ return (uint)4261.0f;
+ }
+
+ if (command.Enabled)
+ {
+ return (uint)5858.26f;
+ }
+
+ return (uint)435.2f;
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
index eac1708e..3a13d377 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
@@ -23,7 +23,7 @@ using Ryujinx.Audio.Renderer.Server.MemoryPool;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-
+using static Ryujinx.Audio.Renderer.Dsp.State.AuxiliaryBufferHeader;
using DspAddress = System.UInt64;
namespace Ryujinx.Audio.Renderer.Server.Effect
@@ -73,7 +73,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
if (BufferUnmapped || parameter.IsNew)
{
- ulong bufferSize = (ulong)Unsafe.SizeOf<int>() * Parameter.BufferStorageSize + (ulong)Unsafe.SizeOf<AuxiliaryBufferHeader>() * 2;
+ ulong bufferSize = (ulong)Unsafe.SizeOf<int>() * Parameter.BufferStorageSize + (ulong)Unsafe.SizeOf<AuxiliaryBufferHeader>();
bool sendBufferUnmapped = !mapper.TryAttachBuffer(out updateErrorInfo, ref WorkBuffers[0], Parameter.SendBufferInfoAddress, bufferSize);
bool returnBufferUnmapped = !mapper.TryAttachBuffer(out updateErrorInfo, ref WorkBuffers[1], Parameter.ReturnBufferInfoAddress, bufferSize);
@@ -85,11 +85,11 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
DspAddress sendDspAddress = WorkBuffers[0].GetReference(false);
DspAddress returnDspAddress = WorkBuffers[1].GetReference(false);
- State.SendBufferInfo = sendDspAddress + (uint)Unsafe.SizeOf<AuxiliaryBufferHeader>();
- State.SendBufferInfoBase = sendDspAddress + (uint)Unsafe.SizeOf<AuxiliaryBufferHeader>() * 2;
+ State.SendBufferInfo = sendDspAddress + (uint)Unsafe.SizeOf<AuxiliaryBufferInfo>();
+ State.SendBufferInfoBase = sendDspAddress + (uint)Unsafe.SizeOf<AuxiliaryBufferHeader>();
- State.ReturnBufferInfo = returnDspAddress + (uint)Unsafe.SizeOf<AuxiliaryBufferHeader>();
- State.ReturnBufferInfoBase = returnDspAddress + (uint)Unsafe.SizeOf<AuxiliaryBufferHeader>() * 2;
+ State.ReturnBufferInfo = returnDspAddress + (uint)Unsafe.SizeOf<AuxiliaryBufferInfo>();
+ State.ReturnBufferInfoBase = returnDspAddress + (uint)Unsafe.SizeOf<AuxiliaryBufferHeader>();
}
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
index c7b06e7a..40e87530 100644
--- a/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
@@ -277,6 +277,8 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return PerformanceDetailType.Mix;
case EffectType.Limiter:
return PerformanceDetailType.Limiter;
+ case EffectType.CaptureBuffer:
+ return PerformanceDetailType.CaptureBuffer;
default:
throw new NotImplementedException($"{Type}");
}
diff --git a/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs
new file mode 100644
index 00000000..6ba0040e
--- /dev/null
+++ b/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.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 Ryujinx.Audio.Renderer.Common;
+using Ryujinx.Audio.Renderer.Dsp.State;
+using Ryujinx.Audio.Renderer.Parameter;
+using Ryujinx.Audio.Renderer.Parameter.Effect;
+using Ryujinx.Audio.Renderer.Server.MemoryPool;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using DspAddress = System.UInt64;
+
+namespace Ryujinx.Audio.Renderer.Server.Effect
+{
+ /// <summary>
+ /// Server state for an capture buffer effect.
+ /// </summary>
+ public class CaptureBufferEffect : BaseEffect
+ {
+ /// <summary>
+ /// The capture buffer parameter.
+ /// </summary>
+ public AuxiliaryBufferParameter Parameter;
+
+ /// <summary>
+ /// Capture buffer state.
+ /// </summary>
+ public AuxiliaryBufferAddresses State;
+
+ public override EffectType TargetEffectType => EffectType.CaptureBuffer;
+
+ public override DspAddress GetWorkBuffer(int index)
+ {
+ return WorkBuffers[index].GetReference(true);
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ {
+ Update(out updateErrorInfo, ref parameter, mapper);
+ }
+
+ public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ {
+ Debug.Assert(IsTypeValid(ref parameter));
+
+ UpdateParameterBase(ref parameter);
+
+ Parameter = MemoryMarshal.Cast<byte, AuxiliaryBufferParameter>(parameter.SpecificData)[0];
+ IsEnabled = parameter.IsEnabled;
+
+ updateErrorInfo = new BehaviourParameter.ErrorInfo();
+
+ if (BufferUnmapped || parameter.IsNew)
+ {
+ ulong bufferSize = (ulong)Unsafe.SizeOf<int>() * Parameter.BufferStorageSize + (ulong)Unsafe.SizeOf<AuxiliaryBufferHeader>();
+
+ bool sendBufferUnmapped = !mapper.TryAttachBuffer(out updateErrorInfo, ref WorkBuffers[0], Parameter.SendBufferInfoAddress, bufferSize);
+
+ BufferUnmapped = sendBufferUnmapped;
+
+ if (!BufferUnmapped)
+ {
+ DspAddress sendDspAddress = WorkBuffers[0].GetReference(false);
+
+ // NOTE: Nintendo directly interact with the CPU side structure in the processing of the DSP command.
+ State.SendBufferInfo = sendDspAddress;
+ State.SendBufferInfoBase = sendDspAddress + (ulong)Unsafe.SizeOf<AuxiliaryBufferHeader>();
+ State.ReturnBufferInfo = 0;
+ State.ReturnBufferInfoBase = 0;
+ }
+ }
+ }
+
+ public override void UpdateForCommandGeneration()
+ {
+ UpdateUsageStateForCommandGeneration();
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs b/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs
index eae48be6..4100f357 100644
--- a/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs
+++ b/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs
@@ -50,5 +50,7 @@ namespace Ryujinx.Audio.Renderer.Server
uint Estimate(UpsampleCommand command);
uint Estimate(LimiterCommandVersion1 command);
uint Estimate(LimiterCommandVersion2 command);
+ uint Estimate(GroupedBiquadFilterCommand command);
+ uint Estimate(CaptureBufferCommand command);
}
}
diff --git a/Ryujinx.Audio/Renderer/Server/StateUpdater.cs b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
index e7a982c4..1f50864d 100644
--- a/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
+++ b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
@@ -254,6 +254,9 @@ namespace Ryujinx.Audio.Renderer.Server
case EffectType.Limiter:
effect = new LimiterEffect();
break;
+ case EffectType.CaptureBuffer:
+ effect = new CaptureBufferEffect();
+ break;
default:
throw new NotImplementedException($"EffectType {parameter.Type} not implemented!");
}