CommandGenerator.cs 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031
  1. //
  2. // Copyright (c) 2019-2021 Ryujinx
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. //
  17. using Ryujinx.Audio.Common;
  18. using Ryujinx.Audio.Renderer.Common;
  19. using Ryujinx.Audio.Renderer.Dsp.Command;
  20. using Ryujinx.Audio.Renderer.Dsp.State;
  21. using Ryujinx.Audio.Renderer.Parameter;
  22. using Ryujinx.Audio.Renderer.Server.Effect;
  23. using Ryujinx.Audio.Renderer.Server.Mix;
  24. using Ryujinx.Audio.Renderer.Server.Performance;
  25. using Ryujinx.Audio.Renderer.Server.Sink;
  26. using Ryujinx.Audio.Renderer.Server.Splitter;
  27. using Ryujinx.Audio.Renderer.Server.Voice;
  28. using Ryujinx.Audio.Renderer.Utils;
  29. using System;
  30. using System.Diagnostics;
  31. namespace Ryujinx.Audio.Renderer.Server
  32. {
  33. public class CommandGenerator
  34. {
  35. private CommandBuffer _commandBuffer;
  36. private RendererSystemContext _rendererContext;
  37. private VoiceContext _voiceContext;
  38. private MixContext _mixContext;
  39. private EffectContext _effectContext;
  40. private SinkContext _sinkContext;
  41. private SplitterContext _splitterContext;
  42. private PerformanceManager _performanceManager;
  43. public CommandGenerator(CommandBuffer commandBuffer, RendererSystemContext rendererContext, VoiceContext voiceContext, MixContext mixContext, EffectContext effectContext, SinkContext sinkContext, SplitterContext splitterContext, PerformanceManager performanceManager)
  44. {
  45. _commandBuffer = commandBuffer;
  46. _rendererContext = rendererContext;
  47. _voiceContext = voiceContext;
  48. _mixContext = mixContext;
  49. _effectContext = effectContext;
  50. _sinkContext = sinkContext;
  51. _splitterContext = splitterContext;
  52. _performanceManager = performanceManager;
  53. _commandBuffer.GenerateClearMixBuffer(Constants.InvalidNodeId);
  54. }
  55. private void GenerateDataSource(ref VoiceState voiceState, Memory<VoiceUpdateState> dspState, int channelIndex)
  56. {
  57. if (voiceState.MixId != Constants.UnusedMixId)
  58. {
  59. ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
  60. _commandBuffer.GenerateDepopPrepare(dspState,
  61. _rendererContext.DepopBuffer,
  62. mix.BufferCount,
  63. mix.BufferOffset,
  64. voiceState.NodeId,
  65. voiceState.WasPlaying);
  66. }
  67. else if (voiceState.SplitterId != Constants.UnusedSplitterId)
  68. {
  69. int destinationId = 0;
  70. while (true)
  71. {
  72. Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId++);
  73. if (destinationSpan.IsEmpty)
  74. {
  75. break;
  76. }
  77. ref SplitterDestination destination = ref destinationSpan[0];
  78. if (destination.IsConfigured())
  79. {
  80. int mixId = destination.DestinationId;
  81. if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
  82. {
  83. ref MixState mix = ref _mixContext.GetState(mixId);
  84. _commandBuffer.GenerateDepopPrepare(dspState,
  85. _rendererContext.DepopBuffer,
  86. mix.BufferCount,
  87. mix.BufferOffset,
  88. voiceState.NodeId,
  89. voiceState.WasPlaying);
  90. destination.MarkAsNeedToUpdateInternalState();
  91. }
  92. }
  93. }
  94. }
  95. if (!voiceState.WasPlaying)
  96. {
  97. Debug.Assert(voiceState.SampleFormat != SampleFormat.Adpcm || channelIndex == 0);
  98. if (_rendererContext.BehaviourContext.IsWaveBufferVersion2Supported())
  99. {
  100. _commandBuffer.GenerateDataSourceVersion2(ref voiceState,
  101. dspState,
  102. (ushort)_rendererContext.MixBufferCount,
  103. (ushort)channelIndex,
  104. voiceState.NodeId);
  105. }
  106. else
  107. {
  108. switch (voiceState.SampleFormat)
  109. {
  110. case SampleFormat.PcmInt16:
  111. _commandBuffer.GeneratePcmInt16DataSourceVersion1(ref voiceState,
  112. dspState,
  113. (ushort)_rendererContext.MixBufferCount,
  114. (ushort)channelIndex,
  115. voiceState.NodeId);
  116. break;
  117. case SampleFormat.PcmFloat:
  118. _commandBuffer.GeneratePcmFloatDataSourceVersion1(ref voiceState,
  119. dspState,
  120. (ushort)_rendererContext.MixBufferCount,
  121. (ushort)channelIndex,
  122. voiceState.NodeId);
  123. break;
  124. case SampleFormat.Adpcm:
  125. _commandBuffer.GenerateAdpcmDataSourceVersion1(ref voiceState,
  126. dspState,
  127. (ushort)_rendererContext.MixBufferCount,
  128. voiceState.NodeId);
  129. break;
  130. default:
  131. throw new NotImplementedException($"Unsupported data source {voiceState.SampleFormat}");
  132. }
  133. }
  134. }
  135. }
  136. private void GenerateBiquadFilterForVoice(ref VoiceState voiceState, Memory<VoiceUpdateState> state, int baseIndex, int bufferOffset, int nodeId)
  137. {
  138. bool supportsOptimizedPath = _rendererContext.BehaviourContext.IsBiquadFilterGroupedOptimizationSupported();
  139. if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable)
  140. {
  141. Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount);
  142. Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
  143. _commandBuffer.GenerateGroupedBiquadFilter(baseIndex, voiceState.BiquadFilters.ToSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
  144. }
  145. else
  146. {
  147. for (int i = 0; i < voiceState.BiquadFilters.Length; i++)
  148. {
  149. ref BiquadFilterParameter filter = ref voiceState.BiquadFilters[i];
  150. if (filter.Enable)
  151. {
  152. Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount);
  153. Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
  154. _commandBuffer.GenerateBiquadFilter(baseIndex,
  155. ref filter,
  156. stateMemory.Slice(i, 1),
  157. bufferOffset,
  158. bufferOffset,
  159. !voiceState.BiquadFilterNeedInitialization[i],
  160. nodeId);
  161. }
  162. }
  163. }
  164. }
  165. private void GenerateVoiceMix(Span<float> mixVolumes, Span<float> previousMixVolumes, Memory<VoiceUpdateState> state, uint bufferOffset, uint bufferCount, uint bufferIndex, int nodeId)
  166. {
  167. if (bufferCount > Constants.VoiceChannelCountMax)
  168. {
  169. _commandBuffer.GenerateMixRampGrouped(bufferCount,
  170. bufferIndex,
  171. bufferOffset,
  172. previousMixVolumes,
  173. mixVolumes,
  174. state,
  175. nodeId);
  176. }
  177. else
  178. {
  179. for (int i = 0; i < bufferCount; i++)
  180. {
  181. float previousMixVolume = previousMixVolumes[i];
  182. float mixVolume = mixVolumes[i];
  183. if (mixVolume != 0.0f || previousMixVolume != 0.0f)
  184. {
  185. _commandBuffer.GenerateMixRamp(previousMixVolume,
  186. mixVolume,
  187. bufferIndex,
  188. bufferOffset + (uint)i,
  189. i,
  190. state,
  191. nodeId);
  192. }
  193. }
  194. }
  195. }
  196. private void GenerateVoice(ref VoiceState voiceState)
  197. {
  198. int nodeId = voiceState.NodeId;
  199. uint channelsCount = voiceState.ChannelsCount;
  200. for (int channelIndex = 0; channelIndex < channelsCount; channelIndex++)
  201. {
  202. Memory<VoiceUpdateState> dspStateMemory = _voiceContext.GetUpdateStateForDsp(voiceState.ChannelResourceIds[channelIndex]);
  203. ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(voiceState.ChannelResourceIds[channelIndex]);
  204. PerformanceDetailType dataSourceDetailType = PerformanceDetailType.Adpcm;
  205. if (voiceState.SampleFormat == SampleFormat.PcmInt16)
  206. {
  207. dataSourceDetailType = PerformanceDetailType.PcmInt16;
  208. }
  209. else if (voiceState.SampleFormat == SampleFormat.PcmFloat)
  210. {
  211. dataSourceDetailType = PerformanceDetailType.PcmFloat;
  212. }
  213. bool performanceInitialized = false;
  214. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  215. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, dataSourceDetailType, PerformanceEntryType.Voice, nodeId))
  216. {
  217. performanceInitialized = true;
  218. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  219. }
  220. GenerateDataSource(ref voiceState, dspStateMemory, channelIndex);
  221. if (performanceInitialized)
  222. {
  223. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  224. }
  225. if (voiceState.WasPlaying)
  226. {
  227. voiceState.PreviousVolume = 0.0f;
  228. }
  229. else if (voiceState.HasAnyDestination())
  230. {
  231. performanceInitialized = false;
  232. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.BiquadFilter, PerformanceEntryType.Voice, nodeId))
  233. {
  234. performanceInitialized = true;
  235. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  236. }
  237. GenerateBiquadFilterForVoice(ref voiceState, dspStateMemory, (int)_rendererContext.MixBufferCount, channelIndex, nodeId);
  238. if (performanceInitialized)
  239. {
  240. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  241. }
  242. performanceInitialized = false;
  243. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.VolumeRamp, PerformanceEntryType.Voice, nodeId))
  244. {
  245. performanceInitialized = true;
  246. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  247. }
  248. _commandBuffer.GenerateVolumeRamp(voiceState.PreviousVolume,
  249. voiceState.Volume,
  250. _rendererContext.MixBufferCount + (uint)channelIndex,
  251. nodeId);
  252. if (performanceInitialized)
  253. {
  254. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  255. }
  256. voiceState.PreviousVolume = voiceState.Volume;
  257. if (voiceState.MixId == Constants.UnusedMixId)
  258. {
  259. if (voiceState.SplitterId != Constants.UnusedSplitterId)
  260. {
  261. int destinationId = channelIndex;
  262. while (true)
  263. {
  264. Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId);
  265. if (destinationSpan.IsEmpty)
  266. {
  267. break;
  268. }
  269. ref SplitterDestination destination = ref destinationSpan[0];
  270. destinationId += (int)channelsCount;
  271. if (destination.IsConfigured())
  272. {
  273. int mixId = destination.DestinationId;
  274. if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
  275. {
  276. ref MixState mix = ref _mixContext.GetState(mixId);
  277. GenerateVoiceMix(destination.MixBufferVolume,
  278. destination.PreviousMixBufferVolume,
  279. dspStateMemory,
  280. mix.BufferOffset,
  281. mix.BufferCount,
  282. _rendererContext.MixBufferCount + (uint)channelIndex,
  283. nodeId);
  284. destination.MarkAsNeedToUpdateInternalState();
  285. }
  286. }
  287. }
  288. }
  289. }
  290. else
  291. {
  292. ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
  293. performanceInitialized = false;
  294. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.Voice, nodeId))
  295. {
  296. performanceInitialized = true;
  297. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  298. }
  299. GenerateVoiceMix(channelResource.Mix.ToSpan(),
  300. channelResource.PreviousMix.ToSpan(),
  301. dspStateMemory,
  302. mix.BufferOffset,
  303. mix.BufferCount,
  304. _rendererContext.MixBufferCount + (uint)channelIndex,
  305. nodeId);
  306. if (performanceInitialized)
  307. {
  308. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  309. }
  310. channelResource.UpdateState();
  311. }
  312. for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++)
  313. {
  314. voiceState.BiquadFilterNeedInitialization[i] = voiceState.BiquadFilters[i].Enable;
  315. }
  316. }
  317. }
  318. }
  319. public void GenerateVoices()
  320. {
  321. for (int i = 0; i < _voiceContext.GetCount(); i++)
  322. {
  323. ref VoiceState sortedState = ref _voiceContext.GetSortedState(i);
  324. if (!sortedState.ShouldSkip() && sortedState.UpdateForCommandGeneration(_voiceContext))
  325. {
  326. int nodeId = sortedState.NodeId;
  327. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  328. bool performanceInitialized = false;
  329. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.Voice, nodeId))
  330. {
  331. performanceInitialized = true;
  332. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  333. }
  334. GenerateVoice(ref sortedState);
  335. if (performanceInitialized)
  336. {
  337. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  338. }
  339. }
  340. }
  341. _splitterContext.UpdateInternalState();
  342. }
  343. public void GeneratePerformance(ref PerformanceEntryAddresses performanceEntryAddresses, PerformanceCommand.Type type, int nodeId)
  344. {
  345. _commandBuffer.GeneratePerformance(ref performanceEntryAddresses, type, nodeId);
  346. }
  347. private void GenerateBufferMixerEffect(int bufferOffset, BufferMixEffect effect, int nodeId)
  348. {
  349. Debug.Assert(effect.Type == EffectType.BufferMix);
  350. if (effect.IsEnabled)
  351. {
  352. for (int i = 0; i < effect.Parameter.MixesCount; i++)
  353. {
  354. if (effect.Parameter.Volumes[i] != 0.0f)
  355. {
  356. _commandBuffer.GenerateMix((uint)bufferOffset + effect.Parameter.Input[i],
  357. (uint)bufferOffset + effect.Parameter.Output[i],
  358. nodeId,
  359. effect.Parameter.Volumes[i]);
  360. }
  361. }
  362. }
  363. }
  364. private void GenerateAuxEffect(uint bufferOffset, AuxiliaryBufferEffect effect, int nodeId)
  365. {
  366. Debug.Assert(effect.Type == EffectType.AuxiliaryBuffer);
  367. if (effect.IsEnabled)
  368. {
  369. effect.GetWorkBuffer(0);
  370. effect.GetWorkBuffer(1);
  371. }
  372. if (effect.State.SendBufferInfoBase != 0 && effect.State.ReturnBufferInfoBase != 0)
  373. {
  374. int i = 0;
  375. uint writeOffset = 0;
  376. for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
  377. {
  378. uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
  379. uint updateCount;
  380. if (channelIndex != 1)
  381. {
  382. updateCount = 0;
  383. }
  384. else
  385. {
  386. updateCount = newUpdateCount;
  387. }
  388. _commandBuffer.GenerateAuxEffect(bufferOffset,
  389. effect.Parameter.Input[i],
  390. effect.Parameter.Output[i],
  391. ref effect.State,
  392. effect.IsEnabled,
  393. effect.Parameter.BufferStorageSize,
  394. effect.State.SendBufferInfoBase,
  395. effect.State.ReturnBufferInfoBase,
  396. updateCount,
  397. writeOffset,
  398. nodeId);
  399. writeOffset = newUpdateCount;
  400. i++;
  401. }
  402. }
  403. }
  404. private void GenerateDelayEffect(uint bufferOffset, DelayEffect effect, int nodeId, bool newEffectChannelMappingSupported)
  405. {
  406. Debug.Assert(effect.Type == EffectType.Delay);
  407. ulong workBuffer = effect.GetWorkBuffer(-1);
  408. _commandBuffer.GenerateDelayEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, newEffectChannelMappingSupported);
  409. }
  410. private void GenerateReverbEffect(uint bufferOffset, ReverbEffect effect, int nodeId, bool isLongSizePreDelaySupported, bool newEffectChannelMappingSupported)
  411. {
  412. Debug.Assert(effect.Type == EffectType.Reverb);
  413. ulong workBuffer = effect.GetWorkBuffer(-1);
  414. _commandBuffer.GenerateReverbEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, isLongSizePreDelaySupported, newEffectChannelMappingSupported);
  415. }
  416. private void GenerateReverb3dEffect(uint bufferOffset, Reverb3dEffect effect, int nodeId, bool newEffectChannelMappingSupported)
  417. {
  418. Debug.Assert(effect.Type == EffectType.Reverb3d);
  419. ulong workBuffer = effect.GetWorkBuffer(-1);
  420. _commandBuffer.GenerateReverb3dEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, newEffectChannelMappingSupported);
  421. }
  422. private void GenerateBiquadFilterEffect(uint bufferOffset, BiquadFilterEffect effect, int nodeId)
  423. {
  424. Debug.Assert(effect.Type == EffectType.BiquadFilter);
  425. if (effect.IsEnabled)
  426. {
  427. bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
  428. (effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed());
  429. BiquadFilterParameter parameter = new BiquadFilterParameter();
  430. parameter.Enable = true;
  431. effect.Parameter.Denominator.ToSpan().CopyTo(parameter.Denominator.ToSpan());
  432. effect.Parameter.Numerator.ToSpan().CopyTo(parameter.Numerator.ToSpan());
  433. for (int i = 0; i < effect.Parameter.ChannelCount; i++)
  434. {
  435. _commandBuffer.GenerateBiquadFilter((int)bufferOffset, ref parameter, effect.State.Slice(i, 1),
  436. effect.Parameter.Input[i],
  437. effect.Parameter.Output[i],
  438. needInitialization,
  439. nodeId);
  440. }
  441. }
  442. else
  443. {
  444. for (int i = 0; i < effect.Parameter.ChannelCount; i++)
  445. {
  446. uint inputBufferIndex = bufferOffset + effect.Parameter.Input[i];
  447. uint outputBufferIndex = bufferOffset + effect.Parameter.Output[i];
  448. // If the input and output isn't the same, generate a command.
  449. if (inputBufferIndex != outputBufferIndex)
  450. {
  451. _commandBuffer.GenerateCopyMixBuffer(inputBufferIndex, outputBufferIndex, nodeId);
  452. }
  453. }
  454. }
  455. }
  456. private void GenerateLimiterEffect(uint bufferOffset, LimiterEffect effect, int nodeId, int effectId)
  457. {
  458. Debug.Assert(effect.Type == EffectType.Limiter);
  459. ulong workBuffer = effect.GetWorkBuffer(-1);
  460. if (_rendererContext.BehaviourContext.IsEffectInfoVersion2Supported())
  461. {
  462. Memory<EffectResultState> dspResultState;
  463. if (effect.Parameter.StatisticsEnabled)
  464. {
  465. dspResultState = _effectContext.GetDspStateMemory(effectId);
  466. }
  467. else
  468. {
  469. dspResultState = Memory<EffectResultState>.Empty;
  470. }
  471. _commandBuffer.GenerateLimiterEffectVersion2(bufferOffset, effect.Parameter, effect.State, dspResultState, effect.IsEnabled, workBuffer, nodeId);
  472. }
  473. else
  474. {
  475. _commandBuffer.GenerateLimiterEffectVersion1(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId);
  476. }
  477. }
  478. private void GenerateCaptureEffect(uint bufferOffset, CaptureBufferEffect effect, int nodeId)
  479. {
  480. Debug.Assert(effect.Type == EffectType.CaptureBuffer);
  481. if (effect.IsEnabled)
  482. {
  483. effect.GetWorkBuffer(0);
  484. }
  485. if (effect.State.SendBufferInfoBase != 0)
  486. {
  487. int i = 0;
  488. uint writeOffset = 0;
  489. for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
  490. {
  491. uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
  492. uint updateCount;
  493. if (channelIndex != 1)
  494. {
  495. updateCount = 0;
  496. }
  497. else
  498. {
  499. updateCount = newUpdateCount;
  500. }
  501. _commandBuffer.GenerateCaptureEffect(bufferOffset,
  502. effect.Parameter.Input[i],
  503. effect.State.SendBufferInfo,
  504. effect.IsEnabled,
  505. effect.Parameter.BufferStorageSize,
  506. effect.State.SendBufferInfoBase,
  507. updateCount,
  508. writeOffset,
  509. nodeId);
  510. writeOffset = newUpdateCount;
  511. i++;
  512. }
  513. }
  514. }
  515. private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
  516. {
  517. int nodeId = mix.NodeId;
  518. bool isFinalMix = mix.MixId == Constants.FinalMixId;
  519. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  520. bool performanceInitialized = false;
  521. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, effect.GetPerformanceDetailType(),
  522. isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix, nodeId))
  523. {
  524. performanceInitialized = true;
  525. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  526. }
  527. switch (effect.Type)
  528. {
  529. case EffectType.BufferMix:
  530. GenerateBufferMixerEffect((int)mix.BufferOffset, (BufferMixEffect)effect, nodeId);
  531. break;
  532. case EffectType.AuxiliaryBuffer:
  533. GenerateAuxEffect(mix.BufferOffset, (AuxiliaryBufferEffect)effect, nodeId);
  534. break;
  535. case EffectType.Delay:
  536. GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported());
  537. break;
  538. case EffectType.Reverb:
  539. GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported());
  540. break;
  541. case EffectType.Reverb3d:
  542. GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported());
  543. break;
  544. case EffectType.BiquadFilter:
  545. GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId);
  546. break;
  547. case EffectType.Limiter:
  548. GenerateLimiterEffect(mix.BufferOffset, (LimiterEffect)effect, nodeId, effectId);
  549. break;
  550. case EffectType.CaptureBuffer:
  551. GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
  552. break;
  553. default:
  554. throw new NotImplementedException($"Unsupported effect type {effect.Type}");
  555. }
  556. if (performanceInitialized)
  557. {
  558. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  559. }
  560. effect.UpdateForCommandGeneration();
  561. }
  562. private void GenerateEffects(ref MixState mix)
  563. {
  564. ReadOnlySpan<int> effectProcessingOrderArray = mix.EffectProcessingOrderArray;
  565. Debug.Assert(_effectContext.GetCount() == 0 || !effectProcessingOrderArray.IsEmpty);
  566. for (int i = 0; i < _effectContext.GetCount(); i++)
  567. {
  568. int effectOrder = effectProcessingOrderArray[i];
  569. if (effectOrder == Constants.InvalidProcessingOrder)
  570. {
  571. break;
  572. }
  573. // BaseEffect is a class, we don't need to pass it by ref
  574. BaseEffect effect = _effectContext.GetEffect(effectOrder);
  575. Debug.Assert(effect.Type != EffectType.Invalid);
  576. Debug.Assert(effect.MixId == mix.MixId);
  577. if (!effect.ShouldSkip())
  578. {
  579. GenerateEffect(ref mix, effectOrder, effect);
  580. }
  581. }
  582. }
  583. private void GenerateMix(ref MixState mix)
  584. {
  585. if (mix.HasAnyDestination())
  586. {
  587. Debug.Assert(mix.DestinationMixId != Constants.UnusedMixId || mix.DestinationSplitterId != Constants.UnusedSplitterId);
  588. if (mix.DestinationMixId == Constants.UnusedMixId)
  589. {
  590. if (mix.DestinationSplitterId != Constants.UnusedSplitterId)
  591. {
  592. int destinationId = 0;
  593. while (true)
  594. {
  595. int destinationIndex = destinationId++;
  596. Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex);
  597. if (destinationSpan.IsEmpty)
  598. {
  599. break;
  600. }
  601. ref SplitterDestination destination = ref destinationSpan[0];
  602. if (destination.IsConfigured())
  603. {
  604. int mixId = destination.DestinationId;
  605. if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
  606. {
  607. ref MixState destinationMix = ref _mixContext.GetState(mixId);
  608. uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount);
  609. for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
  610. {
  611. float volume = mix.Volume * destination.GetMixVolume((int)bufferDestinationIndex);
  612. if (volume != 0.0f)
  613. {
  614. _commandBuffer.GenerateMix(inputBufferIndex,
  615. destinationMix.BufferOffset + bufferDestinationIndex,
  616. mix.NodeId,
  617. volume);
  618. }
  619. }
  620. }
  621. }
  622. }
  623. }
  624. }
  625. else
  626. {
  627. ref MixState destinationMix = ref _mixContext.GetState(mix.DestinationMixId);
  628. for (uint bufferIndex = 0; bufferIndex < mix.BufferCount; bufferIndex++)
  629. {
  630. for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
  631. {
  632. float volume = mix.Volume * mix.GetMixBufferVolume((int)bufferIndex, (int)bufferDestinationIndex);
  633. if (volume != 0.0f)
  634. {
  635. _commandBuffer.GenerateMix(mix.BufferOffset + bufferIndex,
  636. destinationMix.BufferOffset + bufferDestinationIndex,
  637. mix.NodeId,
  638. volume);
  639. }
  640. }
  641. }
  642. }
  643. }
  644. }
  645. private void GenerateSubMix(ref MixState subMix)
  646. {
  647. _commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
  648. subMix.BufferOffset,
  649. subMix.BufferCount,
  650. subMix.NodeId,
  651. subMix.SampleRate);
  652. GenerateEffects(ref subMix);
  653. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  654. int nodeId = subMix.NodeId;
  655. bool performanceInitialized = false;
  656. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.SubMix, nodeId))
  657. {
  658. performanceInitialized = true;
  659. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  660. }
  661. GenerateMix(ref subMix);
  662. if (performanceInitialized)
  663. {
  664. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  665. }
  666. }
  667. public void GenerateSubMixes()
  668. {
  669. for (int id = 0; id < _mixContext.GetCount(); id++)
  670. {
  671. ref MixState sortedState = ref _mixContext.GetSortedState(id);
  672. if (sortedState.IsUsed && sortedState.MixId != Constants.FinalMixId)
  673. {
  674. int nodeId = sortedState.NodeId;
  675. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  676. bool performanceInitialized = false;
  677. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.SubMix, nodeId))
  678. {
  679. performanceInitialized = true;
  680. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  681. }
  682. GenerateSubMix(ref sortedState);
  683. if (performanceInitialized)
  684. {
  685. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  686. }
  687. }
  688. }
  689. }
  690. private void GenerateFinalMix()
  691. {
  692. ref MixState finalMix = ref _mixContext.GetFinalState();
  693. _commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
  694. finalMix.BufferOffset,
  695. finalMix.BufferCount,
  696. finalMix.NodeId,
  697. finalMix.SampleRate);
  698. GenerateEffects(ref finalMix);
  699. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  700. int nodeId = finalMix.NodeId;
  701. bool performanceInitialized = false;
  702. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.FinalMix, nodeId))
  703. {
  704. performanceInitialized = true;
  705. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  706. }
  707. // Only generate volume command if the volume isn't 100%.
  708. if (finalMix.Volume != 1.0f)
  709. {
  710. for (uint bufferIndex = 0; bufferIndex < finalMix.BufferCount; bufferIndex++)
  711. {
  712. bool performanceSubInitialized = false;
  713. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.VolumeRamp, PerformanceEntryType.FinalMix, nodeId))
  714. {
  715. performanceSubInitialized = true;
  716. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  717. }
  718. _commandBuffer.GenerateVolume(finalMix.Volume,
  719. finalMix.BufferOffset + bufferIndex,
  720. nodeId);
  721. if (performanceSubInitialized)
  722. {
  723. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  724. }
  725. }
  726. }
  727. if (performanceInitialized)
  728. {
  729. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  730. }
  731. }
  732. public void GenerateFinalMixes()
  733. {
  734. int nodeId = _mixContext.GetFinalState().NodeId;
  735. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  736. bool performanceInitialized = false;
  737. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.FinalMix, nodeId))
  738. {
  739. performanceInitialized = true;
  740. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  741. }
  742. GenerateFinalMix();
  743. if (performanceInitialized)
  744. {
  745. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  746. }
  747. }
  748. private void GenerateCircularBuffer(CircularBufferSink sink, ref MixState finalMix)
  749. {
  750. _commandBuffer.GenerateCircularBuffer(finalMix.BufferOffset, sink, Constants.InvalidNodeId);
  751. }
  752. private void GenerateDevice(DeviceSink sink, ref MixState finalMix)
  753. {
  754. if (_commandBuffer.CommandList.SampleRate != 48000 && sink.UpsamplerState == null)
  755. {
  756. sink.UpsamplerState = _rendererContext.UpsamplerManager.Allocate();
  757. }
  758. bool useCustomDownMixingCommand = _rendererContext.ChannelCount == 2 && sink.Parameter.DownMixParameterEnabled;
  759. if (useCustomDownMixingCommand)
  760. {
  761. _commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
  762. sink.Parameter.Input.ToSpan(),
  763. sink.Parameter.Input.ToSpan(),
  764. sink.DownMixCoefficients,
  765. Constants.InvalidNodeId);
  766. }
  767. // NOTE: We do the downmixing at the DSP level as it's easier that way.
  768. else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6)
  769. {
  770. _commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
  771. sink.Parameter.Input.ToSpan(),
  772. sink.Parameter.Input.ToSpan(),
  773. Constants.DefaultSurroundToStereoCoefficients,
  774. Constants.InvalidNodeId);
  775. }
  776. CommandList commandList = _commandBuffer.CommandList;
  777. if (sink.UpsamplerState != null)
  778. {
  779. _commandBuffer.GenerateUpsample(finalMix.BufferOffset,
  780. sink.UpsamplerState,
  781. sink.Parameter.InputCount,
  782. sink.Parameter.Input.ToSpan(),
  783. commandList.BufferCount,
  784. commandList.SampleCount,
  785. commandList.SampleRate,
  786. Constants.InvalidNodeId);
  787. }
  788. _commandBuffer.GenerateDeviceSink(finalMix.BufferOffset,
  789. sink,
  790. _rendererContext.SessionId,
  791. commandList.Buffers,
  792. Constants.InvalidNodeId);
  793. }
  794. private void GenerateSink(BaseSink sink, ref MixState finalMix)
  795. {
  796. bool performanceInitialized = false;
  797. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  798. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.Sink, sink.NodeId))
  799. {
  800. performanceInitialized = true;
  801. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, sink.NodeId);
  802. }
  803. if (!sink.ShouldSkip)
  804. {
  805. switch (sink.Type)
  806. {
  807. case SinkType.CircularBuffer:
  808. GenerateCircularBuffer((CircularBufferSink)sink, ref finalMix);
  809. break;
  810. case SinkType.Device:
  811. GenerateDevice((DeviceSink)sink, ref finalMix);
  812. break;
  813. default:
  814. throw new NotImplementedException($"Unsupported sink type {sink.Type}");
  815. }
  816. sink.UpdateForCommandGeneration();
  817. }
  818. if (performanceInitialized)
  819. {
  820. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, sink.NodeId);
  821. }
  822. }
  823. public void GenerateSinks()
  824. {
  825. ref MixState finalMix = ref _mixContext.GetFinalState();
  826. for (int i = 0; i < _sinkContext.GetCount(); i++)
  827. {
  828. // BaseSink is a class, we don't need to pass it by ref
  829. BaseSink sink = _sinkContext.GetSink(i);
  830. if (sink.IsUsed)
  831. {
  832. GenerateSink(sink, ref finalMix);
  833. }
  834. }
  835. }
  836. }
  837. }