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 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 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 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 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 bool IsSampleOffsetValid(SampleFormat format) { bool result; switch (format) { case SampleFormat.PcmInt16: result = IsSampleOffsetInRangeForPcm(); break; case SampleFormat.PcmFloat: result = IsSampleOffsetInRangeForPcm(); break; case SampleFormat.Adpcm: result = AdpcmHelper.GetAdpcmDataSize((int)StartSampleOffset) <= Size && AdpcmHelper.GetAdpcmDataSize((int)EndSampleOffset) <= Size; break; default: throw new NotImplementedException($"{format} not implemented!"); } return result; } } /// /// 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 } } }