// // 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.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 { /// <summary> /// Base class used as a server state for an effect. /// </summary> public class BaseEffect { /// <summary> /// The <see cref="EffectType"/> of the effect. /// </summary> public EffectType Type; /// <summary> /// Set to true if the effect must be active. /// </summary> public bool IsEnabled; /// <summary> /// Set to true if the internal effect work buffers used wasn't mapped. /// </summary> public bool BufferUnmapped; /// <summary> /// The current state of the effect. /// </summary> public UsageState UsageState; /// <summary> /// The target mix id of the effect. /// </summary> public int MixId; /// <summary> /// Position of the effect while processing effects. /// </summary> public uint ProcessingOrder; /// <summary> /// Array of all the work buffer used by the effect. /// </summary> protected AddressInfo[] WorkBuffers; /// <summary> /// Create a new <see cref="BaseEffect"/>. /// </summary> public BaseEffect() { Type = TargetEffectType; UsageState = UsageState.Invalid; IsEnabled = false; BufferUnmapped = false; MixId = RendererConstants.UnusedMixId; ProcessingOrder = uint.MaxValue; WorkBuffers = new AddressInfo[2]; foreach (ref AddressInfo info in WorkBuffers.AsSpan()) { info = AddressInfo.Create(); } } /// <summary> /// The target <see cref="EffectType"/> handled by this <see cref="BaseEffect"/>. /// </summary> public virtual EffectType TargetEffectType => EffectType.Invalid; /// <summary> /// Check if the <see cref="EffectType"/> sent by the user match the internal <see cref="EffectType"/>. /// </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) { return parameter.Type == TargetEffectType; } /// <summary> /// Update the usage state during command generation. /// </summary> protected void UpdateUsageStateForCommandGeneration() { UsageState = IsEnabled ? UsageState.Enabled : UsageState.Disabled; } /// <summary> /// Update the internal common parameters from a user parameter. /// </summary> /// <param name="parameter">The user parameter.</param> protected void UpdateParameterBase(ref EffectInParameter parameter) { MixId = parameter.MixId; ProcessingOrder = parameter.ProcessingOrder; } /// <summary> /// Force unmap all the work buffers. /// </summary> /// <param name="mapper">The mapper to use.</param> public void ForceUnmapBuffers(PoolMapper mapper) { foreach (ref AddressInfo info in WorkBuffers.AsSpan()) { if (info.GetReference(false) != 0) { mapper.ForceUnmap(ref info); } } } /// <summary> /// Check if the effect needs to be skipped. /// </summary> /// <returns>Returns true if the effect needs to be skipped.</returns> public bool ShouldSkip() { return BufferUnmapped; } /// <summary> /// Update the <see cref="BaseEffect"/> state during command generation. /// </summary> public virtual void UpdateForCommandGeneration() { Debug.Assert(Type == TargetEffectType); } /// <summary> /// Update the internal state from a user 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) { Debug.Assert(IsTypeValid(ref parameter)); updateErrorInfo = new ErrorInfo(); } /// <summary> /// Get the work buffer DSP address at the given index. /// </summary> /// <param name="index">The index of the work buffer</param> /// <returns>The work buffer DSP address at the given index.</returns> public virtual DspAddress GetWorkBuffer(int index) { throw new InvalidOperationException(); } /// <summary> /// Get the first work buffer DSP address. /// </summary> /// <returns>The first work buffer DSP address.</returns> 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; } /// <summary> /// Store the output status to the given user output. /// </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) { if (isAudioRendererActive) { if (UsageState == UsageState.Disabled) { outStatus.State = EffectOutStatus.EffectState.Disabled; } else { outStatus.State = EffectOutStatus.EffectState.Enabled; } } else if (UsageState == UsageState.New) { outStatus.State = EffectOutStatus.EffectState.Enabled; } else { outStatus.State = EffectOutStatus.EffectState.Disabled; } } /// <summary> /// Get the <see cref="PerformanceDetailType"/> associated to the <see cref="Type"/> of this effect. /// </summary> /// <returns>The <see cref="PerformanceDetailType"/> associated to the <see cref="Type"/> of this effect.</returns> 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; default: throw new NotImplementedException($"{Type}"); } } } }