mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-11-07 22:18:33 +00:00
amadeus: Update to REV9 (#2309)
* amadeus: Update to REV9 This implements all the changes made with REV9 on 12.0.0. * Address Ac_k's comments
This commit is contained in:
parent
54ea2285f0
commit
f3b0b4831c
|
@ -55,6 +55,11 @@ namespace Ryujinx.Audio.Renderer.Common
|
|||
/// <summary>
|
||||
/// Effect applying a biquad filter.
|
||||
/// </summary>
|
||||
BiquadFilter
|
||||
BiquadFilter,
|
||||
|
||||
/// <summary>
|
||||
/// Effect applying a limiter (DRC).
|
||||
/// </summary>
|
||||
Limiter,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace Ryujinx.Audio.Renderer.Common
|
|||
Aux,
|
||||
Reverb,
|
||||
Reverb3d,
|
||||
PcmFloat
|
||||
PcmFloat,
|
||||
Limiter
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||
Reverb3d,
|
||||
Performance,
|
||||
ClearMixBuffer,
|
||||
CopyMixBuffer
|
||||
CopyMixBuffer,
|
||||
LimiterVersion1,
|
||||
LimiterVersion2
|
||||
}
|
||||
}
|
||||
|
|
160
Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs
Normal file
160
Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs
Normal file
|
@ -0,0 +1,160 @@
|
|||
//
|
||||
// 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.Audio.Renderer.Parameter.Effect;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
{
|
||||
public class LimiterCommandVersion1 : ICommand
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public int NodeId { get; }
|
||||
|
||||
public CommandType CommandType => CommandType.LimiterVersion1;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
|
||||
public LimiterParameter Parameter => _parameter;
|
||||
public Memory<LimiterState> State { get; }
|
||||
public ulong WorkBuffer { get; }
|
||||
public ushort[] OutputBufferIndices { get; }
|
||||
public ushort[] InputBufferIndices { get; }
|
||||
public bool IsEffectEnabled { get; }
|
||||
|
||||
private LimiterParameter _parameter;
|
||||
|
||||
public LimiterCommandVersion1(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, bool isEnabled, ulong workBuffer, int nodeId)
|
||||
{
|
||||
Enabled = true;
|
||||
NodeId = nodeId;
|
||||
_parameter = parameter;
|
||||
State = state;
|
||||
WorkBuffer = workBuffer;
|
||||
|
||||
IsEffectEnabled = isEnabled;
|
||||
|
||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
{
|
||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Process(CommandList context)
|
||||
{
|
||||
ref LimiterState state = ref State.Span[0];
|
||||
|
||||
if (IsEffectEnabled)
|
||||
{
|
||||
if (Parameter.Status == Server.Effect.UsageState.Invalid)
|
||||
{
|
||||
state = new LimiterState(ref _parameter, WorkBuffer);
|
||||
}
|
||||
else if (Parameter.Status == Server.Effect.UsageState.New)
|
||||
{
|
||||
state.UpdateParameter(ref _parameter);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessLimiter(context);
|
||||
}
|
||||
|
||||
private void ProcessLimiter(CommandList context)
|
||||
{
|
||||
Debug.Assert(Parameter.IsChannelCountValid());
|
||||
|
||||
if (IsEffectEnabled && Parameter.IsChannelCountValid())
|
||||
{
|
||||
ref LimiterState state = ref State.Span[0];
|
||||
|
||||
ReadOnlyMemory<float>[] inputBuffers = new ReadOnlyMemory<float>[Parameter.ChannelCount];
|
||||
Memory<float>[] outputBuffers = new Memory<float>[Parameter.ChannelCount];
|
||||
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
{
|
||||
inputBuffers[i] = context.GetBufferMemory(InputBufferIndices[i]);
|
||||
outputBuffers[i] = context.GetBufferMemory(OutputBufferIndices[i]);
|
||||
}
|
||||
|
||||
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||
{
|
||||
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||
{
|
||||
float inputSample = inputBuffers[channelIndex].Span[sampleIndex];
|
||||
|
||||
float sampleInputMax = Math.Abs(inputSample * Parameter.InputGain);
|
||||
|
||||
float inputCoefficient = Parameter.ReleaseCoefficient;
|
||||
|
||||
if (sampleInputMax > state.DectectorAverage[channelIndex])
|
||||
{
|
||||
inputCoefficient = Parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
state.DectectorAverage[channelIndex] += inputCoefficient * (sampleInputMax - state.DectectorAverage[channelIndex]);
|
||||
|
||||
float attenuation = 1.0f;
|
||||
|
||||
if (state.DectectorAverage[channelIndex] > Parameter.Threshold)
|
||||
{
|
||||
attenuation = Parameter.Threshold / state.DectectorAverage[channelIndex];
|
||||
}
|
||||
|
||||
float outputCoefficient = Parameter.ReleaseCoefficient;
|
||||
|
||||
if (state.CompressionGain[channelIndex] > attenuation)
|
||||
{
|
||||
outputCoefficient = Parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
state.CompressionGain[channelIndex] += outputCoefficient * (attenuation - state.CompressionGain[channelIndex]);
|
||||
|
||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||
|
||||
outputBuffers[channelIndex].Span[sampleIndex] = delayedSample * state.CompressionGain[channelIndex] * Parameter.OutputGain;
|
||||
|
||||
delayedSample = inputSample;
|
||||
|
||||
state.DelayedSampleBufferPosition[channelIndex]++;
|
||||
|
||||
while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
|
||||
{
|
||||
state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
{
|
||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||
{
|
||||
context.GetBufferMemory(InputBufferIndices[i]).CopyTo(context.GetBufferMemory(OutputBufferIndices[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
179
Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs
Normal file
179
Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs
Normal file
|
@ -0,0 +1,179 @@
|
|||
//
|
||||
// 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.Audio.Renderer.Parameter;
|
||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
{
|
||||
public class LimiterCommandVersion2 : ICommand
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public int NodeId { get; }
|
||||
|
||||
public CommandType CommandType => CommandType.LimiterVersion2;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
|
||||
public LimiterParameter Parameter => _parameter;
|
||||
public Memory<LimiterState> State { get; }
|
||||
public Memory<EffectResultState> ResultState { get; }
|
||||
public ulong WorkBuffer { get; }
|
||||
public ushort[] OutputBufferIndices { get; }
|
||||
public ushort[] InputBufferIndices { get; }
|
||||
public bool IsEffectEnabled { get; }
|
||||
|
||||
private LimiterParameter _parameter;
|
||||
|
||||
public LimiterCommandVersion2(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, Memory<EffectResultState> resultState, bool isEnabled, ulong workBuffer, int nodeId)
|
||||
{
|
||||
Enabled = true;
|
||||
NodeId = nodeId;
|
||||
_parameter = parameter;
|
||||
State = state;
|
||||
ResultState = resultState;
|
||||
WorkBuffer = workBuffer;
|
||||
|
||||
IsEffectEnabled = isEnabled;
|
||||
|
||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
{
|
||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Process(CommandList context)
|
||||
{
|
||||
ref LimiterState state = ref State.Span[0];
|
||||
|
||||
if (IsEffectEnabled)
|
||||
{
|
||||
if (Parameter.Status == Server.Effect.UsageState.Invalid)
|
||||
{
|
||||
state = new LimiterState(ref _parameter, WorkBuffer);
|
||||
}
|
||||
else if (Parameter.Status == Server.Effect.UsageState.New)
|
||||
{
|
||||
state.UpdateParameter(ref _parameter);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessLimiter(context);
|
||||
}
|
||||
|
||||
private void ProcessLimiter(CommandList context)
|
||||
{
|
||||
Debug.Assert(Parameter.IsChannelCountValid());
|
||||
|
||||
if (IsEffectEnabled && Parameter.IsChannelCountValid())
|
||||
{
|
||||
ref LimiterState state = ref State.Span[0];
|
||||
|
||||
if (!ResultState.IsEmpty && Parameter.StatisticsReset)
|
||||
{
|
||||
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
||||
|
||||
statistics.Reset();
|
||||
}
|
||||
|
||||
ReadOnlyMemory<float>[] inputBuffers = new ReadOnlyMemory<float>[Parameter.ChannelCount];
|
||||
Memory<float>[] outputBuffers = new Memory<float>[Parameter.ChannelCount];
|
||||
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
{
|
||||
inputBuffers[i] = context.GetBufferMemory(InputBufferIndices[i]);
|
||||
outputBuffers[i] = context.GetBufferMemory(OutputBufferIndices[i]);
|
||||
}
|
||||
|
||||
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||
{
|
||||
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||
{
|
||||
float inputSample = inputBuffers[channelIndex].Span[sampleIndex];
|
||||
|
||||
float sampleInputMax = Math.Abs(inputSample * Parameter.InputGain);
|
||||
|
||||
float inputCoefficient = Parameter.ReleaseCoefficient;
|
||||
|
||||
if (sampleInputMax > state.DectectorAverage[channelIndex])
|
||||
{
|
||||
inputCoefficient = Parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
state.DectectorAverage[channelIndex] += inputCoefficient * (sampleInputMax - state.DectectorAverage[channelIndex]);
|
||||
|
||||
float attenuation = 1.0f;
|
||||
|
||||
if (state.DectectorAverage[channelIndex] > Parameter.Threshold)
|
||||
{
|
||||
attenuation = Parameter.Threshold / state.DectectorAverage[channelIndex];
|
||||
}
|
||||
|
||||
float outputCoefficient = Parameter.ReleaseCoefficient;
|
||||
|
||||
if (state.CompressionGain[channelIndex] > attenuation)
|
||||
{
|
||||
outputCoefficient = Parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
state.CompressionGain[channelIndex] += outputCoefficient * (attenuation - state.CompressionGain[channelIndex]);
|
||||
|
||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||
|
||||
outputBuffers[channelIndex].Span[sampleIndex] = delayedSample * state.CompressionGain[channelIndex] * Parameter.OutputGain;
|
||||
|
||||
delayedSample = inputSample;
|
||||
|
||||
state.DelayedSampleBufferPosition[channelIndex]++;
|
||||
|
||||
while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
|
||||
{
|
||||
state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
|
||||
}
|
||||
|
||||
if (!ResultState.IsEmpty)
|
||||
{
|
||||
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
||||
|
||||
statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax);
|
||||
statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], state.CompressionGain[channelIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
{
|
||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||
{
|
||||
context.GetBufferMemory(InputBufferIndices[i]).CopyTo(context.GetBufferMemory(OutputBufferIndices[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
46
Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs
Normal file
46
Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// 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.Parameter.Effect;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||
{
|
||||
public class LimiterState
|
||||
{
|
||||
public float[] DectectorAverage;
|
||||
public float[] CompressionGain;
|
||||
public float[] DelayedSampleBuffer;
|
||||
public int[] DelayedSampleBufferPosition;
|
||||
|
||||
public LimiterState(ref LimiterParameter parameter, ulong workBuffer)
|
||||
{
|
||||
DectectorAverage = new float[parameter.ChannelCount];
|
||||
CompressionGain = new float[parameter.ChannelCount];
|
||||
DelayedSampleBuffer = new float[parameter.ChannelCount * parameter.DelayBufferSampleCountMax];
|
||||
DelayedSampleBufferPosition = new int[parameter.ChannelCount];
|
||||
|
||||
DectectorAverage.AsSpan().Fill(0.0f);
|
||||
CompressionGain.AsSpan().Fill(1.0f);
|
||||
DelayedSampleBufferPosition.AsSpan().Fill(0);
|
||||
|
||||
UpdateParameter(ref parameter);
|
||||
}
|
||||
|
||||
public void UpdateParameter(ref LimiterParameter parameter) {}
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ using System.Runtime.InteropServices;
|
|||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.AuxiliaryBuffer"/>.
|
||||
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.AuxiliaryBuffer"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct AuxiliaryBufferParameter
|
||||
|
|
|
@ -22,7 +22,7 @@ using System.Runtime.InteropServices;
|
|||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BiquadFilter"/>.
|
||||
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BiquadFilter"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct BiquadFilterEffectParameter
|
||||
|
|
|
@ -21,7 +21,7 @@ using System.Runtime.InteropServices;
|
|||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BufferMix"/>.
|
||||
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.BufferMix"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct BufferMixParameter
|
||||
|
|
|
@ -22,7 +22,7 @@ using System.Runtime.InteropServices;
|
|||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Delay"/>.
|
||||
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Delay"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct DelayParameter
|
||||
|
@ -103,7 +103,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
|||
/// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
|
||||
public bool IsChannelCountValid()
|
||||
{
|
||||
return EffectInParameter.IsChannelCountValid(ChannelCount);
|
||||
return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -112,7 +112,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
|||
/// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
|
||||
public bool IsChannelCountMaxValid()
|
||||
{
|
||||
return EffectInParameter.IsChannelCountValid(ChannelCountMax);
|
||||
return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
155
Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs
Normal file
155
Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs
Normal file
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// 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.Server.Effect;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Limiter"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct LimiterParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// The input channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
|
||||
/// </summary>
|
||||
public Array6<byte> Input;
|
||||
|
||||
/// <summary>
|
||||
/// The output channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
|
||||
/// </summary>
|
||||
public Array6<byte> Output;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of channels supported.
|
||||
/// </summary>
|
||||
public ushort ChannelCountMax;
|
||||
|
||||
/// <summary>
|
||||
/// The total channel count used.
|
||||
/// </summary>
|
||||
public ushort ChannelCount;
|
||||
|
||||
/// <summary>
|
||||
/// The target sample rate.
|
||||
/// </summary>
|
||||
/// <remarks>This is in kHz.</remarks>
|
||||
public int SampleRate;
|
||||
|
||||
/// <summary>
|
||||
/// The look ahead max time.
|
||||
/// <remarks>This is in microseconds.</remarks>
|
||||
/// </summary>
|
||||
public int LookAheadTimeMax;
|
||||
|
||||
/// <summary>
|
||||
/// The attack time.
|
||||
/// <remarks>This is in microseconds.</remarks>
|
||||
/// </summary>
|
||||
public int AttackTime;
|
||||
|
||||
/// <summary>
|
||||
/// The release time.
|
||||
/// <remarks>This is in microseconds.</remarks>
|
||||
/// </summary>
|
||||
public int ReleaseTime;
|
||||
|
||||
/// <summary>
|
||||
/// The look ahead time.
|
||||
/// <remarks>This is in microseconds.</remarks>
|
||||
/// </summary>
|
||||
public int LookAheadTime;
|
||||
|
||||
/// <summary>
|
||||
/// The attack coefficient.
|
||||
/// </summary>
|
||||
public float AttackCoefficient;
|
||||
|
||||
/// <summary>
|
||||
/// The release coefficient.
|
||||
/// </summary>
|
||||
public float ReleaseCoefficient;
|
||||
|
||||
/// <summary>
|
||||
/// The threshold.
|
||||
/// </summary>
|
||||
public float Threshold;
|
||||
|
||||
/// <summary>
|
||||
/// The input gain.
|
||||
/// </summary>
|
||||
public float InputGain;
|
||||
|
||||
/// <summary>
|
||||
/// The output gain.
|
||||
/// </summary>
|
||||
public float OutputGain;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum samples stored in the delay buffer.
|
||||
/// </summary>
|
||||
public int DelayBufferSampleCountMin;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum samples stored in the delay buffer.
|
||||
/// </summary>
|
||||
public int DelayBufferSampleCountMax;
|
||||
|
||||
/// <summary>
|
||||
/// The current usage status of the effect on the client side.
|
||||
/// </summary>
|
||||
public UsageState Status;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if the limiter effect should output statistics.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool StatisticsEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate to the DSP that the user did a statistics reset.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool StatisticsReset;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved/padding.
|
||||
/// </summary>
|
||||
private byte _reserved;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the <see cref="ChannelCount"/> is valid.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
|
||||
public bool IsChannelCountValid()
|
||||
{
|
||||
return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the <see cref="ChannelCountMax"/> is valid.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
|
||||
public bool IsChannelCountMaxValid()
|
||||
{
|
||||
return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
|
||||
}
|
||||
}
|
||||
}
|
48
Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs
Normal file
48
Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// 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.Common.Memory;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// Effect result state for <seealso cref="Common.EffectType.Limiter"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct LimiterStatistics
|
||||
{
|
||||
/// <summary>
|
||||
/// The max input sample value recorded by the limiter.
|
||||
/// </summary>
|
||||
public Array6<float> InputMax;
|
||||
|
||||
/// <summary>
|
||||
/// Compression gain min value.
|
||||
/// </summary>
|
||||
public Array6<float> CompressionGainMin;
|
||||
|
||||
/// <summary>
|
||||
/// Reset the statistics.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
InputMax.ToSpan().Fill(0.0f);
|
||||
CompressionGainMin.ToSpan().Fill(1.0f);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ using System.Runtime.InteropServices;
|
|||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Reverb3d"/>.
|
||||
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Reverb3d"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct Reverb3dParameter
|
||||
|
@ -129,7 +129,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
|||
/// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
|
||||
public bool IsChannelCountValid()
|
||||
{
|
||||
return EffectInParameter.IsChannelCountValid(ChannelCount);
|
||||
return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -138,7 +138,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
|||
/// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
|
||||
public bool IsChannelCountMaxValid()
|
||||
{
|
||||
return EffectInParameter.IsChannelCountValid(ChannelCountMax);
|
||||
return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ using System.Runtime.InteropServices;
|
|||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="EffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Reverb"/>.
|
||||
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Reverb"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ReverbParameter
|
||||
|
@ -121,7 +121,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
|||
/// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
|
||||
public bool IsChannelCountValid()
|
||||
{
|
||||
return EffectInParameter.IsChannelCountValid(ChannelCount);
|
||||
return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
|||
/// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
|
||||
public bool IsChannelCountMaxValid()
|
||||
{
|
||||
return EffectInParameter.IsChannelCountValid(ChannelCountMax);
|
||||
return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,10 @@ using System.Runtime.InteropServices;
|
|||
namespace Ryujinx.Audio.Renderer.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Input information for an effect.
|
||||
/// Input information for an effect version 1.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct EffectInParameter
|
||||
public struct EffectInParameterVersion1 : IEffectInParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of the effect.
|
||||
|
@ -85,11 +85,22 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
|||
[StructLayout(LayoutKind.Sequential, Size = 0xA0, Pack = 1)]
|
||||
private struct SpecificDataStruct { }
|
||||
|
||||
/// <summary>
|
||||
/// Specific data changing depending of the <see cref="Type"/>. See also the <see cref="Effect"/> namespace.
|
||||
/// </summary>
|
||||
public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart);
|
||||
|
||||
EffectType IEffectInParameter.Type => Type;
|
||||
|
||||
bool IEffectInParameter.IsNew => IsNew;
|
||||
|
||||
bool IEffectInParameter.IsEnabled => IsEnabled;
|
||||
|
||||
int IEffectInParameter.MixId => MixId;
|
||||
|
||||
ulong IEffectInParameter.BufferBase => BufferBase;
|
||||
|
||||
ulong IEffectInParameter.BufferSize => BufferSize;
|
||||
|
||||
uint IEffectInParameter.ProcessingOrder => ProcessingOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the given channel count is valid.
|
||||
/// </summary>
|
114
Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs
Normal file
114
Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs
Normal file
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// 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.Common.Utilities;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Input information for an effect version 2. (added with REV9)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct EffectInParameterVersion2 : IEffectInParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of the effect.
|
||||
/// </summary>
|
||||
public EffectType Type;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the effect is new.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool IsNew;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the effect must be active.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool IsEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved/padding.
|
||||
/// </summary>
|
||||
private byte _reserved1;
|
||||
|
||||
/// <summary>
|
||||
/// The target mix id of the effect.
|
||||
/// </summary>
|
||||
public int MixId;
|
||||
|
||||
/// <summary>
|
||||
/// Address of the processing workbuffer.
|
||||
/// </summary>
|
||||
/// <remarks>This is additional data that could be required by the effect processing.</remarks>
|
||||
public ulong BufferBase;
|
||||
|
||||
/// <summary>
|
||||
/// Size of the processing workbuffer.
|
||||
/// </summary>
|
||||
/// <remarks>This is additional data that could be required by the effect processing.</remarks>
|
||||
public ulong BufferSize;
|
||||
|
||||
/// <summary>
|
||||
/// Position of the effect while processing effects.
|
||||
/// </summary>
|
||||
public uint ProcessingOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved/padding.
|
||||
/// </summary>
|
||||
private uint _reserved2;
|
||||
|
||||
/// <summary>
|
||||
/// Specific data storage.
|
||||
/// </summary>
|
||||
private SpecificDataStruct _specificDataStart;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0xA0, Pack = 1)]
|
||||
private struct SpecificDataStruct { }
|
||||
|
||||
public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart);
|
||||
|
||||
EffectType IEffectInParameter.Type => Type;
|
||||
|
||||
bool IEffectInParameter.IsNew => IsNew;
|
||||
|
||||
bool IEffectInParameter.IsEnabled => IsEnabled;
|
||||
|
||||
int IEffectInParameter.MixId => MixId;
|
||||
|
||||
ulong IEffectInParameter.BufferBase => BufferBase;
|
||||
|
||||
ulong IEffectInParameter.BufferSize => BufferSize;
|
||||
|
||||
uint IEffectInParameter.ProcessingOrder => ProcessingOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the given channel count is valid.
|
||||
/// </summary>
|
||||
/// <param name="channelCount">The channel count to check</param>
|
||||
/// <returns>Returns true if the channel count is valid.</returns>
|
||||
public static bool IsChannelCountValid(int channelCount)
|
||||
{
|
||||
return channelCount == 1 || channelCount == 2 || channelCount == 4 || channelCount == 6;
|
||||
}
|
||||
}
|
||||
}
|
40
Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs
Normal file
40
Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs
Normal file
|
@ -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/>.
|
||||
//
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Output information for an effect version 1.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct EffectOutStatusVersion1 : IEffectOutStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Current effect state.
|
||||
/// </summary>
|
||||
public EffectState State;
|
||||
|
||||
/// <summary>
|
||||
/// Unused/Reserved.
|
||||
/// </summary>
|
||||
private unsafe fixed byte _reserved[15];
|
||||
|
||||
EffectState IEffectOutStatus.State { get => State; set => State = value; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
//
|
||||
//
|
||||
// Copyright (c) 2019-2021 Ryujinx
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
|
@ -20,27 +20,11 @@ using System.Runtime.InteropServices;
|
|||
namespace Ryujinx.Audio.Renderer.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Output information for an effect.
|
||||
/// Output information for an effect version 2. (added with REV9)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct EffectOutStatus
|
||||
public struct EffectOutStatusVersion2 : IEffectOutStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The state of an effect.
|
||||
/// </summary>
|
||||
public enum EffectState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The effect is enabled.
|
||||
/// </summary>
|
||||
Enabled = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The effect is disabled.
|
||||
/// </summary>
|
||||
Disabled = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current effect state.
|
||||
/// </summary>
|
||||
|
@ -50,5 +34,12 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
|||
/// Unused/Reserved.
|
||||
/// </summary>
|
||||
private unsafe fixed byte _reserved[15];
|
||||
|
||||
/// <summary>
|
||||
/// Current result state.
|
||||
/// </summary>
|
||||
public EffectResultState ResultState;
|
||||
|
||||
EffectState IEffectOutStatus.State { get => State; set => State = value; }
|
||||
}
|
||||
}
|
43
Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs
Normal file
43
Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// 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.Common.Utilities;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Effect result state (added in REV9).
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct EffectResultState
|
||||
{
|
||||
/// <summary>
|
||||
/// Specific data storage.
|
||||
/// </summary>
|
||||
private SpecificDataStruct _specificDataStart;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x80, Pack = 1)]
|
||||
private struct SpecificDataStruct { }
|
||||
|
||||
/// <summary>
|
||||
/// Specific data changing depending of the type of effect. See also the <see cref="Effect"/> namespace.
|
||||
/// </summary>
|
||||
public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart);
|
||||
}
|
||||
}
|
35
Ryujinx.Audio/Renderer/Parameter/EffectState.cs
Normal file
35
Ryujinx.Audio/Renderer/Parameter/EffectState.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// 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.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// The state of an effect.
|
||||
/// </summary>
|
||||
public enum EffectState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The effect is enabled.
|
||||
/// </summary>
|
||||
Enabled = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The effect is disabled.
|
||||
/// </summary>
|
||||
Disabled = 4
|
||||
}
|
||||
}
|
70
Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs
Normal file
70
Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// 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 System;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic interface to represent input information for an effect.
|
||||
/// </summary>
|
||||
public interface IEffectInParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of the effect.
|
||||
/// </summary>
|
||||
EffectType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the effect is new.
|
||||
/// </summary>
|
||||
bool IsNew { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the effect must be active.
|
||||
/// </summary>
|
||||
bool IsEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The target mix id of the effect.
|
||||
/// </summary>
|
||||
int MixId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Address of the processing workbuffer.
|
||||
/// </summary>
|
||||
/// <remarks>This is additional data that could be required by the effect processing.</remarks>
|
||||
ulong BufferBase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the processing workbuffer.
|
||||
/// </summary>
|
||||
/// <remarks>This is additional data that could be required by the effect processing.</remarks>
|
||||
ulong BufferSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Position of the effect while processing effects.
|
||||
/// </summary>
|
||||
uint ProcessingOrder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Specific data changing depending of the <see cref="Type"/>. See also the <see cref="Effect"/> namespace.
|
||||
/// </summary>
|
||||
Span<byte> SpecificData { get; }
|
||||
}
|
||||
}
|
30
Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs
Normal file
30
Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs
Normal file
|
@ -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/>.
|
||||
//
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Parameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic interface to represent output information for an effect.
|
||||
/// </summary>
|
||||
public interface IEffectOutStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Current effect state.
|
||||
/// </summary>
|
||||
EffectState State { get; set; }
|
||||
}
|
||||
}
|
|
@ -307,7 +307,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
|
||||
_upsamplerManager = new UpsamplerManager(upSamplerWorkBuffer, _upsamplerCount);
|
||||
|
||||
_effectContext.Initialize(parameter.EffectCount);
|
||||
_effectContext.Initialize(parameter.EffectCount, _behaviourContext.IsEffectInfoVersion2Supported() ? parameter.EffectCount : 0);
|
||||
_sinkContext.Initialize(parameter.SinkCount);
|
||||
|
||||
Memory<VoiceUpdateState> voiceUpdateStatesDsp = workBufferAllocator.Allocate<VoiceUpdateState>(parameter.VoiceCount, VoiceUpdateState.Align);
|
||||
|
@ -636,6 +636,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
|
||||
_voiceContext.UpdateForCommandGeneration();
|
||||
|
||||
if (_behaviourContext.IsEffectInfoVersion2Supported())
|
||||
{
|
||||
_effectContext.UpdateResultStateForCommandGeneration();
|
||||
}
|
||||
|
||||
ulong endTicks = GetSystemTicks();
|
||||
|
||||
_totalElapsedTicks = endTicks - startTicks;
|
||||
|
|
|
@ -89,10 +89,18 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
/// <remarks>This was added in system update 9.0.0</remarks>
|
||||
public const int Revision8 = 8 << 24;
|
||||
|
||||
/// <summary>
|
||||
/// REV9:
|
||||
/// EffectInfo parameters were revisited with a new revision (version 2) allowing more data control between the client and server.
|
||||
/// A new effect was added: Limiter. This effect is effectively implemented with a DRC while providing statistics on the processing on <see cref="Parameter.EffectOutStatusVersion2"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This was added in system update 12.0.0</remarks>
|
||||
public const int Revision9 = 9 << 24;
|
||||
|
||||
/// <summary>
|
||||
/// Last revision supported by the implementation.
|
||||
/// </summary>
|
||||
public const int LastRevision = Revision8;
|
||||
public const int LastRevision = Revision9;
|
||||
|
||||
/// <summary>
|
||||
/// Target revision magic supported by the implementation.
|
||||
|
@ -330,6 +338,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the audio renderer should use the new effect info format.
|
||||
/// </summary>
|
||||
/// <returns>True if the audio renderer should use the new effect info format.</returns>
|
||||
public bool IsEffectInfoVersion2Supported()
|
||||
{
|
||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision9);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
||||
/// </summary>
|
||||
|
|
|
@ -371,6 +371,49 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new <see cref="LimiterCommandVersion1"/>.
|
||||
/// </summary>
|
||||
/// <param name="bufferOffset">The target buffer offset.</param>
|
||||
/// <param name="parameter">The limiter parameter.</param>
|
||||
/// <param name="state">The limiter state.</param>
|
||||
/// <param name="isEnabled">Set to true if the effect should be active.</param>
|
||||
/// <param name="workBuffer">The work buffer to use for processing.</param>
|
||||
/// <param name="nodeId">The node id associated to this command.</param>
|
||||
public void GenerateLimiterEffectVersion1(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, bool isEnabled, ulong workBuffer, int nodeId)
|
||||
{
|
||||
if (parameter.IsChannelCountValid())
|
||||
{
|
||||
LimiterCommandVersion1 command = new LimiterCommandVersion1(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId);
|
||||
|
||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||
|
||||
AddCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new <see cref="LimiterCommandVersion2"/>.
|
||||
/// </summary>
|
||||
/// <param name="bufferOffset">The target buffer offset.</param>
|
||||
/// <param name="parameter">The limiter parameter.</param>
|
||||
/// <param name="state">The limiter state.</param>
|
||||
/// <param name="effectResultState">The DSP effect result state.</param>
|
||||
/// <param name="isEnabled">Set to true if the effect should be active.</param>
|
||||
/// <param name="workBuffer">The work buffer to use for processing.</param>
|
||||
/// <param name="nodeId">The node id associated to this command.</param>
|
||||
public void GenerateLimiterEffectVersion2(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, Memory<EffectResultState> effectResultState, bool isEnabled, ulong workBuffer, int nodeId)
|
||||
{
|
||||
if (parameter.IsChannelCountValid())
|
||||
{
|
||||
LimiterCommandVersion2 command = new LimiterCommandVersion2(bufferOffset, parameter, state, effectResultState, isEnabled, workBuffer, nodeId);
|
||||
|
||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||
|
||||
AddCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new <see cref="AuxiliaryBufferCommand"/>.
|
||||
/// </summary>
|
||||
|
|
|
@ -538,7 +538,25 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
}
|
||||
}
|
||||
|
||||
private void GenerateEffect(ref MixState mix, BaseEffect effect)
|
||||
private void GenerateLimiterEffect(uint bufferOffset, LimiterEffect effect, int nodeId, int effectId)
|
||||
{
|
||||
Debug.Assert(effect.Type == EffectType.Limiter);
|
||||
|
||||
ulong workBuffer = effect.GetWorkBuffer(-1);
|
||||
|
||||
if (_rendererContext.BehaviourContext.IsEffectInfoVersion2Supported())
|
||||
{
|
||||
Memory<EffectResultState> dspResultState = _effectContext.GetDspStateMemory(effectId);
|
||||
|
||||
_commandBuffer.GenerateLimiterEffectVersion2(bufferOffset, effect.Parameter, effect.State, dspResultState, effect.IsEnabled, workBuffer, nodeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_commandBuffer.GenerateLimiterEffectVersion1(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
|
||||
{
|
||||
int nodeId = mix.NodeId;
|
||||
|
||||
|
@ -576,6 +594,9 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
case EffectType.BiquadFilter:
|
||||
GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId);
|
||||
break;
|
||||
case EffectType.Limiter:
|
||||
GenerateLimiterEffect(mix.BufferOffset, (LimiterEffect)effect, nodeId, effectId);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
|
||||
}
|
||||
|
@ -611,7 +632,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
|
||||
if (!effect.ShouldSkip())
|
||||
{
|
||||
GenerateEffect(ref mix, effect);
|
||||
GenerateEffect(ref mix, effectOrder, effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,5 +176,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(LimiterCommandVersion1 command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(LimiterCommandVersion2 command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -540,5 +540,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(LimiterCommandVersion1 command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint Estimate(LimiterCommandVersion2 command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Renderer.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;
|
||||
|
@ -632,5 +633,127 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
throw new NotImplementedException($"{format}");
|
||||
}
|
||||
}
|
||||
|
||||
private uint EstimateLimiterCommandCommon(LimiterParameter parameter, bool enabled)
|
||||
{
|
||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||
|
||||
if (_sampleCount == 160)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
switch (parameter.ChannelCount)
|
||||
{
|
||||
case 1:
|
||||
return (uint)21392.0f;
|
||||
case 2:
|
||||
return (uint)26829.0f;
|
||||
case 4:
|
||||
return (uint)32405.0f;
|
||||
case 6:
|
||||
return (uint)52219.0f;
|
||||
default:
|
||||
throw new NotImplementedException($"{parameter.ChannelCount}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (parameter.ChannelCount)
|
||||
{
|
||||
case 1:
|
||||
return (uint)897.0f;
|
||||
case 2:
|
||||
return (uint)931.55f;
|
||||
case 4:
|
||||
return (uint)975.39f;
|
||||
case 6:
|
||||
return (uint)1016.8f;
|
||||
default:
|
||||
throw new NotImplementedException($"{parameter.ChannelCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
switch (parameter.ChannelCount)
|
||||
{
|
||||
case 1:
|
||||
return (uint)30556.0f;
|
||||
case 2:
|
||||
return (uint)39011.0f;
|
||||
case 4:
|
||||
return (uint)48270.0f;
|
||||
case 6:
|
||||
return (uint)76712.0f;
|
||||
default:
|
||||
throw new NotImplementedException($"{parameter.ChannelCount}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (parameter.ChannelCount)
|
||||
{
|
||||
case 1:
|
||||
return (uint)874.43f;
|
||||
case 2:
|
||||
return (uint)921.55f;
|
||||
case 4:
|
||||
return (uint)945.26f;
|
||||
case 6:
|
||||
return (uint)992.26f;
|
||||
default:
|
||||
throw new NotImplementedException($"{parameter.ChannelCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint Estimate(LimiterCommandVersion1 command)
|
||||
{
|
||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||
|
||||
return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled);
|
||||
}
|
||||
|
||||
public uint Estimate(LimiterCommandVersion2 command)
|
||||
{
|
||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||
|
||||
if (!command.Parameter.StatisticsEnabled || !command.IsEffectEnabled)
|
||||
{
|
||||
return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled);
|
||||
}
|
||||
|
||||
if (_sampleCount == 160)
|
||||
{
|
||||
switch (command.Parameter.ChannelCount)
|
||||
{
|
||||
case 1:
|
||||
return (uint)23309.0f;
|
||||
case 2:
|
||||
return (uint)29954.0f;
|
||||
case 4:
|
||||
return (uint)35807.0f;
|
||||
case 6:
|
||||
return (uint)58340.0f;
|
||||
default:
|
||||
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||
}
|
||||
}
|
||||
|
||||
switch (command.Parameter.ChannelCount)
|
||||
{
|
||||
case 1:
|
||||
return (uint)33526.0f;
|
||||
case 2:
|
||||
return (uint)43549.0f;
|
||||
case 4:
|
||||
return (uint)52190.0f;
|
||||
case 6:
|
||||
return (uint)85527.0f;
|
||||
default:
|
||||
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
return WorkBuffers[index].GetReference(true);
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
|
||||
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));
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
/// <returns>Returns true if the <see cref="EffectType"/> sent by the user matches the internal <see cref="EffectType"/>.</returns>
|
||||
public bool IsTypeValid(ref EffectInParameter parameter)
|
||||
public bool IsTypeValid<T>(ref T parameter) where T: unmanaged, IEffectInParameter
|
||||
{
|
||||
return parameter.Type == TargetEffectType;
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
/// Update the internal common parameters from a user parameter.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
protected void UpdateParameterBase(ref EffectInParameter parameter)
|
||||
protected void UpdateParameterBase<T>(ref T parameter) where T : unmanaged, IEffectInParameter
|
||||
{
|
||||
MixId = parameter.MixId;
|
||||
ProcessingOrder = parameter.ProcessingOrder;
|
||||
|
@ -154,12 +154,38 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the internal state from a user parameter.
|
||||
/// Initialize the given <paramref name="state"/> result state.
|
||||
/// </summary>
|
||||
/// <param name="state">The state to initalize</param>
|
||||
public virtual void InitializeResultState(ref EffectResultState state) {}
|
||||
|
||||
/// <summary>
|
||||
/// Update the <paramref name="destState"/> result state with <paramref name="srcState"/>.
|
||||
/// </summary>
|
||||
/// <param name="destState">The destination result state</param>
|
||||
/// <param name="srcState">The source result state</param>
|
||||
public virtual void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState) {}
|
||||
|
||||
/// <summary>
|
||||
/// Update the internal state from a user version 1 parameter.
|
||||
/// </summary>
|
||||
/// <param name="updateErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
/// <param name="mapper">The mapper to use.</param>
|
||||
public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
|
||||
public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
|
||||
updateErrorInfo = new ErrorInfo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the internal state from a user version 2 parameter.
|
||||
/// </summary>
|
||||
/// <param name="updateErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
/// <param name="mapper">The mapper to use.</param>
|
||||
public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||
{
|
||||
Debug.Assert(IsTypeValid(ref parameter));
|
||||
|
||||
|
@ -206,26 +232,26 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
/// </summary>
|
||||
/// <param name="outStatus">The given user output.</param>
|
||||
/// <param name="isAudioRendererActive">If set to true, the <see cref="AudioRenderSystem"/> is active.</param>
|
||||
public void StoreStatus(ref EffectOutStatus outStatus, bool isAudioRendererActive)
|
||||
public void StoreStatus<T>(ref T outStatus, bool isAudioRendererActive) where T: unmanaged, IEffectOutStatus
|
||||
{
|
||||
if (isAudioRendererActive)
|
||||
{
|
||||
if (UsageState == UsageState.Disabled)
|
||||
{
|
||||
outStatus.State = EffectOutStatus.EffectState.Disabled;
|
||||
outStatus.State = EffectState.Disabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
outStatus.State = EffectOutStatus.EffectState.Enabled;
|
||||
outStatus.State = EffectState.Enabled;
|
||||
}
|
||||
}
|
||||
else if (UsageState == UsageState.New)
|
||||
{
|
||||
outStatus.State = EffectOutStatus.EffectState.Enabled;
|
||||
outStatus.State = EffectState.Enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
outStatus.State = EffectOutStatus.EffectState.Disabled;
|
||||
outStatus.State = EffectState.Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,6 +275,8 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
return PerformanceDetailType.Reverb3d;
|
||||
case EffectType.BufferMix:
|
||||
return PerformanceDetailType.Mix;
|
||||
case EffectType.Limiter:
|
||||
return PerformanceDetailType.Limiter;
|
||||
default:
|
||||
throw new NotImplementedException($"{Type}");
|
||||
}
|
||||
|
|
|
@ -52,7 +52,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
|
||||
public override EffectType TargetEffectType => EffectType.BiquadFilter;
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
|
||||
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));
|
||||
|
||||
|
|
|
@ -36,7 +36,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
|
||||
public override EffectType TargetEffectType => EffectType.BufferMix;
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
|
||||
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));
|
||||
|
||||
|
|
|
@ -54,7 +54,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
return GetSingleBuffer();
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
|
||||
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));
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using Ryujinx.Audio.Renderer.Utils;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
|
@ -34,6 +37,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
/// </summary>
|
||||
private uint _effectCount;
|
||||
|
||||
private EffectResultState[] _resultStatesCpu;
|
||||
private EffectResultState[] _resultStatesDsp;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="EffectContext"/>.
|
||||
/// </summary>
|
||||
|
@ -47,7 +53,8 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
/// Initialize the <see cref="EffectContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="effectCount">The total effect count.</param>
|
||||
public void Initialize(uint effectCount)
|
||||
/// <param name="resultStateCount">The total result state count.</param>
|
||||
public void Initialize(uint effectCount, uint resultStateCount)
|
||||
{
|
||||
_effectCount = effectCount;
|
||||
_effects = new BaseEffect[effectCount];
|
||||
|
@ -56,6 +63,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
{
|
||||
_effects[i] = new BaseEffect();
|
||||
}
|
||||
|
||||
_resultStatesCpu = new EffectResultState[resultStateCount];
|
||||
_resultStatesDsp = new EffectResultState[resultStateCount];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -78,5 +88,53 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
|
||||
return ref _effects[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to use.</param>
|
||||
/// <returns>A reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.</returns>
|
||||
/// <remarks>The returned <see cref="EffectResultState"/> should only be used when updating the server state.</remarks>
|
||||
public ref EffectResultState GetState(int index)
|
||||
{
|
||||
Debug.Assert(index >= 0 && index < _resultStatesCpu.Length);
|
||||
|
||||
return ref _resultStatesCpu[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to use.</param>
|
||||
/// <returns>A reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.</returns>
|
||||
/// <remarks>The returned <see cref="EffectResultState"/> should only be used in the context of processing on the <see cref="Dsp.AudioProcessor"/>.</remarks>
|
||||
public ref EffectResultState GetDspState(int index)
|
||||
{
|
||||
Debug.Assert(index >= 0 && index < _resultStatesDsp.Length);
|
||||
|
||||
return ref _resultStatesDsp[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a memory instance to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to use.</param>
|
||||
/// <returns>A memory instance to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.</returns>
|
||||
/// <remarks>The returned <see cref="Memory{EffectResultState}"/> should only be used in the context of processing on the <see cref="Dsp.AudioProcessor"/>.</remarks>
|
||||
public Memory<EffectResultState> GetDspStateMemory(int index)
|
||||
{
|
||||
return SpanIOHelper.GetMemory(_resultStatesDsp.AsMemory(), index, (uint)_resultStatesDsp.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update internal state during command generation.
|
||||
/// </summary>
|
||||
public void UpdateResultStateForCommandGeneration()
|
||||
{
|
||||
for (int index = 0; index < _resultStatesCpu.Length; index++)
|
||||
{
|
||||
_effects[index].UpdateResultState(ref _resultStatesCpu[index], ref _resultStatesDsp[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
112
Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs
Normal file
112
Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs
Normal file
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// 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;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// Server state for a limiter effect.
|
||||
/// </summary>
|
||||
public class LimiterEffect : BaseEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// The limiter parameter.
|
||||
/// </summary>
|
||||
public LimiterParameter Parameter;
|
||||
|
||||
/// <summary>
|
||||
/// The limiter state.
|
||||
/// </summary>
|
||||
public Memory<LimiterState> State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="LimiterEffect"/>.
|
||||
/// </summary>
|
||||
public LimiterEffect()
|
||||
{
|
||||
State = new LimiterState[1];
|
||||
}
|
||||
|
||||
public override EffectType TargetEffectType => EffectType.Limiter;
|
||||
|
||||
public override ulong GetWorkBuffer(int index)
|
||||
{
|
||||
return GetSingleBuffer();
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
ref LimiterParameter limiterParameter = ref MemoryMarshal.Cast<byte, LimiterParameter>(parameter.SpecificData)[0];
|
||||
|
||||
updateErrorInfo = new BehaviourParameter.ErrorInfo();
|
||||
|
||||
UpdateParameterBase(ref parameter);
|
||||
|
||||
Parameter = limiterParameter;
|
||||
|
||||
IsEnabled = parameter.IsEnabled;
|
||||
|
||||
if (BufferUnmapped || parameter.IsNew)
|
||||
{
|
||||
UsageState = UsageState.New;
|
||||
Parameter.Status = UsageState.Invalid;
|
||||
|
||||
BufferUnmapped = !mapper.TryAttachBuffer(out updateErrorInfo, ref WorkBuffers[0], parameter.BufferBase, parameter.BufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateForCommandGeneration()
|
||||
{
|
||||
UpdateUsageStateForCommandGeneration();
|
||||
|
||||
Parameter.Status = UsageState.Enabled;
|
||||
Parameter.StatisticsReset = false;
|
||||
}
|
||||
|
||||
public override void InitializeResultState(ref EffectResultState state)
|
||||
{
|
||||
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(state.SpecificData)[0];
|
||||
|
||||
statistics.Reset();
|
||||
}
|
||||
|
||||
public override void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState)
|
||||
{
|
||||
destState = srcState;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,7 +53,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
return GetSingleBuffer();
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
|
||||
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));
|
||||
|
||||
|
|
|
@ -56,7 +56,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||
return GetSingleBuffer();
|
||||
}
|
||||
|
||||
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper)
|
||||
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));
|
||||
|
||||
|
|
|
@ -48,5 +48,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
uint Estimate(DeviceSinkCommand command);
|
||||
uint Estimate(DownMixSurroundToStereoCommand command);
|
||||
uint Estimate(UpsampleCommand command);
|
||||
uint Estimate(LimiterCommandVersion1 command);
|
||||
uint Estimate(LimiterCommandVersion2 command);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
using Ryujinx.Audio.Renderer.Common;
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server.Performance
|
||||
{
|
||||
|
|
|
@ -224,7 +224,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private static void ResetEffect(ref BaseEffect effect, ref EffectInParameter parameter, PoolMapper mapper)
|
||||
private static void ResetEffect<T>(ref BaseEffect effect, ref T parameter, PoolMapper mapper) where T: unmanaged, IEffectInParameter
|
||||
{
|
||||
effect.ForceUnmapBuffers(mapper);
|
||||
|
||||
|
@ -251,6 +251,9 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
case EffectType.BiquadFilter:
|
||||
effect = new BiquadFilterEffect();
|
||||
break;
|
||||
case EffectType.Limiter:
|
||||
effect = new LimiterEffect();
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"EffectType {parameter.Type} not implemented!");
|
||||
}
|
||||
|
@ -258,14 +261,26 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
|
||||
public ResultCode UpdateEffects(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools)
|
||||
{
|
||||
if (context.GetCount() * Unsafe.SizeOf<EffectInParameter>() != _inputHeader.EffectsSize)
|
||||
if (_behaviourContext.IsEffectInfoVersion2Supported())
|
||||
{
|
||||
return UpdateEffectsVersion2(context, isAudioRendererActive, memoryPools);
|
||||
}
|
||||
else
|
||||
{
|
||||
return UpdateEffectsVersion1(context, isAudioRendererActive, memoryPools);
|
||||
}
|
||||
}
|
||||
|
||||
public ResultCode UpdateEffectsVersion2(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools)
|
||||
{
|
||||
if (context.GetCount() * Unsafe.SizeOf<EffectInParameterVersion2>() != _inputHeader.EffectsSize)
|
||||
{
|
||||
return ResultCode.InvalidUpdateInfo;
|
||||
}
|
||||
|
||||
int initialOutputSize = _output.Length;
|
||||
|
||||
ReadOnlySpan<EffectInParameter> parameters = MemoryMarshal.Cast<byte, EffectInParameter>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span);
|
||||
ReadOnlySpan<EffectInParameterVersion2> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion2>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span);
|
||||
|
||||
_input = _input.Slice((int)_inputHeader.EffectsSize);
|
||||
|
||||
|
@ -273,9 +288,65 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
|
||||
for (int i = 0; i < context.GetCount(); i++)
|
||||
{
|
||||
EffectInParameter parameter = parameters[i];
|
||||
EffectInParameterVersion2 parameter = parameters[i];
|
||||
|
||||
ref EffectOutStatus outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatus>(ref _output)[0];
|
||||
ref EffectOutStatusVersion2 outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatusVersion2>(ref _output)[0];
|
||||
|
||||
ref BaseEffect effect = ref context.GetEffect(i);
|
||||
|
||||
if (!effect.IsTypeValid(ref parameter))
|
||||
{
|
||||
ResetEffect(ref effect, ref parameter, mapper);
|
||||
}
|
||||
|
||||
effect.Update(out ErrorInfo updateErrorInfo, ref parameter, mapper);
|
||||
|
||||
if (updateErrorInfo.ErrorCode != ResultCode.Success)
|
||||
{
|
||||
_behaviourContext.AppendError(ref updateErrorInfo);
|
||||
}
|
||||
|
||||
effect.StoreStatus(ref outStatus, isAudioRendererActive);
|
||||
|
||||
if (parameter.IsNew)
|
||||
{
|
||||
effect.InitializeResultState(ref context.GetDspState(i));
|
||||
effect.InitializeResultState(ref context.GetState(i));
|
||||
}
|
||||
|
||||
effect.UpdateResultState(ref outStatus.ResultState, ref context.GetState(i));
|
||||
}
|
||||
|
||||
int currentOutputSize = _output.Length;
|
||||
|
||||
OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatusVersion2>() * context.GetCount());
|
||||
OutputHeader.TotalSize += OutputHeader.EffectsSize;
|
||||
|
||||
Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ResultCode UpdateEffectsVersion1(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools)
|
||||
{
|
||||
if (context.GetCount() * Unsafe.SizeOf<EffectInParameterVersion1>() != _inputHeader.EffectsSize)
|
||||
{
|
||||
return ResultCode.InvalidUpdateInfo;
|
||||
}
|
||||
|
||||
int initialOutputSize = _output.Length;
|
||||
|
||||
ReadOnlySpan<EffectInParameterVersion1> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion1>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span);
|
||||
|
||||
_input = _input.Slice((int)_inputHeader.EffectsSize);
|
||||
|
||||
PoolMapper mapper = new PoolMapper(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
|
||||
|
||||
for (int i = 0; i < context.GetCount(); i++)
|
||||
{
|
||||
EffectInParameterVersion1 parameter = parameters[i];
|
||||
|
||||
ref EffectOutStatusVersion1 outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatusVersion1>(ref _output)[0];
|
||||
|
||||
ref BaseEffect effect = ref context.GetEffect(i);
|
||||
|
||||
|
@ -296,7 +367,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
|
||||
int currentOutputSize = _output.Length;
|
||||
|
||||
OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatus>() * context.GetCount());
|
||||
OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatusVersion1>() * context.GetCount());
|
||||
OutputHeader.TotalSize += OutputHeader.EffectsSize;
|
||||
|
||||
Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize);
|
||||
|
|
|
@ -9,7 +9,8 @@ namespace Ryujinx.Tests.Audio.Renderer
|
|||
[Test]
|
||||
public void EnsureTypeSize()
|
||||
{
|
||||
Assert.AreEqual(0xC0, Unsafe.SizeOf<EffectInParameter>());
|
||||
Assert.AreEqual(0xC0, Unsafe.SizeOf<EffectInParameterVersion1>());
|
||||
Assert.AreEqual(0xC0, Unsafe.SizeOf<EffectInParameterVersion2>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ namespace Ryujinx.Tests.Audio.Renderer
|
|||
[Test]
|
||||
public void EnsureTypeSize()
|
||||
{
|
||||
Assert.AreEqual(0x10, Unsafe.SizeOf<EffectOutStatus>());
|
||||
Assert.AreEqual(0x10, Unsafe.SizeOf<EffectOutStatusVersion1>());
|
||||
Assert.AreEqual(0x90, Unsafe.SizeOf<EffectOutStatusVersion2>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using NUnit.Framework;
|
||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Tests.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
class LimiterParameterTests
|
||||
{
|
||||
[Test]
|
||||
public void EnsureTypeSize()
|
||||
{
|
||||
Assert.AreEqual(0x44, Unsafe.SizeOf<LimiterParameter>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using NUnit.Framework;
|
||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Tests.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
class LimiterStatisticsTests
|
||||
{
|
||||
[Test]
|
||||
public void EnsureTypeSize()
|
||||
{
|
||||
Assert.AreEqual(0x30, Unsafe.SizeOf<LimiterStatistics>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue