using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Server.MemoryPool;
using Ryujinx.Audio.Renderer.Utils;
using System;
using System.Diagnostics;
using static Ryujinx.Audio.Renderer.Common.BehaviourParameter;
using DspAddress = System.UInt64;
namespace Ryujinx.Audio.Renderer.Server.Effect
{
///
/// Base class used as a server state for an effect.
///
public class BaseEffect
{
///
/// The of the effect.
///
public EffectType Type;
///
/// Set to true if the effect must be active.
///
public bool IsEnabled;
///
/// Set to true if the internal effect work buffers used wasn't mapped.
///
public bool BufferUnmapped;
///
/// The current state of the effect.
///
public UsageState UsageState;
///
/// The target mix id of the effect.
///
public int MixId;
///
/// Position of the effect while processing effects.
///
public uint ProcessingOrder;
///
/// Array of all the work buffer used by the effect.
///
protected AddressInfo[] WorkBuffers;
///
/// Create a new .
///
public BaseEffect()
{
Type = TargetEffectType;
UsageState = UsageState.Invalid;
IsEnabled = false;
BufferUnmapped = false;
MixId = Constants.UnusedMixId;
ProcessingOrder = uint.MaxValue;
WorkBuffers = new AddressInfo[2];
foreach (ref AddressInfo info in WorkBuffers.AsSpan())
{
info = AddressInfo.Create();
}
}
///
/// The target handled by this .
///
public virtual EffectType TargetEffectType => EffectType.Invalid;
///
/// Check if the sent by the user match the internal .
///
/// The user parameter.
/// Returns true if the sent by the user matches the internal .
public bool IsTypeValid(ref T parameter) where T : unmanaged, IEffectInParameter
{
return parameter.Type == TargetEffectType;
}
///
/// Update the usage state during command generation.
///
protected void UpdateUsageStateForCommandGeneration()
{
UsageState = IsEnabled ? UsageState.Enabled : UsageState.Disabled;
}
///
/// Update the internal common parameters from a user parameter.
///
/// The user parameter.
protected void UpdateParameterBase(ref T parameter) where T : unmanaged, IEffectInParameter
{
MixId = parameter.MixId;
ProcessingOrder = parameter.ProcessingOrder;
}
///
/// Force unmap all the work buffers.
///
/// The mapper to use.
public void ForceUnmapBuffers(PoolMapper mapper)
{
foreach (ref AddressInfo info in WorkBuffers.AsSpan())
{
if (info.GetReference(false) != 0)
{
mapper.ForceUnmap(ref info);
}
}
}
///
/// Check if the effect needs to be skipped.
///
/// Returns true if the effect needs to be skipped.
public bool ShouldSkip()
{
return BufferUnmapped;
}
///
/// Update the state during command generation.
///
public virtual void UpdateForCommandGeneration()
{
Debug.Assert(Type == TargetEffectType);
}
///
/// Initialize the given result state.
///
/// The state to initalize
public virtual void InitializeResultState(ref EffectResultState state) { }
///
/// Update the result state with .
///
/// The destination result state
/// The source result state
public virtual void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState) { }
///
/// Update the internal state from a user version 1 parameter.
///
/// The possible that was generated.
/// The user parameter.
/// The mapper to use.
public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
{
Debug.Assert(IsTypeValid(ref parameter));
updateErrorInfo = new ErrorInfo();
}
///
/// Update the internal state from a user version 2 parameter.
///
/// The possible that was generated.
/// The user parameter.
/// The mapper to use.
public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
{
Debug.Assert(IsTypeValid(ref parameter));
updateErrorInfo = new ErrorInfo();
}
///
/// Get the work buffer DSP address at the given index.
///
/// The index of the work buffer
/// The work buffer DSP address at the given index.
public virtual DspAddress GetWorkBuffer(int index)
{
throw new InvalidOperationException();
}
///
/// Get the first work buffer DSP address.
///
/// The first work buffer DSP address.
protected DspAddress GetSingleBuffer()
{
if (IsEnabled)
{
return WorkBuffers[0].GetReference(true);
}
if (UsageState != UsageState.Disabled)
{
DspAddress address = WorkBuffers[0].GetReference(false);
ulong size = WorkBuffers[0].Size;
if (address != 0 && size != 0)
{
AudioProcessorMemoryManager.InvalidateDataCache(address, size);
}
}
return 0;
}
///
/// Store the output status to the given user output.
///
/// The given user output.
/// If set to true, the is active.
public void StoreStatus(ref T outStatus, bool isAudioRendererActive) where T : unmanaged, IEffectOutStatus
{
if (isAudioRendererActive)
{
if (UsageState == UsageState.Disabled)
{
outStatus.State = EffectState.Disabled;
}
else
{
outStatus.State = EffectState.Enabled;
}
}
else if (UsageState == UsageState.New)
{
outStatus.State = EffectState.Enabled;
}
else
{
outStatus.State = EffectState.Disabled;
}
}
///
/// Get the associated to the of this effect.
///
/// The associated to the of this effect.
public PerformanceDetailType GetPerformanceDetailType()
{
return Type switch
{
EffectType.BiquadFilter => PerformanceDetailType.BiquadFilter,
EffectType.AuxiliaryBuffer => PerformanceDetailType.Aux,
EffectType.Delay => PerformanceDetailType.Delay,
EffectType.Reverb => PerformanceDetailType.Reverb,
EffectType.Reverb3d => PerformanceDetailType.Reverb3d,
EffectType.BufferMix => PerformanceDetailType.Mix,
EffectType.Limiter => PerformanceDetailType.Limiter,
EffectType.CaptureBuffer => PerformanceDetailType.CaptureBuffer,
EffectType.Compressor => PerformanceDetailType.Compressor,
_ => throw new NotImplementedException($"{Type}"),
};
}
}
}