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() { switch (Type) { case EffectType.BiquadFilter: return PerformanceDetailType.BiquadFilter; case EffectType.AuxiliaryBuffer: return PerformanceDetailType.Aux; case EffectType.Delay: return PerformanceDetailType.Delay; case EffectType.Reverb: return PerformanceDetailType.Reverb; case EffectType.Reverb3d: return PerformanceDetailType.Reverb3d; case EffectType.BufferMix: return PerformanceDetailType.Mix; case EffectType.Limiter: return PerformanceDetailType.Limiter; case EffectType.CaptureBuffer: return PerformanceDetailType.CaptureBuffer; case EffectType.Compressor: return PerformanceDetailType.Compressor; default: throw new NotImplementedException($"{Type}"); } } } }