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 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 ClearMixBufferCommand(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 DepopPrepareCommand(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 PerformanceCommand(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 VolumeRampCommand(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 DataSourceVersion2Command(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 PcmInt16DataSourceCommandVersion1(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 PcmFloatDataSourceCommandVersion1(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 AdpcmDataSourceCommandVersion1(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 BiquadFilterCommand(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 GenerateGroupedBiquadFilter(int baseIndex, ReadOnlySpan filters, Memory biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan isInitialized, int nodeId) { GroupedBiquadFilterCommand command = new GroupedBiquadFilterCommand(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, Span previousVolume, Span volume, Memory state, int nodeId) { MixRampGroupedCommand command = new MixRampGroupedCommand(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 MixRampCommand(previousVolume, volume, inputBufferIndex, outputBufferIndex, lastSampleIndex, state, 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 GenerateDepopForMixBuffersCommand(Memory depopBuffer, uint bufferOffset, uint bufferCount, int nodeId, uint sampleRate) { DepopForMixBuffersCommand command = new DepopForMixBuffersCommand(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 CopyMixBufferCommand(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 MixCommand(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 ReverbCommand(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 Reverb3dCommand(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 DelayCommand(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 LimiterCommandVersion1(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 LimiterCommandVersion2(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 AuxiliaryBufferCommand(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 CaptureBufferCommand(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 CompressorCommand(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 VolumeCommand(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 CircularBufferSinkCommand(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 DownMixSurroundToStereoCommand(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 UpsampleCommand(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 DeviceSinkCommand(bufferOffset, sink, sessionId, buffer, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } }