//
// 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 .
//
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;
default:
throw new NotImplementedException($"{Type}");
}
}
}
}