using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Dsp.Command; using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Server.Performance; using Ryujinx.Audio.Renderer.Server.Sink; using Ryujinx.Audio.Renderer.Server.Upsampler; using Ryujinx.Audio.Renderer.Server.Voice; using System; using CpuAddress = System.UInt64; namespace Ryujinx.Audio.Renderer.Server { /// /// An API to generate commands and aggregate them into a . /// public class CommandBuffer { /// /// The command processing time estimator in use. /// private readonly ICommandProcessingTimeEstimator _commandProcessingTimeEstimator; /// /// The estimated total processing time. /// public uint EstimatedProcessingTime { get; set; } /// /// The command list that is populated by the . /// public CommandList CommandList { get; } /// /// Create a new . /// /// The command list that will store the generated commands. /// The command processing time estimator to use. public CommandBuffer(CommandList commandList, ICommandProcessingTimeEstimator commandProcessingTimeEstimator) { CommandList = commandList; EstimatedProcessingTime = 0; _commandProcessingTimeEstimator = commandProcessingTimeEstimator; } /// /// Add a new generated command to the . /// /// The command to add. private void AddCommand(ICommand command) { EstimatedProcessingTime += command.EstimatedProcessingTime; CommandList.AddCommand(command); } /// /// Generate a new . /// /// The node id associated to this command. public void GenerateClearMixBuffer(int nodeId) { ClearMixBufferCommand command = new(nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The voice state associated. /// The depop buffer. /// The buffer count. /// The target buffer offset. /// The node id associated to this command. /// Set to true if the voice was playing previously. public void GenerateDepopPrepare(Memory state, Memory depopBuffer, uint bufferCount, uint bufferOffset, int nodeId, bool wasPlaying) { DepopPrepareCommand command = new(state, depopBuffer, bufferCount, bufferOffset, nodeId, wasPlaying); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The . /// The performance operation to perform. /// The node id associated to this command. public void GeneratePerformance(ref PerformanceEntryAddresses performanceEntryAddresses, PerformanceCommand.Type type, int nodeId) { PerformanceCommand command = new(ref performanceEntryAddresses, type, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The previous volume. /// The new volume. /// The index of the mix buffer to use. /// The node id associated to this command. public void GenerateVolumeRamp(float previousVolume, float volume, uint bufferIndex, int nodeId) { VolumeRampCommand command = new(previousVolume, volume, bufferIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The to generate the command from. /// The to generate the command from. /// The output buffer index to use. /// The target channel index. /// The node id associated to this command. public void GenerateDataSourceVersion2(ref VoiceState voiceState, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { DataSourceVersion2Command command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The to generate the command from. /// The to generate the command from. /// The output buffer index to use. /// The target channel index. /// The node id associated to this command. public void GeneratePcmInt16DataSourceVersion1(ref VoiceState voiceState, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { PcmInt16DataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The to generate the command from. /// The to generate the command from. /// The output buffer index to use. /// The target channel index. /// The node id associated to this command. public void GeneratePcmFloatDataSourceVersion1(ref VoiceState voiceState, Memory state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { PcmFloatDataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The to generate the command from. /// The to generate the command from. /// The output buffer index to use. /// The node id associated to this command. public void GenerateAdpcmDataSourceVersion1(ref VoiceState voiceState, Memory state, ushort outputBufferIndex, int nodeId) { AdpcmDataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The base index of the input and output buffer. /// The biquad filter parameter. /// The biquad state. /// The input buffer offset. /// The output buffer offset. /// Set to true if the biquad filter state needs to be initialized. /// The node id associated to this command. public void GenerateBiquadFilter(int baseIndex, ref BiquadFilterParameter filter, Memory biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, bool needInitialization, int nodeId) { BiquadFilterCommand command = new(baseIndex, ref filter, biquadFilterStateMemory, inputBufferOffset, outputBufferOffset, needInitialization, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The base index of the input and output buffer. /// The biquad filter parameters. /// The biquad states. /// The input buffer offset. /// The output buffer offset. /// Set to true if the biquad filter state is initialized. /// The node id associated to this command. public void GenerateMultiTapBiquadFilter(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) { MultiTapBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The mix buffer count. /// The base input index. /// The base output index. /// The previous volume. /// The new volume. /// The to generate the command from. /// The node id associated to this command. public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, ReadOnlySpan previousVolume, ReadOnlySpan volume, Memory state, int nodeId) { MixRampGroupedCommand command = new(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The previous volume. /// The new volume. /// The input buffer index. /// The output buffer index. /// The index in the array to store the ramped sample. /// The to generate the command from. /// The node id associated to this command. public void GenerateMixRamp(float previousVolume, float volume, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory state, int nodeId) { MixRampCommand command = new(previousVolume, volume, inputBufferIndex, outputBufferIndex, lastSampleIndex, state, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The previous volume. /// The new volume. /// The input buffer index. /// The output buffer index. /// The index in the array to store the ramped sample. /// The to generate the command from. /// The biquad filter parameter. /// The biquad state. /// The previous biquad state. /// Set to true if the biquad filter state needs to be initialized. /// Set to true if the mix has volume ramp, and should be taken into account. /// Set to true if the buffer is the first mix buffer. /// The node id associated to this command. public void GenerateBiquadFilterAndMix( float previousVolume, float volume, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory state, ref BiquadFilterParameter filter, Memory biquadFilterState, Memory previousBiquadFilterState, bool needInitialization, bool hasVolumeRamp, bool isFirstMixBuffer, int nodeId) { BiquadFilterAndMixCommand command = new( previousVolume, volume, inputBufferIndex, outputBufferIndex, lastSampleIndex, state, ref filter, biquadFilterState, previousBiquadFilterState, needInitialization, hasVolumeRamp, isFirstMixBuffer, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The previous volume. /// The new volume. /// The input buffer index. /// The output buffer index. /// The index in the array to store the ramped sample. /// The to generate the command from. /// First biquad filter parameter. /// Second biquad filter parameter. /// First biquad state. /// Second biquad state. /// First previous biquad state. /// Second previous biquad state. /// Set to true if the first biquad filter state needs to be initialized. /// Set to true if the second biquad filter state needs to be initialized. /// Set to true if the mix has volume ramp, and should be taken into account. /// Set to true if the buffer is the first mix buffer. /// The node id associated to this command. public void GenerateMultiTapBiquadFilterAndMix( float previousVolume, float volume, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory state, ref BiquadFilterParameter filter0, ref BiquadFilterParameter filter1, Memory biquadFilterState0, Memory biquadFilterState1, Memory previousBiquadFilterState0, Memory previousBiquadFilterState1, bool needInitialization0, bool needInitialization1, bool hasVolumeRamp, bool isFirstMixBuffer, int nodeId) { MultiTapBiquadFilterAndMixCommand command = new( previousVolume, volume, inputBufferIndex, outputBufferIndex, lastSampleIndex, state, ref filter0, ref filter1, biquadFilterState0, biquadFilterState1, previousBiquadFilterState0, previousBiquadFilterState1, needInitialization0, needInitialization1, hasVolumeRamp, isFirstMixBuffer, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The depop buffer. /// The target buffer offset. /// The buffer count. /// The node id associated to this command. /// The target sample rate in use. public void GenerateDepopForMixBuffers(Memory depopBuffer, uint bufferOffset, uint bufferCount, int nodeId, uint sampleRate) { DepopForMixBuffersCommand command = new(depopBuffer, bufferOffset, bufferCount, nodeId, sampleRate); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The input buffer index. /// The output buffer index. /// The node id associated to this command. public void GenerateCopyMixBuffer(uint inputBufferIndex, uint outputBufferIndex, int nodeId) { CopyMixBufferCommand command = new(inputBufferIndex, outputBufferIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The input buffer index. /// The output buffer index. /// The node id associated to this command. /// The mix volume. public void GenerateMix(uint inputBufferIndex, uint outputBufferIndex, int nodeId, float volume) { MixCommand command = new(inputBufferIndex, outputBufferIndex, nodeId, volume); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Generate a new . /// /// The target buffer offset. /// The reverb parameter. /// The reverb state. /// Set to true if the effect should be active. /// The work buffer to use for processing. /// The node id associated to this command. /// If set to true, the long size pre-delay is supported. /// If set to true, the new effect channel mapping for 5.1 is supported. public void GenerateReverbEffect(uint bufferOffset, ReverbParameter parameter, Memory state, bool isEnabled, CpuAddress workBuffer, int nodeId, bool isLongSizePreDelaySupported, bool newEffectChannelMappingSupported) { if (parameter.IsChannelCountValid()) { ReverbCommand command = new(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, isLongSizePreDelaySupported, newEffectChannelMappingSupported); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } /// /// Generate a new . /// /// The target buffer offset. /// The reverb 3d parameter. /// The reverb 3d state. /// Set to true if the effect should be active. /// The work buffer to use for processing. /// The node id associated to this command. /// If set to true, the new effect channel mapping for 5.1 is supported. public void GenerateReverb3dEffect(uint bufferOffset, Reverb3dParameter parameter, Memory state, bool isEnabled, CpuAddress workBuffer, int nodeId, bool newEffectChannelMappingSupported) { if (parameter.IsChannelCountValid()) { Reverb3dCommand command = new(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, newEffectChannelMappingSupported); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } /// /// Generate a new . /// /// The target buffer offset. /// The delay parameter. /// The delay state. /// Set to true if the effect should be active. /// The work buffer to use for processing. /// The node id associated to this command. /// If set to true, the new effect channel mapping for 5.1 is supported. public void GenerateDelayEffect(uint bufferOffset, DelayParameter parameter, Memory state, bool isEnabled, CpuAddress workBuffer, int nodeId, bool newEffectChannelMappingSupported) { if (parameter.IsChannelCountValid()) { DelayCommand command = new(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, newEffectChannelMappingSupported); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } /// /// Generate a new . /// /// The target buffer offset. /// The limiter parameter. /// The limiter state. /// Set to true if the effect should be active. /// The work buffer to use for processing. /// The node id associated to this command. public void GenerateLimiterEffectVersion1(uint bufferOffset, LimiterParameter parameter, Memory state, bool isEnabled, ulong workBuffer, int nodeId) { if (parameter.IsChannelCountValid()) { LimiterCommandVersion1 command = new(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } /// /// Generate a new . /// /// The target buffer offset. /// The limiter parameter. /// The limiter state. /// The DSP effect result state. /// Set to true if the effect should be active. /// The work buffer to use for processing. /// The node id associated to this command. public void GenerateLimiterEffectVersion2(uint bufferOffset, LimiterParameter parameter, Memory state, Memory effectResultState, bool isEnabled, ulong workBuffer, int nodeId) { if (parameter.IsChannelCountValid()) { LimiterCommandVersion2 command = new(bufferOffset, parameter, state, effectResultState, isEnabled, workBuffer, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } /// /// Generate a new . /// /// The target buffer offset. /// The input buffer offset. /// The output buffer offset. /// The aux state. /// Set to true if the effect should be active. /// The limit of the circular buffer. /// The guest address of the output buffer. /// The guest address of the input buffer. /// The count to add on the offset after write/read operations. /// The write offset. /// The node id associated to this command. public void GenerateAuxEffect(uint bufferOffset, byte inputBufferOffset, byte outputBufferOffset, ref AuxiliaryBufferAddresses state, bool isEnabled, uint countMax, CpuAddress outputBuffer, CpuAddress inputBuffer, uint updateCount, uint writeOffset, int nodeId) { if (state.SendBufferInfoBase != 0 && state.ReturnBufferInfoBase != 0) { AuxiliaryBufferCommand command = new(bufferOffset, inputBufferOffset, outputBufferOffset, ref state, isEnabled, countMax, outputBuffer, inputBuffer, updateCount, writeOffset, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } /// /// Generate a new . /// /// The target buffer offset. /// The input buffer offset. /// The capture state. /// Set to true if the effect should be active. /// The limit of the circular buffer. /// The guest address of the output buffer. /// The count to add on the offset after write operations. /// The write offset. /// The node id associated to this command. public void GenerateCaptureEffect(uint bufferOffset, byte inputBufferOffset, ulong sendBufferInfo, bool isEnabled, uint countMax, CpuAddress outputBuffer, uint updateCount, uint writeOffset, int nodeId) { if (sendBufferInfo != 0) { CaptureBufferCommand command = new(bufferOffset, inputBufferOffset, sendBufferInfo, isEnabled, countMax, outputBuffer, updateCount, writeOffset, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory state, bool isEnabled, int nodeId) { if (parameter.IsChannelCountValid()) { CompressorCommand command = new(bufferOffset, parameter, state, isEnabled, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } /// /// Generate a new . /// /// The target volume to apply. /// The offset of the mix buffer. /// The node id associated to this command. public void GenerateVolume(float volume, uint bufferOffset, int nodeId) { VolumeCommand command = new(volume, bufferOffset, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The offset of the mix buffer. /// The of the circular buffer. /// The node id associated to this command. public void GenerateCircularBuffer(uint bufferOffset, CircularBufferSink sink, int nodeId) { CircularBufferSinkCommand command = new(bufferOffset, ref sink.Parameter, ref sink.CircularBufferAddressInfo, sink.CurrentWriteOffset, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The offset of the mix buffer. /// The input buffer offset. /// The output buffer offset. /// The downmixer parameters to use. /// The node id associated to this command. public void GenerateDownMixSurroundToStereo(uint bufferOffset, Span inputBufferOffset, Span outputBufferOffset, float[] downMixParameter, int nodeId) { DownMixSurroundToStereoCommand command = new(bufferOffset, inputBufferOffset, outputBufferOffset, downMixParameter, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The offset of the mix buffer. /// The associated. /// The total input count. /// The input buffer mix offset. /// The buffer count per sample. /// The source sample count. /// The source sample rate. /// The node id associated to this command. public void GenerateUpsample(uint bufferOffset, UpsamplerState upsampler, uint inputCount, Span inputBufferOffset, uint bufferCountPerSample, uint sampleCount, uint sampleRate, int nodeId) { UpsampleCommand command = new(bufferOffset, upsampler, inputCount, inputBufferOffset, bufferCountPerSample, sampleCount, sampleRate, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } /// /// Create a new . /// /// The offset of the mix buffer. /// The of the device sink. /// The current audio renderer session id. /// The mix buffer in use. /// The node id associated to this command. public void GenerateDeviceSink(uint bufferOffset, DeviceSink sink, int sessionId, Memory buffer, int nodeId) { DeviceSinkCommand command = new(bufferOffset, sink, sessionId, buffer, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } }