using Ryujinx.Audio.Renderer.Parameter; using System; using System.Diagnostics; using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Renderer.Server.Splitter { /// /// Server state for a splitter destination. /// public ref struct SplitterDestination { private ref SplitterDestinationVersion1 _v1; private ref SplitterDestinationVersion2 _v2; /// /// Checks if the splitter destination data reference is null. /// public bool IsNull => Unsafe.IsNullRef(ref _v1) && Unsafe.IsNullRef(ref _v2); /// /// The splitter unique id. /// public int Id { get { if (Unsafe.IsNullRef(ref _v2)) { if (Unsafe.IsNullRef(ref _v1)) { return 0; } else { return _v1.Id; } } else { return _v2.Id; } } } /// /// The mix to output the result of the splitter. /// public int DestinationId { get { if (Unsafe.IsNullRef(ref _v2)) { if (Unsafe.IsNullRef(ref _v1)) { return 0; } else { return _v1.DestinationId; } } else { return _v2.DestinationId; } } } /// /// Mix buffer volumes. /// /// Used when a splitter id is specified in the mix. public Span MixBufferVolume { get { if (Unsafe.IsNullRef(ref _v2)) { if (Unsafe.IsNullRef(ref _v1)) { return Span.Empty; } else { return _v1.MixBufferVolume; } } else { return _v2.MixBufferVolume; } } } /// /// Previous mix buffer volumes. /// /// Used when a splitter id is specified in the mix. public Span PreviousMixBufferVolume { get { if (Unsafe.IsNullRef(ref _v2)) { if (Unsafe.IsNullRef(ref _v1)) { return Span.Empty; } else { return _v1.PreviousMixBufferVolume; } } else { return _v2.PreviousMixBufferVolume; } } } /// /// Get the of the next element or null if not present. /// public readonly SplitterDestination Next { get { unsafe { if (Unsafe.IsNullRef(ref _v2)) { if (Unsafe.IsNullRef(ref _v1)) { return new SplitterDestination(); } else { return new SplitterDestination(ref _v1.Next); } } else { return new SplitterDestination(ref _v2.Next); } } } } /// /// Creates a new splitter destination wrapper for the version 1 splitter destination data. /// /// Version 1 splitter destination data public SplitterDestination(ref SplitterDestinationVersion1 v1) { _v1 = ref v1; _v2 = ref Unsafe.NullRef(); } /// /// Creates a new splitter destination wrapper for the version 2 splitter destination data. /// /// Version 2 splitter destination data public SplitterDestination(ref SplitterDestinationVersion2 v2) { _v1 = ref Unsafe.NullRef(); _v2 = ref v2; } /// /// Creates a new splitter destination wrapper for the splitter destination data. /// /// Version 1 splitter destination data /// Version 2 splitter destination data public unsafe SplitterDestination(SplitterDestinationVersion1* v1, SplitterDestinationVersion2* v2) { _v1 = ref Unsafe.AsRef(v1); _v2 = ref Unsafe.AsRef(v2); } /// /// Update the splitter destination data from user parameter. /// /// The user parameter. /// Indicates that the audio renderer revision in use supports explicitly resetting the volume. public void Update(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter { if (Unsafe.IsNullRef(ref _v2)) { _v1.Update(parameter, isPrevVolumeResetSupported); } else { _v2.Update(parameter, isPrevVolumeResetSupported); } } /// /// Update the internal state of the instance. /// public void UpdateInternalState() { if (Unsafe.IsNullRef(ref _v2)) { _v1.UpdateInternalState(); } else { _v2.UpdateInternalState(); } } /// /// Set the update internal state marker. /// public void MarkAsNeedToUpdateInternalState() { if (Unsafe.IsNullRef(ref _v2)) { _v1.MarkAsNeedToUpdateInternalState(); } else { _v2.MarkAsNeedToUpdateInternalState(); } } /// /// Return true if the splitter destination is used and has a destination. /// /// True if the splitter destination is used and has a destination. public readonly bool IsConfigured() { return Unsafe.IsNullRef(ref _v2) ? _v1.IsConfigured() : _v2.IsConfigured(); } /// /// Get the volume for a given destination. /// /// The destination index to use. /// The volume for the given destination. public float GetMixVolume(int destinationIndex) { return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolume(destinationIndex) : _v2.GetMixVolume(destinationIndex); } /// /// Get the previous volume for a given destination. /// /// The destination index to use. /// The volume for the given destination. public float GetMixVolumePrev(int destinationIndex) { return Unsafe.IsNullRef(ref _v2) ? _v1.GetMixVolumePrev(destinationIndex) : _v2.GetMixVolumePrev(destinationIndex); } /// /// Clear the volumes. /// public void ClearVolumes() { if (Unsafe.IsNullRef(ref _v2)) { _v1.ClearVolumes(); } else { _v2.ClearVolumes(); } } /// /// Link the next element to the given splitter destination. /// /// The given splitter destination to link. public void Link(SplitterDestination next) { if (Unsafe.IsNullRef(ref _v2)) { Debug.Assert(!Unsafe.IsNullRef(ref next._v1)); _v1.Link(ref next._v1); } else { Debug.Assert(!Unsafe.IsNullRef(ref next._v2)); _v2.Link(ref next._v2); } } /// /// Remove the link to the next element. /// public void Unlink() { if (Unsafe.IsNullRef(ref _v2)) { _v1.Unlink(); } else { _v2.Unlink(); } } /// /// Checks if any biquad filter is enabled. /// /// True if any biquad filter is enabled. public bool IsBiquadFilterEnabled() { return !Unsafe.IsNullRef(ref _v2) && _v2.IsBiquadFilterEnabled(); } /// /// Checks if any biquad filter was previously enabled. /// /// True if any biquad filter was previously enabled. public bool IsBiquadFilterEnabledPrev() { return !Unsafe.IsNullRef(ref _v2) && _v2.IsBiquadFilterEnabledPrev(); } /// /// Gets the biquad filter parameters. /// /// Biquad filter index (0 or 1). /// Biquad filter parameters. public ref BiquadFilterParameter GetBiquadFilterParameter(int index) { Debug.Assert(!Unsafe.IsNullRef(ref _v2)); return ref _v2.GetBiquadFilterParameter(index); } /// /// Checks if any biquad filter was previously enabled. /// /// Biquad filter index (0 or 1). public void UpdateBiquadFilterEnabledPrev(int index) { if (!Unsafe.IsNullRef(ref _v2)) { _v2.UpdateBiquadFilterEnabledPrev(index); } } /// /// Get the reference for the version 1 splitter destination data, or null if version 2 is being used or the destination is null. /// /// Reference for the version 1 splitter destination data. public ref SplitterDestinationVersion1 GetV1RefOrNull() { return ref _v1; } /// /// Get the reference for the version 2 splitter destination data, or null if version 1 is being used or the destination is null. /// /// Reference for the version 2 splitter destination data. public ref SplitterDestinationVersion2 GetV2RefOrNull() { return ref _v2; } } }