CommandGenerator.cs 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  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)
  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);
  409. }
  410. private void GenerateReverbEffect(uint bufferOffset, ReverbEffect effect, int nodeId, bool isLongSizePreDelaySupported)
  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);
  415. }
  416. private void GenerateReverb3dEffect(uint bufferOffset, Reverb3dEffect effect, int nodeId)
  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);
  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 = _effectContext.GetDspStateMemory(effectId);
  463. _commandBuffer.GenerateLimiterEffectVersion2(bufferOffset, effect.Parameter, effect.State, dspResultState, effect.IsEnabled, workBuffer, nodeId);
  464. }
  465. else
  466. {
  467. _commandBuffer.GenerateLimiterEffectVersion1(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId);
  468. }
  469. }
  470. private void GenerateCaptureEffect(uint bufferOffset, CaptureBufferEffect effect, int nodeId)
  471. {
  472. Debug.Assert(effect.Type == EffectType.CaptureBuffer);
  473. if (effect.IsEnabled)
  474. {
  475. effect.GetWorkBuffer(0);
  476. }
  477. if (effect.State.SendBufferInfoBase != 0)
  478. {
  479. int i = 0;
  480. uint writeOffset = 0;
  481. for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
  482. {
  483. uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
  484. uint updateCount;
  485. if (channelIndex != 1)
  486. {
  487. updateCount = 0;
  488. }
  489. else
  490. {
  491. updateCount = newUpdateCount;
  492. }
  493. _commandBuffer.GenerateCaptureEffect(bufferOffset,
  494. effect.Parameter.Input[i],
  495. effect.State.SendBufferInfo,
  496. effect.IsEnabled,
  497. effect.Parameter.BufferStorageSize,
  498. effect.State.SendBufferInfoBase,
  499. updateCount,
  500. writeOffset,
  501. nodeId);
  502. writeOffset = newUpdateCount;
  503. i++;
  504. }
  505. }
  506. }
  507. private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
  508. {
  509. int nodeId = mix.NodeId;
  510. bool isFinalMix = mix.MixId == Constants.FinalMixId;
  511. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  512. bool performanceInitialized = false;
  513. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, effect.GetPerformanceDetailType(),
  514. isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix, nodeId))
  515. {
  516. performanceInitialized = true;
  517. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  518. }
  519. switch (effect.Type)
  520. {
  521. case EffectType.BufferMix:
  522. GenerateBufferMixerEffect((int)mix.BufferOffset, (BufferMixEffect)effect, nodeId);
  523. break;
  524. case EffectType.AuxiliaryBuffer:
  525. GenerateAuxEffect(mix.BufferOffset, (AuxiliaryBufferEffect)effect, nodeId);
  526. break;
  527. case EffectType.Delay:
  528. GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId);
  529. break;
  530. case EffectType.Reverb:
  531. GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported);
  532. break;
  533. case EffectType.Reverb3d:
  534. GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId);
  535. break;
  536. case EffectType.BiquadFilter:
  537. GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId);
  538. break;
  539. case EffectType.Limiter:
  540. GenerateLimiterEffect(mix.BufferOffset, (LimiterEffect)effect, nodeId, effectId);
  541. break;
  542. case EffectType.CaptureBuffer:
  543. GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
  544. break;
  545. default:
  546. throw new NotImplementedException($"Unsupported effect type {effect.Type}");
  547. }
  548. if (performanceInitialized)
  549. {
  550. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  551. }
  552. effect.UpdateForCommandGeneration();
  553. }
  554. private void GenerateEffects(ref MixState mix)
  555. {
  556. ReadOnlySpan<int> effectProcessingOrderArray = mix.EffectProcessingOrderArray;
  557. Debug.Assert(_effectContext.GetCount() == 0 || !effectProcessingOrderArray.IsEmpty);
  558. for (int i = 0; i < _effectContext.GetCount(); i++)
  559. {
  560. int effectOrder = effectProcessingOrderArray[i];
  561. if (effectOrder == Constants.InvalidProcessingOrder)
  562. {
  563. break;
  564. }
  565. // BaseEffect is a class, we don't need to pass it by ref
  566. BaseEffect effect = _effectContext.GetEffect(effectOrder);
  567. Debug.Assert(effect.Type != EffectType.Invalid);
  568. Debug.Assert(effect.MixId == mix.MixId);
  569. if (!effect.ShouldSkip())
  570. {
  571. GenerateEffect(ref mix, effectOrder, effect);
  572. }
  573. }
  574. }
  575. private void GenerateMix(ref MixState mix)
  576. {
  577. if (mix.HasAnyDestination())
  578. {
  579. Debug.Assert(mix.DestinationMixId != Constants.UnusedMixId || mix.DestinationSplitterId != Constants.UnusedSplitterId);
  580. if (mix.DestinationMixId == Constants.UnusedMixId)
  581. {
  582. if (mix.DestinationSplitterId != Constants.UnusedSplitterId)
  583. {
  584. int destinationId = 0;
  585. while (true)
  586. {
  587. int destinationIndex = destinationId++;
  588. Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex);
  589. if (destinationSpan.IsEmpty)
  590. {
  591. break;
  592. }
  593. ref SplitterDestination destination = ref destinationSpan[0];
  594. if (destination.IsConfigured())
  595. {
  596. int mixId = destination.DestinationId;
  597. if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
  598. {
  599. ref MixState destinationMix = ref _mixContext.GetState(mixId);
  600. uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount);
  601. for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
  602. {
  603. float volume = mix.Volume * destination.GetMixVolume((int)bufferDestinationIndex);
  604. if (volume != 0.0f)
  605. {
  606. _commandBuffer.GenerateMix(inputBufferIndex,
  607. destinationMix.BufferOffset + bufferDestinationIndex,
  608. mix.NodeId,
  609. volume);
  610. }
  611. }
  612. }
  613. }
  614. }
  615. }
  616. }
  617. else
  618. {
  619. ref MixState destinationMix = ref _mixContext.GetState(mix.DestinationMixId);
  620. for (uint bufferIndex = 0; bufferIndex < mix.BufferCount; bufferIndex++)
  621. {
  622. for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
  623. {
  624. float volume = mix.Volume * mix.GetMixBufferVolume((int)bufferIndex, (int)bufferDestinationIndex);
  625. if (volume != 0.0f)
  626. {
  627. _commandBuffer.GenerateMix(mix.BufferOffset + bufferIndex,
  628. destinationMix.BufferOffset + bufferDestinationIndex,
  629. mix.NodeId,
  630. volume);
  631. }
  632. }
  633. }
  634. }
  635. }
  636. }
  637. private void GenerateSubMix(ref MixState subMix)
  638. {
  639. _commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
  640. subMix.BufferOffset,
  641. subMix.BufferCount,
  642. subMix.NodeId,
  643. subMix.SampleRate);
  644. GenerateEffects(ref subMix);
  645. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  646. int nodeId = subMix.NodeId;
  647. bool performanceInitialized = false;
  648. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.SubMix, nodeId))
  649. {
  650. performanceInitialized = true;
  651. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  652. }
  653. GenerateMix(ref subMix);
  654. if (performanceInitialized)
  655. {
  656. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  657. }
  658. }
  659. public void GenerateSubMixes()
  660. {
  661. for (int id = 0; id < _mixContext.GetCount(); id++)
  662. {
  663. ref MixState sortedState = ref _mixContext.GetSortedState(id);
  664. if (sortedState.IsUsed && sortedState.MixId != Constants.FinalMixId)
  665. {
  666. int nodeId = sortedState.NodeId;
  667. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  668. bool performanceInitialized = false;
  669. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.SubMix, nodeId))
  670. {
  671. performanceInitialized = true;
  672. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  673. }
  674. GenerateSubMix(ref sortedState);
  675. if (performanceInitialized)
  676. {
  677. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  678. }
  679. }
  680. }
  681. }
  682. private void GenerateFinalMix()
  683. {
  684. ref MixState finalMix = ref _mixContext.GetFinalState();
  685. _commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
  686. finalMix.BufferOffset,
  687. finalMix.BufferCount,
  688. finalMix.NodeId,
  689. finalMix.SampleRate);
  690. GenerateEffects(ref finalMix);
  691. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  692. int nodeId = finalMix.NodeId;
  693. bool performanceInitialized = false;
  694. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.FinalMix, nodeId))
  695. {
  696. performanceInitialized = true;
  697. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  698. }
  699. // Only generate volume command if the volume isn't 100%.
  700. if (finalMix.Volume != 1.0f)
  701. {
  702. for (uint bufferIndex = 0; bufferIndex < finalMix.BufferCount; bufferIndex++)
  703. {
  704. bool performanceSubInitialized = false;
  705. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.VolumeRamp, PerformanceEntryType.FinalMix, nodeId))
  706. {
  707. performanceSubInitialized = true;
  708. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  709. }
  710. _commandBuffer.GenerateVolume(finalMix.Volume,
  711. finalMix.BufferOffset + bufferIndex,
  712. nodeId);
  713. if (performanceSubInitialized)
  714. {
  715. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  716. }
  717. }
  718. }
  719. if (performanceInitialized)
  720. {
  721. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  722. }
  723. }
  724. public void GenerateFinalMixes()
  725. {
  726. int nodeId = _mixContext.GetFinalState().NodeId;
  727. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  728. bool performanceInitialized = false;
  729. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.FinalMix, nodeId))
  730. {
  731. performanceInitialized = true;
  732. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  733. }
  734. GenerateFinalMix();
  735. if (performanceInitialized)
  736. {
  737. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  738. }
  739. }
  740. private void GenerateCircularBuffer(CircularBufferSink sink, ref MixState finalMix)
  741. {
  742. _commandBuffer.GenerateCircularBuffer(finalMix.BufferOffset, sink, Constants.InvalidNodeId);
  743. }
  744. private void GenerateDevice(DeviceSink sink, ref MixState finalMix)
  745. {
  746. if (_commandBuffer.CommandList.SampleRate != 48000 && sink.UpsamplerState == null)
  747. {
  748. sink.UpsamplerState = _rendererContext.UpsamplerManager.Allocate();
  749. }
  750. bool useCustomDownMixingCommand = _rendererContext.ChannelCount == 2 && sink.Parameter.DownMixParameterEnabled;
  751. if (useCustomDownMixingCommand)
  752. {
  753. _commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
  754. sink.Parameter.Input.ToSpan(),
  755. sink.Parameter.Input.ToSpan(),
  756. sink.DownMixCoefficients,
  757. Constants.InvalidNodeId);
  758. }
  759. // NOTE: We do the downmixing at the DSP level as it's easier that way.
  760. else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6)
  761. {
  762. _commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
  763. sink.Parameter.Input.ToSpan(),
  764. sink.Parameter.Input.ToSpan(),
  765. Constants.DefaultSurroundToStereoCoefficients,
  766. Constants.InvalidNodeId);
  767. }
  768. CommandList commandList = _commandBuffer.CommandList;
  769. if (sink.UpsamplerState != null)
  770. {
  771. _commandBuffer.GenerateUpsample(finalMix.BufferOffset,
  772. sink.UpsamplerState,
  773. sink.Parameter.InputCount,
  774. sink.Parameter.Input.ToSpan(),
  775. commandList.BufferCount,
  776. commandList.SampleCount,
  777. commandList.SampleRate,
  778. Constants.InvalidNodeId);
  779. }
  780. _commandBuffer.GenerateDeviceSink(finalMix.BufferOffset,
  781. sink,
  782. _rendererContext.SessionId,
  783. commandList.Buffers,
  784. Constants.InvalidNodeId);
  785. }
  786. private void GenerateSink(BaseSink sink, ref MixState finalMix)
  787. {
  788. bool performanceInitialized = false;
  789. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  790. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.Sink, sink.NodeId))
  791. {
  792. performanceInitialized = true;
  793. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, sink.NodeId);
  794. }
  795. if (!sink.ShouldSkip)
  796. {
  797. switch (sink.Type)
  798. {
  799. case SinkType.CircularBuffer:
  800. GenerateCircularBuffer((CircularBufferSink)sink, ref finalMix);
  801. break;
  802. case SinkType.Device:
  803. GenerateDevice((DeviceSink)sink, ref finalMix);
  804. break;
  805. default:
  806. throw new NotImplementedException($"Unsupported sink type {sink.Type}");
  807. }
  808. sink.UpdateForCommandGeneration();
  809. }
  810. if (performanceInitialized)
  811. {
  812. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, sink.NodeId);
  813. }
  814. }
  815. public void GenerateSinks()
  816. {
  817. ref MixState finalMix = ref _mixContext.GetFinalState();
  818. for (int i = 0; i < _sinkContext.GetCount(); i++)
  819. {
  820. // BaseSink is a class, we don't need to pass it by ref
  821. BaseSink sink = _sinkContext.GetSink(i);
  822. if (sink.IsUsed)
  823. {
  824. GenerateSink(sink, ref finalMix);
  825. }
  826. }
  827. }
  828. }
  829. }