using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Common.Memory;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter
{
///
/// Input information for a voice.
///
[StructLayout(LayoutKind.Sequential, Size = 0x170, Pack = 1)]
public struct VoiceInParameter
{
///
/// Id of the voice.
///
public int Id;
///
/// Node id of the voice.
///
public int NodeId;
///
/// Set to true if the voice is new.
///
[MarshalAs(UnmanagedType.I1)]
public bool IsNew;
///
/// Set to true if the voice is used.
///
[MarshalAs(UnmanagedType.I1)]
public bool InUse;
///
/// The voice wanted by the user.
///
public PlayState PlayState;
///
/// The of the voice.
///
public SampleFormat SampleFormat;
///
/// The sample rate of the voice.
///
public uint SampleRate;
///
/// The priority of the voice.
///
public uint Priority;
///
/// Target sorting position of the voice. (Used to sort voices with the same )
///
public uint SortingOrder;
///
/// The total channel count used.
///
public uint ChannelCount;
///
/// The pitch used on the voice.
///
public float Pitch;
///
/// The output volume of the voice.
///
public float Volume;
///
/// Biquad filters to apply to the output of the voice.
///
public Array2 BiquadFilters;
///
/// Total count of of the voice.
///
public uint WaveBuffersCount;
///
/// Current playing of the voice.
///
public uint WaveBuffersIndex;
///
/// Reserved/unused.
///
private readonly uint _reserved1;
///
/// User state address required by the data source.
///
/// Only used for as the address of the GC-ADPCM coefficients.
public ulong DataSourceStateAddress;
///
/// User state size required by the data source.
///
/// Only used for as the size of the GC-ADPCM coefficients.
public ulong DataSourceStateSize;
///
/// The target mix id of the voice.
///
public int MixId;
///
/// The target splitter id of the voice.
///
public uint SplitterId;
///
/// The wavebuffer parameters of this voice.
///
public Array4 WaveBuffers;
///
/// The channel resource ids associated to the voice.
///
public Array6 ChannelResourceIds;
///
/// Reset the voice drop flag during voice server update.
///
[MarshalAs(UnmanagedType.I1)]
public bool ResetVoiceDropFlag;
///
/// Flush the amount of wavebuffer specified. This will result in the wavebuffer being skipped and marked played.
///
/// This was added on REV5.
public byte FlushWaveBufferCount;
///
/// Reserved/unused.
///
private readonly ushort _reserved2;
///
/// Change the behaviour of the voice.
///
/// This was added on REV5.
public DecodingBehaviour DecodingBehaviourFlags;
///
/// Change the Sample Rate Conversion (SRC) quality of the voice.
///
/// This was added on REV8.
public SampleRateConversionQuality SrcQuality;
///
/// This was previously used for opus codec support on the Audio Renderer and was removed on REV3.
///
public uint ExternalContext;
///
/// This was previously used for opus codec support on the Audio Renderer and was removed on REV3.
///
public uint ExternalContextSize;
///
/// Reserved/unused.
///
private unsafe fixed uint _reserved3[2];
///
/// Input information for a voice wavebuffer.
///
[StructLayout(LayoutKind.Sequential, Size = 0x38, Pack = 1)]
public struct WaveBufferInternal
{
///
/// Address of the wavebuffer data.
///
public ulong Address;
///
/// Size of the wavebuffer data.
///
public ulong Size;
///
/// Offset of the first sample to play.
///
public uint StartSampleOffset;
///
/// Offset of the last sample to play.
///
public uint EndSampleOffset;
///
/// If set to true, the wavebuffer will loop when reaching .
///
///
/// Starting with REV8, you can specify how many times to loop the wavebuffer () and where it should start and end when looping ( and )
///
[MarshalAs(UnmanagedType.I1)]
public bool ShouldLoop;
///
/// Indicates that this is the last wavebuffer to play of the voice.
///
[MarshalAs(UnmanagedType.I1)]
public bool IsEndOfStream;
///
/// Indicates if the server should update its internal state.
///
[MarshalAs(UnmanagedType.I1)]
public bool SentToServer;
///
/// Reserved/unused.
///
private readonly byte _reserved;
///
/// If set to anything other than 0, specifies how many times to loop the wavebuffer.
///
/// This was added in REV8.
public int LoopCount;
///
/// Address of the context used by the sample decoder.
///
/// This is only currently used by .
public ulong ContextAddress;
///
/// Size of the context used by the sample decoder.
///
/// This is only currently used by .
public ulong ContextSize;
///
/// If set to anything other than 0, specifies the offset of the first sample to play when looping.
///
/// This was added in REV8.
public uint LoopFirstSampleOffset;
///
/// If set to anything other than 0, specifies the offset of the last sample to play when looping.
///
/// This was added in REV8.
public uint LoopLastSampleOffset;
///
/// Check if the sample offsets are in a valid range for generic PCM.
///
/// The PCM sample type
/// Returns true if the sample offset are in range of the size.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private readonly bool IsSampleOffsetInRangeForPcm() where T : unmanaged
{
uint dataTypeSize = (uint)Unsafe.SizeOf();
return StartSampleOffset * dataTypeSize <= Size &&
EndSampleOffset * dataTypeSize <= Size;
}
///
/// Check if the sample offsets are in a valid range for the given .
///
/// The target
/// Returns true if the sample offset are in range of the size.
public readonly bool IsSampleOffsetValid(SampleFormat format)
{
return format switch
{
SampleFormat.PcmInt16 => IsSampleOffsetInRangeForPcm(),
SampleFormat.PcmFloat => IsSampleOffsetInRangeForPcm(),
SampleFormat.Adpcm => AdpcmHelper.GetAdpcmDataSize((int)StartSampleOffset) <= Size && AdpcmHelper.GetAdpcmDataSize((int)EndSampleOffset) <= Size,
_ => throw new NotImplementedException($"{format} not implemented!"),
};
}
}
///
/// Flag altering the behaviour of wavebuffer decoding.
///
[Flags]
public enum DecodingBehaviour : ushort
{
///
/// Default decoding behaviour.
///
Default = 0,
///
/// Reset the played samples accumulator when looping.
///
PlayedSampleCountResetWhenLooping = 1,
///
/// Skip pitch and Sample Rate Conversion (SRC).
///
SkipPitchAndSampleRateConversion = 2,
}
///
/// Specify the quality to use during Sample Rate Conversion (SRC) and pitch handling.
///
/// This was added in REV8.
public enum SampleRateConversionQuality : byte
{
///
/// Resample interpolating 4 samples per output sample.
///
Default,
///
/// Resample interpolating 8 samples per output sample.
///
High,
///
/// Resample interpolating 1 samples per output sample.
///
Low,
}
}
}