| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274 |
- using Ryujinx.Audio.Common;
- 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.Server.Effect;
- using Ryujinx.Audio.Renderer.Server.Mix;
- using Ryujinx.Audio.Renderer.Server.Performance;
- using Ryujinx.Audio.Renderer.Server.Sink;
- using Ryujinx.Audio.Renderer.Server.Splitter;
- using Ryujinx.Audio.Renderer.Server.Voice;
- using Ryujinx.Audio.Renderer.Utils;
- using System;
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- namespace Ryujinx.Audio.Renderer.Server
- {
- public class CommandGenerator
- {
- private readonly CommandBuffer _commandBuffer;
- private readonly RendererSystemContext _rendererContext;
- private readonly VoiceContext _voiceContext;
- private readonly MixContext _mixContext;
- private readonly EffectContext _effectContext;
- private readonly SinkContext _sinkContext;
- private readonly SplitterContext _splitterContext;
- private readonly PerformanceManager _performanceManager;
- public CommandGenerator(CommandBuffer commandBuffer, RendererSystemContext rendererContext, VoiceContext voiceContext, MixContext mixContext, EffectContext effectContext, SinkContext sinkContext, SplitterContext splitterContext, PerformanceManager performanceManager)
- {
- _commandBuffer = commandBuffer;
- _rendererContext = rendererContext;
- _voiceContext = voiceContext;
- _mixContext = mixContext;
- _effectContext = effectContext;
- _sinkContext = sinkContext;
- _splitterContext = splitterContext;
- _performanceManager = performanceManager;
- _commandBuffer.GenerateClearMixBuffer(Constants.InvalidNodeId);
- }
- private void GenerateDataSource(ref VoiceState voiceState, Memory<VoiceUpdateState> dspState, int channelIndex)
- {
- if (voiceState.MixId != Constants.UnusedMixId)
- {
- ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
- _commandBuffer.GenerateDepopPrepare(
- dspState,
- _rendererContext.DepopBuffer,
- mix.BufferCount,
- mix.BufferOffset,
- voiceState.NodeId,
- voiceState.WasPlaying);
- }
- else if (voiceState.SplitterId != Constants.UnusedSplitterId)
- {
- int destinationId = 0;
- while (true)
- {
- SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++);
- if (destination.IsNull)
- {
- break;
- }
- if (destination.IsConfigured())
- {
- int mixId = destination.DestinationId;
- if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
- {
- ref MixState mix = ref _mixContext.GetState(mixId);
- _commandBuffer.GenerateDepopPrepare(
- dspState,
- _rendererContext.DepopBuffer,
- mix.BufferCount,
- mix.BufferOffset,
- voiceState.NodeId,
- voiceState.WasPlaying);
- destination.MarkAsNeedToUpdateInternalState();
- }
- }
- }
- }
- if (!voiceState.WasPlaying)
- {
- Debug.Assert(voiceState.SampleFormat != SampleFormat.Adpcm || channelIndex == 0);
- if (_rendererContext.BehaviourContext.IsWaveBufferVersion2Supported())
- {
- _commandBuffer.GenerateDataSourceVersion2(
- ref voiceState,
- dspState,
- (ushort)_rendererContext.MixBufferCount,
- (ushort)channelIndex,
- voiceState.NodeId);
- }
- else
- {
- switch (voiceState.SampleFormat)
- {
- case SampleFormat.PcmInt16:
- _commandBuffer.GeneratePcmInt16DataSourceVersion1(
- ref voiceState,
- dspState,
- (ushort)_rendererContext.MixBufferCount,
- (ushort)channelIndex,
- voiceState.NodeId);
- break;
- case SampleFormat.PcmFloat:
- _commandBuffer.GeneratePcmFloatDataSourceVersion1(
- ref voiceState,
- dspState,
- (ushort)_rendererContext.MixBufferCount,
- (ushort)channelIndex,
- voiceState.NodeId);
- break;
- case SampleFormat.Adpcm:
- _commandBuffer.GenerateAdpcmDataSourceVersion1(
- ref voiceState,
- dspState,
- (ushort)_rendererContext.MixBufferCount,
- voiceState.NodeId);
- break;
- default:
- throw new NotImplementedException($"Unsupported data source {voiceState.SampleFormat}");
- }
- }
- }
- }
- private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory<VoiceUpdateState> state, int baseIndex, int bufferOffset, int nodeId)
- {
- bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing();
- if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable)
- {
- Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
- Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
- _commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
- }
- else
- {
- for (int i = 0; i < voiceState.BiquadFilters.Length; i++)
- {
- ref BiquadFilterParameter filter = ref voiceState.BiquadFilters[i];
- if (filter.Enable)
- {
- Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
- Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
- _commandBuffer.GenerateBiquadFilter(
- baseIndex,
- ref filter,
- stateMemory.Slice(i, 1),
- bufferOffset,
- bufferOffset,
- !voiceState.BiquadFilterNeedInitialization[i],
- nodeId);
- }
- }
- }
- }
- private void GenerateVoiceMixWithSplitter(
- SplitterDestination destination,
- Memory<VoiceUpdateState> state,
- uint bufferOffset,
- uint bufferCount,
- uint bufferIndex,
- int nodeId)
- {
- ReadOnlySpan<float> mixVolumes = destination.MixBufferVolume;
- ReadOnlySpan<float> previousMixVolumes = destination.PreviousMixBufferVolume;
- ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0);
- ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1);
- Memory<BiquadFilterState> bqfState = _splitterContext.GetBiquadFilterState(destination);
- bool isFirstMixBuffer = true;
- for (int i = 0; i < bufferCount; i++)
- {
- float previousMixVolume = previousMixVolumes[i];
- float mixVolume = mixVolumes[i];
- if (mixVolume != 0.0f || previousMixVolume != 0.0f)
- {
- if (bqf0.Enable && bqf1.Enable)
- {
- _commandBuffer.GenerateMultiTapBiquadFilterAndMix(
- previousMixVolume,
- mixVolume,
- bufferIndex,
- bufferOffset + (uint)i,
- i,
- state,
- ref bqf0,
- ref bqf1,
- bqfState[..1],
- bqfState.Slice(1, 1),
- bqfState.Slice(2, 1),
- bqfState.Slice(3, 1),
- !destination.IsBiquadFilterEnabledPrev(),
- !destination.IsBiquadFilterEnabledPrev(),
- true,
- isFirstMixBuffer,
- nodeId);
- destination.UpdateBiquadFilterEnabledPrev(0);
- destination.UpdateBiquadFilterEnabledPrev(1);
- }
- else if (bqf0.Enable)
- {
- _commandBuffer.GenerateBiquadFilterAndMix(
- previousMixVolume,
- mixVolume,
- bufferIndex,
- bufferOffset + (uint)i,
- i,
- state,
- ref bqf0,
- bqfState[..1],
- bqfState.Slice(1, 1),
- !destination.IsBiquadFilterEnabledPrev(),
- true,
- isFirstMixBuffer,
- nodeId);
- destination.UpdateBiquadFilterEnabledPrev(0);
- }
- else if (bqf1.Enable)
- {
- _commandBuffer.GenerateBiquadFilterAndMix(
- previousMixVolume,
- mixVolume,
- bufferIndex,
- bufferOffset + (uint)i,
- i,
- state,
- ref bqf1,
- bqfState[..1],
- bqfState.Slice(1, 1),
- !destination.IsBiquadFilterEnabledPrev(),
- true,
- isFirstMixBuffer,
- nodeId);
- destination.UpdateBiquadFilterEnabledPrev(1);
- }
- isFirstMixBuffer = false;
- }
- }
- }
- private void GenerateVoiceMix(
- ReadOnlySpan<float> mixVolumes,
- ReadOnlySpan<float> previousMixVolumes,
- Memory<VoiceUpdateState> state,
- uint bufferOffset,
- uint bufferCount,
- uint bufferIndex,
- int nodeId)
- {
- if (bufferCount > Constants.VoiceChannelCountMax)
- {
- _commandBuffer.GenerateMixRampGrouped(
- bufferCount,
- bufferIndex,
- bufferOffset,
- previousMixVolumes,
- mixVolumes,
- state,
- nodeId);
- }
- else
- {
- for (int i = 0; i < bufferCount; i++)
- {
- float previousMixVolume = previousMixVolumes[i];
- float mixVolume = mixVolumes[i];
- if (mixVolume != 0.0f || previousMixVolume != 0.0f)
- {
- _commandBuffer.GenerateMixRamp(
- previousMixVolume,
- mixVolume,
- bufferIndex,
- bufferOffset + (uint)i,
- i,
- state,
- nodeId);
- }
- }
- }
- }
- private void GenerateVoice(ref VoiceState voiceState)
- {
- int nodeId = voiceState.NodeId;
- uint channelsCount = voiceState.ChannelsCount;
- for (int channelIndex = 0; channelIndex < channelsCount; channelIndex++)
- {
- Memory<VoiceUpdateState> dspStateMemory = _voiceContext.GetUpdateStateForDsp(voiceState.ChannelResourceIds[channelIndex]);
- ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(voiceState.ChannelResourceIds[channelIndex]);
- PerformanceDetailType dataSourceDetailType = PerformanceDetailType.Adpcm;
- if (voiceState.SampleFormat == SampleFormat.PcmInt16)
- {
- dataSourceDetailType = PerformanceDetailType.PcmInt16;
- }
- else if (voiceState.SampleFormat == SampleFormat.PcmFloat)
- {
- dataSourceDetailType = PerformanceDetailType.PcmFloat;
- }
- bool performanceInitialized = false;
- PerformanceEntryAddresses performanceEntry = new();
- if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, dataSourceDetailType, PerformanceEntryType.Voice, nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- GenerateDataSource(ref voiceState, dspStateMemory, channelIndex);
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- if (voiceState.WasPlaying)
- {
- voiceState.PreviousVolume = 0.0f;
- }
- else if (voiceState.HasAnyDestination())
- {
- performanceInitialized = false;
- if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.BiquadFilter, PerformanceEntryType.Voice, nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- GenerateBiquadFilterForVoice(ref voiceState, dspStateMemory, (int)_rendererContext.MixBufferCount, channelIndex, nodeId);
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- performanceInitialized = false;
- if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.VolumeRamp, PerformanceEntryType.Voice, nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- _commandBuffer.GenerateVolumeRamp(
- voiceState.PreviousVolume,
- voiceState.Volume,
- _rendererContext.MixBufferCount + (uint)channelIndex,
- nodeId);
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- voiceState.PreviousVolume = voiceState.Volume;
- if (voiceState.MixId == Constants.UnusedMixId)
- {
- if (voiceState.SplitterId != Constants.UnusedSplitterId)
- {
- int destinationId = channelIndex;
- while (true)
- {
- SplitterDestination destination = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId);
- if (destination.IsNull)
- {
- break;
- }
- destinationId += (int)channelsCount;
- if (destination.IsConfigured())
- {
- int mixId = destination.DestinationId;
- if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
- {
- ref MixState mix = ref _mixContext.GetState(mixId);
- if (destination.IsBiquadFilterEnabled())
- {
- GenerateVoiceMixWithSplitter(
- destination,
- dspStateMemory,
- mix.BufferOffset,
- mix.BufferCount,
- _rendererContext.MixBufferCount + (uint)channelIndex,
- nodeId);
- }
- else
- {
- GenerateVoiceMix(
- destination.MixBufferVolume,
- destination.PreviousMixBufferVolume,
- dspStateMemory,
- mix.BufferOffset,
- mix.BufferCount,
- _rendererContext.MixBufferCount + (uint)channelIndex,
- nodeId);
- }
- destination.MarkAsNeedToUpdateInternalState();
- }
- }
- }
- }
- }
- else
- {
- ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
- performanceInitialized = false;
- if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.Voice, nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- GenerateVoiceMix(
- channelResource.Mix.AsSpan(),
- channelResource.PreviousMix.AsSpan(),
- dspStateMemory,
- mix.BufferOffset,
- mix.BufferCount,
- _rendererContext.MixBufferCount + (uint)channelIndex,
- nodeId);
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- channelResource.UpdateState();
- }
- for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++)
- {
- voiceState.BiquadFilterNeedInitialization[i] = voiceState.BiquadFilters[i].Enable;
- }
- }
- }
- }
- public void GenerateVoices()
- {
- for (int i = 0; i < _voiceContext.GetCount(); i++)
- {
- ref VoiceState sortedState = ref _voiceContext.GetSortedState(i);
- if (!sortedState.ShouldSkip() && sortedState.UpdateForCommandGeneration(_voiceContext))
- {
- int nodeId = sortedState.NodeId;
- PerformanceEntryAddresses performanceEntry = new();
- bool performanceInitialized = false;
- if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.Voice, nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- GenerateVoice(ref sortedState);
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- }
- }
- _splitterContext.UpdateInternalState();
- }
- public void GeneratePerformance(ref PerformanceEntryAddresses performanceEntryAddresses, PerformanceCommand.Type type, int nodeId)
- {
- _commandBuffer.GeneratePerformance(ref performanceEntryAddresses, type, nodeId);
- }
- private void GenerateBufferMixerEffect(int bufferOffset, BufferMixEffect effect, int nodeId)
- {
- Debug.Assert(effect.Type == EffectType.BufferMix);
- if (effect.IsEnabled)
- {
- for (int i = 0; i < effect.Parameter.MixesCount; i++)
- {
- if (effect.Parameter.Volumes[i] != 0.0f)
- {
- _commandBuffer.GenerateMix(
- (uint)bufferOffset + effect.Parameter.Input[i],
- (uint)bufferOffset + effect.Parameter.Output[i],
- nodeId,
- effect.Parameter.Volumes[i]);
- }
- }
- }
- }
- private void GenerateAuxEffect(uint bufferOffset, AuxiliaryBufferEffect effect, int nodeId)
- {
- Debug.Assert(effect.Type == EffectType.AuxiliaryBuffer);
- if (effect.IsEnabled)
- {
- effect.GetWorkBuffer(0);
- effect.GetWorkBuffer(1);
- }
- if (effect.State.SendBufferInfoBase != 0 && effect.State.ReturnBufferInfoBase != 0)
- {
- int i = 0;
- uint writeOffset = 0;
- for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
- {
- uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
- uint updateCount;
- if (channelIndex != 1)
- {
- updateCount = 0;
- }
- else
- {
- updateCount = newUpdateCount;
- }
- _commandBuffer.GenerateAuxEffect(
- bufferOffset,
- effect.Parameter.Input[i],
- effect.Parameter.Output[i],
- ref effect.State,
- effect.IsEnabled,
- effect.Parameter.BufferStorageSize,
- effect.State.SendBufferInfoBase,
- effect.State.ReturnBufferInfoBase,
- updateCount,
- writeOffset,
- nodeId);
- writeOffset = newUpdateCount;
- i++;
- }
- }
- }
- private void GenerateDelayEffect(uint bufferOffset, DelayEffect effect, int nodeId, bool newEffectChannelMappingSupported)
- {
- Debug.Assert(effect.Type == EffectType.Delay);
- ulong workBuffer = effect.GetWorkBuffer(-1);
- _commandBuffer.GenerateDelayEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, newEffectChannelMappingSupported);
- }
- private void GenerateReverbEffect(uint bufferOffset, ReverbEffect effect, int nodeId, bool isLongSizePreDelaySupported, bool newEffectChannelMappingSupported)
- {
- Debug.Assert(effect.Type == EffectType.Reverb);
- ulong workBuffer = effect.GetWorkBuffer(-1);
- _commandBuffer.GenerateReverbEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, isLongSizePreDelaySupported, newEffectChannelMappingSupported);
- }
- private void GenerateReverb3dEffect(uint bufferOffset, Reverb3dEffect effect, int nodeId, bool newEffectChannelMappingSupported)
- {
- Debug.Assert(effect.Type == EffectType.Reverb3d);
- ulong workBuffer = effect.GetWorkBuffer(-1);
- _commandBuffer.GenerateReverb3dEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, newEffectChannelMappingSupported);
- }
- private void GenerateBiquadFilterEffect(uint bufferOffset, BiquadFilterEffect effect, int nodeId)
- {
- Debug.Assert(effect.Type == EffectType.BiquadFilter);
- if (effect.IsEnabled)
- {
- bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
- (effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed());
- BiquadFilterParameter parameter = new()
- {
- Enable = true,
- };
- effect.Parameter.Denominator.AsSpan().CopyTo(parameter.Denominator.AsSpan());
- effect.Parameter.Numerator.AsSpan().CopyTo(parameter.Numerator.AsSpan());
- for (int i = 0; i < effect.Parameter.ChannelCount; i++)
- {
- _commandBuffer.GenerateBiquadFilter(
- (int)bufferOffset,
- ref parameter,
- effect.State.Slice(i, 1),
- effect.Parameter.Input[i],
- effect.Parameter.Output[i],
- needInitialization,
- nodeId);
- }
- }
- else
- {
- for (int i = 0; i < effect.Parameter.ChannelCount; i++)
- {
- uint inputBufferIndex = bufferOffset + effect.Parameter.Input[i];
- uint outputBufferIndex = bufferOffset + effect.Parameter.Output[i];
- // If the input and output isn't the same, generate a command.
- if (inputBufferIndex != outputBufferIndex)
- {
- _commandBuffer.GenerateCopyMixBuffer(inputBufferIndex, outputBufferIndex, nodeId);
- }
- }
- }
- }
- private void GenerateLimiterEffect(uint bufferOffset, LimiterEffect effect, int nodeId, int effectId)
- {
- Debug.Assert(effect.Type == EffectType.Limiter);
- ulong workBuffer = effect.GetWorkBuffer(-1);
- if (_rendererContext.BehaviourContext.IsEffectInfoVersion2Supported())
- {
- Memory<EffectResultState> dspResultState;
- if (effect.Parameter.StatisticsEnabled)
- {
- dspResultState = _effectContext.GetDspStateMemory(effectId);
- }
- else
- {
- dspResultState = Memory<EffectResultState>.Empty;
- }
- _commandBuffer.GenerateLimiterEffectVersion2(bufferOffset, effect.Parameter, effect.State, dspResultState, effect.IsEnabled, workBuffer, nodeId);
- }
- else
- {
- _commandBuffer.GenerateLimiterEffectVersion1(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId);
- }
- }
- private void GenerateCaptureEffect(uint bufferOffset, CaptureBufferEffect effect, int nodeId)
- {
- Debug.Assert(effect.Type == EffectType.CaptureBuffer);
- if (effect.IsEnabled)
- {
- effect.GetWorkBuffer(0);
- }
- if (effect.State.SendBufferInfoBase != 0)
- {
- int i = 0;
- uint writeOffset = 0;
- for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
- {
- uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
- uint updateCount;
- if (channelIndex != 1)
- {
- updateCount = 0;
- }
- else
- {
- updateCount = newUpdateCount;
- }
- _commandBuffer.GenerateCaptureEffect(
- bufferOffset,
- effect.Parameter.Input[i],
- effect.State.SendBufferInfo,
- effect.IsEnabled,
- effect.Parameter.BufferStorageSize,
- effect.State.SendBufferInfoBase,
- updateCount,
- writeOffset,
- nodeId);
- writeOffset = newUpdateCount;
- i++;
- }
- }
- }
- private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId, int effectId)
- {
- Debug.Assert(effect.Type == EffectType.Compressor);
- Memory<EffectResultState> dspResultState;
- if (effect.Parameter.StatisticsEnabled)
- {
- dspResultState = _effectContext.GetDspStateMemory(effectId);
- }
- else
- {
- dspResultState = Memory<EffectResultState>.Empty;
- }
- _commandBuffer.GenerateCompressorEffect(
- bufferOffset,
- effect.Parameter,
- effect.State,
- dspResultState,
- effect.IsEnabled,
- nodeId);
- }
- private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
- {
- int nodeId = mix.NodeId;
- bool isFinalMix = mix.MixId == Constants.FinalMixId;
- PerformanceEntryAddresses performanceEntry = new();
- bool performanceInitialized = false;
- if (_performanceManager != null && _performanceManager.GetNextEntry(
- out performanceEntry,
- effect.GetPerformanceDetailType(),
- isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix,
- nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- switch (effect.Type)
- {
- case EffectType.BufferMix:
- GenerateBufferMixerEffect((int)mix.BufferOffset, (BufferMixEffect)effect, nodeId);
- break;
- case EffectType.AuxiliaryBuffer:
- GenerateAuxEffect(mix.BufferOffset, (AuxiliaryBufferEffect)effect, nodeId);
- break;
- case EffectType.Delay:
- GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported());
- break;
- case EffectType.Reverb:
- GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported());
- break;
- case EffectType.Reverb3d:
- GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported());
- break;
- case EffectType.BiquadFilter:
- GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId);
- break;
- case EffectType.Limiter:
- GenerateLimiterEffect(mix.BufferOffset, (LimiterEffect)effect, nodeId, effectId);
- break;
- case EffectType.CaptureBuffer:
- GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
- break;
- case EffectType.Compressor:
- GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId, effectId);
- break;
- default:
- throw new NotImplementedException($"Unsupported effect type {effect.Type}");
- }
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- effect.UpdateForCommandGeneration();
- }
- private void GenerateEffects(ref MixState mix)
- {
- ReadOnlySpan<int> effectProcessingOrderArray = mix.EffectProcessingOrderArray;
- Debug.Assert(_effectContext.GetCount() == 0 || !effectProcessingOrderArray.IsEmpty);
- for (int i = 0; i < _effectContext.GetCount(); i++)
- {
- int effectOrder = effectProcessingOrderArray[i];
- if (effectOrder == Constants.InvalidProcessingOrder)
- {
- break;
- }
- // BaseEffect is a class, we don't need to pass it by ref
- BaseEffect effect = _effectContext.GetEffect(effectOrder);
- Debug.Assert(effect.Type != EffectType.Invalid);
- Debug.Assert(effect.MixId == mix.MixId);
- if (!effect.ShouldSkip())
- {
- GenerateEffect(ref mix, effectOrder, effect);
- }
- }
- }
- private void GenerateMixWithSplitter(
- uint inputBufferIndex,
- uint outputBufferIndex,
- float volume,
- SplitterDestination destination,
- ref bool isFirstMixBuffer,
- int nodeId)
- {
- ref BiquadFilterParameter bqf0 = ref destination.GetBiquadFilterParameter(0);
- ref BiquadFilterParameter bqf1 = ref destination.GetBiquadFilterParameter(1);
- Memory<BiquadFilterState> bqfState = _splitterContext.GetBiquadFilterState(destination);
- if (bqf0.Enable && bqf1.Enable)
- {
- _commandBuffer.GenerateMultiTapBiquadFilterAndMix(
- 0f,
- volume,
- inputBufferIndex,
- outputBufferIndex,
- 0,
- Memory<VoiceUpdateState>.Empty,
- ref bqf0,
- ref bqf1,
- bqfState[..1],
- bqfState.Slice(1, 1),
- bqfState.Slice(2, 1),
- bqfState.Slice(3, 1),
- !destination.IsBiquadFilterEnabledPrev(),
- !destination.IsBiquadFilterEnabledPrev(),
- false,
- isFirstMixBuffer,
- nodeId);
- destination.UpdateBiquadFilterEnabledPrev(0);
- destination.UpdateBiquadFilterEnabledPrev(1);
- }
- else if (bqf0.Enable)
- {
- _commandBuffer.GenerateBiquadFilterAndMix(
- 0f,
- volume,
- inputBufferIndex,
- outputBufferIndex,
- 0,
- Memory<VoiceUpdateState>.Empty,
- ref bqf0,
- bqfState[..1],
- bqfState.Slice(1, 1),
- !destination.IsBiquadFilterEnabledPrev(),
- false,
- isFirstMixBuffer,
- nodeId);
- destination.UpdateBiquadFilterEnabledPrev(0);
- }
- else if (bqf1.Enable)
- {
- _commandBuffer.GenerateBiquadFilterAndMix(
- 0f,
- volume,
- inputBufferIndex,
- outputBufferIndex,
- 0,
- Memory<VoiceUpdateState>.Empty,
- ref bqf1,
- bqfState[..1],
- bqfState.Slice(1, 1),
- !destination.IsBiquadFilterEnabledPrev(),
- false,
- isFirstMixBuffer,
- nodeId);
- destination.UpdateBiquadFilterEnabledPrev(1);
- }
- isFirstMixBuffer = false;
- }
- private void GenerateMix(ref MixState mix)
- {
- if (mix.HasAnyDestination())
- {
- Debug.Assert(mix.DestinationMixId != Constants.UnusedMixId || mix.DestinationSplitterId != Constants.UnusedSplitterId);
- if (mix.DestinationMixId == Constants.UnusedMixId)
- {
- if (mix.DestinationSplitterId != Constants.UnusedSplitterId)
- {
- int destinationId = 0;
- while (true)
- {
- int destinationIndex = destinationId++;
- SplitterDestination destination = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex);
- if (destination.IsNull)
- {
- break;
- }
- if (destination.IsConfigured())
- {
- int mixId = destination.DestinationId;
- if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
- {
- ref MixState destinationMix = ref _mixContext.GetState(mixId);
- uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount);
- bool isFirstMixBuffer = true;
- for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
- {
- float volume = mix.Volume * destination.GetMixVolume((int)bufferDestinationIndex);
- if (volume != 0.0f)
- {
- if (destination.IsBiquadFilterEnabled())
- {
- GenerateMixWithSplitter(
- inputBufferIndex,
- destinationMix.BufferOffset + bufferDestinationIndex,
- volume,
- destination,
- ref isFirstMixBuffer,
- mix.NodeId);
- }
- else
- {
- _commandBuffer.GenerateMix(
- inputBufferIndex,
- destinationMix.BufferOffset + bufferDestinationIndex,
- mix.NodeId,
- volume);
- }
- }
- }
- }
- }
- }
- }
- }
- else
- {
- ref MixState destinationMix = ref _mixContext.GetState(mix.DestinationMixId);
- for (uint bufferIndex = 0; bufferIndex < mix.BufferCount; bufferIndex++)
- {
- for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
- {
- float volume = mix.Volume * mix.GetMixBufferVolume((int)bufferIndex, (int)bufferDestinationIndex);
- if (volume != 0.0f)
- {
- _commandBuffer.GenerateMix(
- mix.BufferOffset + bufferIndex,
- destinationMix.BufferOffset + bufferDestinationIndex,
- mix.NodeId,
- volume);
- }
- }
- }
- }
- }
- }
- private void GenerateSubMix(ref MixState subMix)
- {
- _commandBuffer.GenerateDepopForMixBuffers(
- _rendererContext.DepopBuffer,
- subMix.BufferOffset,
- subMix.BufferCount,
- subMix.NodeId,
- subMix.SampleRate);
- GenerateEffects(ref subMix);
- PerformanceEntryAddresses performanceEntry = new();
- int nodeId = subMix.NodeId;
- bool performanceInitialized = false;
- if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.SubMix, nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- GenerateMix(ref subMix);
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- }
- public void GenerateSubMixes()
- {
- for (int id = 0; id < _mixContext.GetCount(); id++)
- {
- ref MixState sortedState = ref _mixContext.GetSortedState(id);
- if (sortedState.IsUsed && sortedState.MixId != Constants.FinalMixId)
- {
- int nodeId = sortedState.NodeId;
- PerformanceEntryAddresses performanceEntry = new();
- bool performanceInitialized = false;
- if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.SubMix, nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- GenerateSubMix(ref sortedState);
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- }
- }
- }
- private void GenerateFinalMix()
- {
- ref MixState finalMix = ref _mixContext.GetFinalState();
- _commandBuffer.GenerateDepopForMixBuffers(
- _rendererContext.DepopBuffer,
- finalMix.BufferOffset,
- finalMix.BufferCount,
- finalMix.NodeId,
- finalMix.SampleRate);
- GenerateEffects(ref finalMix);
- PerformanceEntryAddresses performanceEntry = new();
- int nodeId = finalMix.NodeId;
- bool performanceInitialized = false;
- if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.FinalMix, nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- // Only generate volume command if the volume isn't 100%.
- if (finalMix.Volume != 1.0f)
- {
- for (uint bufferIndex = 0; bufferIndex < finalMix.BufferCount; bufferIndex++)
- {
- bool performanceSubInitialized = false;
- if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.VolumeRamp, PerformanceEntryType.FinalMix, nodeId))
- {
- performanceSubInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- _commandBuffer.GenerateVolume(
- finalMix.Volume,
- finalMix.BufferOffset + bufferIndex,
- nodeId);
- if (performanceSubInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- }
- }
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- }
- public void GenerateFinalMixes()
- {
- int nodeId = _mixContext.GetFinalState().NodeId;
- PerformanceEntryAddresses performanceEntry = new();
- bool performanceInitialized = false;
- if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.FinalMix, nodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
- }
- GenerateFinalMix();
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
- }
- }
- private void GenerateCircularBuffer(CircularBufferSink sink, ref MixState finalMix)
- {
- _commandBuffer.GenerateCircularBuffer(finalMix.BufferOffset, sink, Constants.InvalidNodeId);
- }
- private void GenerateDevice(DeviceSink sink, ref MixState finalMix)
- {
- if (_commandBuffer.CommandList.SampleRate != 48000 && sink.UpsamplerState == null)
- {
- sink.UpsamplerState = _rendererContext.UpsamplerManager.Allocate();
- }
- bool useCustomDownMixingCommand = _rendererContext.ChannelCount == 2 && sink.Parameter.DownMixParameterEnabled;
- if (useCustomDownMixingCommand)
- {
- _commandBuffer.GenerateDownMixSurroundToStereo(
- finalMix.BufferOffset,
- sink.Parameter.Input.AsSpan(),
- sink.Parameter.Input.AsSpan(),
- sink.DownMixCoefficients,
- Constants.InvalidNodeId);
- }
- // NOTE: We do the downmixing at the DSP level as it's easier that way.
- else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6)
- {
- _commandBuffer.GenerateDownMixSurroundToStereo(
- finalMix.BufferOffset,
- sink.Parameter.Input.AsSpan(),
- sink.Parameter.Input.AsSpan(),
- Constants.DefaultSurroundToStereoCoefficients,
- Constants.InvalidNodeId);
- }
- CommandList commandList = _commandBuffer.CommandList;
- if (sink.UpsamplerState != null)
- {
- _commandBuffer.GenerateUpsample(
- finalMix.BufferOffset,
- sink.UpsamplerState,
- sink.Parameter.InputCount,
- sink.Parameter.Input.AsSpan(),
- commandList.BufferCount,
- commandList.SampleCount,
- commandList.SampleRate,
- Constants.InvalidNodeId);
- }
- _commandBuffer.GenerateDeviceSink(
- finalMix.BufferOffset,
- sink,
- _rendererContext.SessionId,
- commandList.Buffers,
- Constants.InvalidNodeId);
- }
- private void GenerateSink(BaseSink sink, ref MixState finalMix)
- {
- bool performanceInitialized = false;
- PerformanceEntryAddresses performanceEntry = new();
- if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.Sink, sink.NodeId))
- {
- performanceInitialized = true;
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, sink.NodeId);
- }
- if (!sink.ShouldSkip)
- {
- switch (sink.Type)
- {
- case SinkType.CircularBuffer:
- GenerateCircularBuffer((CircularBufferSink)sink, ref finalMix);
- break;
- case SinkType.Device:
- GenerateDevice((DeviceSink)sink, ref finalMix);
- break;
- default:
- throw new NotImplementedException($"Unsupported sink type {sink.Type}");
- }
- sink.UpdateForCommandGeneration();
- }
- if (performanceInitialized)
- {
- GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, sink.NodeId);
- }
- }
- public void GenerateSinks()
- {
- ref MixState finalMix = ref _mixContext.GetFinalState();
- for (int i = 0; i < _sinkContext.GetCount(); i++)
- {
- // BaseSink is a class, we don't need to pass it by ref
- BaseSink sink = _sinkContext.GetSink(i);
- if (sink.IsUsed)
- {
- GenerateSink(sink, ref finalMix);
- }
- }
- }
- }
- }
|