CommandGenerator.cs 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  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. for (int i = 0; i < voiceState.BiquadFilters.Length; i++)
  139. {
  140. ref BiquadFilterParameter filter = ref voiceState.BiquadFilters[i];
  141. if (filter.Enable)
  142. {
  143. Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount);
  144. Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
  145. _commandBuffer.GenerateBiquadFilter(baseIndex,
  146. ref filter,
  147. stateMemory.Slice(i, 1),
  148. bufferOffset,
  149. bufferOffset,
  150. !voiceState.BiquadFilterNeedInitialization[i],
  151. nodeId);
  152. }
  153. }
  154. }
  155. private void GenerateVoiceMix(Span<float> mixVolumes, Span<float> previousMixVolumes, Memory<VoiceUpdateState> state, uint bufferOffset, uint bufferCount, uint bufferIndex, int nodeId)
  156. {
  157. if (bufferCount > Constants.VoiceChannelCountMax)
  158. {
  159. _commandBuffer.GenerateMixRampGrouped(bufferCount,
  160. bufferIndex,
  161. bufferOffset,
  162. previousMixVolumes,
  163. mixVolumes,
  164. state,
  165. nodeId);
  166. }
  167. else
  168. {
  169. for (int i = 0; i < bufferCount; i++)
  170. {
  171. float previousMixVolume = previousMixVolumes[i];
  172. float mixVolume = mixVolumes[i];
  173. if (mixVolume != 0.0f || previousMixVolume != 0.0f)
  174. {
  175. _commandBuffer.GenerateMixRamp(previousMixVolume,
  176. mixVolume,
  177. bufferIndex,
  178. bufferOffset + (uint)i,
  179. i,
  180. state,
  181. nodeId);
  182. }
  183. }
  184. }
  185. }
  186. private void GenerateVoice(ref VoiceState voiceState)
  187. {
  188. int nodeId = voiceState.NodeId;
  189. uint channelsCount = voiceState.ChannelsCount;
  190. for (int channelIndex = 0; channelIndex < channelsCount; channelIndex++)
  191. {
  192. Memory<VoiceUpdateState> dspStateMemory = _voiceContext.GetUpdateStateForDsp(voiceState.ChannelResourceIds[channelIndex]);
  193. ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(voiceState.ChannelResourceIds[channelIndex]);
  194. PerformanceDetailType dataSourceDetailType = PerformanceDetailType.Adpcm;
  195. if (voiceState.SampleFormat == SampleFormat.PcmInt16)
  196. {
  197. dataSourceDetailType = PerformanceDetailType.PcmInt16;
  198. }
  199. else if (voiceState.SampleFormat == SampleFormat.PcmFloat)
  200. {
  201. dataSourceDetailType = PerformanceDetailType.PcmFloat;
  202. }
  203. bool performanceInitialized = false;
  204. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  205. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, dataSourceDetailType, PerformanceEntryType.Voice, nodeId))
  206. {
  207. performanceInitialized = true;
  208. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  209. }
  210. GenerateDataSource(ref voiceState, dspStateMemory, channelIndex);
  211. if (performanceInitialized)
  212. {
  213. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  214. }
  215. if (voiceState.WasPlaying)
  216. {
  217. voiceState.PreviousVolume = 0.0f;
  218. }
  219. else if (voiceState.HasAnyDestination())
  220. {
  221. performanceInitialized = false;
  222. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.BiquadFilter, PerformanceEntryType.Voice, nodeId))
  223. {
  224. performanceInitialized = true;
  225. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  226. }
  227. GenerateBiquadFilterForVoice(ref voiceState, dspStateMemory, (int)_rendererContext.MixBufferCount, channelIndex, nodeId);
  228. if (performanceInitialized)
  229. {
  230. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  231. }
  232. performanceInitialized = false;
  233. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.VolumeRamp, PerformanceEntryType.Voice, nodeId))
  234. {
  235. performanceInitialized = true;
  236. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  237. }
  238. _commandBuffer.GenerateVolumeRamp(voiceState.PreviousVolume,
  239. voiceState.Volume,
  240. _rendererContext.MixBufferCount + (uint)channelIndex,
  241. nodeId);
  242. if (performanceInitialized)
  243. {
  244. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  245. }
  246. voiceState.PreviousVolume = voiceState.Volume;
  247. if (voiceState.MixId == Constants.UnusedMixId)
  248. {
  249. if (voiceState.SplitterId != Constants.UnusedSplitterId)
  250. {
  251. int destinationId = channelIndex;
  252. while (true)
  253. {
  254. Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)voiceState.SplitterId, destinationId);
  255. if (destinationSpan.IsEmpty)
  256. {
  257. break;
  258. }
  259. ref SplitterDestination destination = ref destinationSpan[0];
  260. destinationId += (int)channelsCount;
  261. if (destination.IsConfigured())
  262. {
  263. int mixId = destination.DestinationId;
  264. if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
  265. {
  266. ref MixState mix = ref _mixContext.GetState(mixId);
  267. GenerateVoiceMix(destination.MixBufferVolume,
  268. destination.PreviousMixBufferVolume,
  269. dspStateMemory,
  270. mix.BufferOffset,
  271. mix.BufferCount,
  272. _rendererContext.MixBufferCount + (uint)channelIndex,
  273. nodeId);
  274. destination.MarkAsNeedToUpdateInternalState();
  275. }
  276. }
  277. }
  278. }
  279. }
  280. else
  281. {
  282. ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
  283. performanceInitialized = false;
  284. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.Voice, nodeId))
  285. {
  286. performanceInitialized = true;
  287. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  288. }
  289. GenerateVoiceMix(channelResource.Mix.ToSpan(),
  290. channelResource.PreviousMix.ToSpan(),
  291. dspStateMemory,
  292. mix.BufferOffset,
  293. mix.BufferCount,
  294. _rendererContext.MixBufferCount + (uint)channelIndex,
  295. nodeId);
  296. if (performanceInitialized)
  297. {
  298. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  299. }
  300. channelResource.UpdateState();
  301. }
  302. for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++)
  303. {
  304. voiceState.BiquadFilterNeedInitialization[i] = voiceState.BiquadFilters[i].Enable;
  305. }
  306. }
  307. }
  308. }
  309. public void GenerateVoices()
  310. {
  311. for (int i = 0; i < _voiceContext.GetCount(); i++)
  312. {
  313. ref VoiceState sortedState = ref _voiceContext.GetSortedState(i);
  314. if (!sortedState.ShouldSkip() && sortedState.UpdateForCommandGeneration(_voiceContext))
  315. {
  316. int nodeId = sortedState.NodeId;
  317. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  318. bool performanceInitialized = false;
  319. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.Voice, nodeId))
  320. {
  321. performanceInitialized = true;
  322. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  323. }
  324. GenerateVoice(ref sortedState);
  325. if (performanceInitialized)
  326. {
  327. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  328. }
  329. }
  330. }
  331. _splitterContext.UpdateInternalState();
  332. }
  333. public void GeneratePerformance(ref PerformanceEntryAddresses performanceEntryAddresses, PerformanceCommand.Type type, int nodeId)
  334. {
  335. _commandBuffer.GeneratePerformance(ref performanceEntryAddresses, type, nodeId);
  336. }
  337. private void GenerateBufferMixerEffect(int bufferOffset, BufferMixEffect effect, int nodeId)
  338. {
  339. Debug.Assert(effect.Type == EffectType.BufferMix);
  340. if (effect.IsEnabled)
  341. {
  342. for (int i = 0; i < effect.Parameter.MixesCount; i++)
  343. {
  344. if (effect.Parameter.Volumes[i] != 0.0f)
  345. {
  346. _commandBuffer.GenerateMix((uint)bufferOffset + effect.Parameter.Input[i],
  347. (uint)bufferOffset + effect.Parameter.Output[i],
  348. nodeId,
  349. effect.Parameter.Volumes[i]);
  350. }
  351. }
  352. }
  353. }
  354. private void GenerateAuxEffect(uint bufferOffset, AuxiliaryBufferEffect effect, int nodeId)
  355. {
  356. Debug.Assert(effect.Type == EffectType.AuxiliaryBuffer);
  357. if (effect.IsEnabled)
  358. {
  359. effect.GetWorkBuffer(0);
  360. effect.GetWorkBuffer(1);
  361. }
  362. if (effect.State.SendBufferInfoBase != 0 && effect.State.ReturnBufferInfoBase != 0)
  363. {
  364. int i = 0;
  365. uint writeOffset = 0;
  366. for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
  367. {
  368. uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
  369. uint updateCount;
  370. if ((channelIndex - 1) != 0)
  371. {
  372. updateCount = 0;
  373. }
  374. else
  375. {
  376. updateCount = newUpdateCount;
  377. }
  378. _commandBuffer.GenerateAuxEffect(bufferOffset,
  379. effect.Parameter.Input[i],
  380. effect.Parameter.Output[i],
  381. ref effect.State,
  382. effect.IsEnabled,
  383. effect.Parameter.BufferStorageSize,
  384. effect.State.SendBufferInfoBase,
  385. effect.State.ReturnBufferInfoBase,
  386. updateCount,
  387. writeOffset,
  388. nodeId);
  389. writeOffset = newUpdateCount;
  390. i++;
  391. }
  392. }
  393. }
  394. private void GenerateDelayEffect(uint bufferOffset, DelayEffect effect, int nodeId)
  395. {
  396. Debug.Assert(effect.Type == EffectType.Delay);
  397. ulong workBuffer = effect.GetWorkBuffer(-1);
  398. _commandBuffer.GenerateDelayEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId);
  399. }
  400. private void GenerateReverbEffect(uint bufferOffset, ReverbEffect effect, int nodeId, bool isLongSizePreDelaySupported)
  401. {
  402. Debug.Assert(effect.Type == EffectType.Reverb);
  403. ulong workBuffer = effect.GetWorkBuffer(-1);
  404. _commandBuffer.GenerateReverbEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, isLongSizePreDelaySupported);
  405. }
  406. private void GenerateReverb3dEffect(uint bufferOffset, Reverb3dEffect effect, int nodeId)
  407. {
  408. Debug.Assert(effect.Type == EffectType.Reverb3d);
  409. ulong workBuffer = effect.GetWorkBuffer(-1);
  410. _commandBuffer.GenerateReverb3dEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId);
  411. }
  412. private void GenerateBiquadFilterEffect(uint bufferOffset, BiquadFilterEffect effect, int nodeId)
  413. {
  414. Debug.Assert(effect.Type == EffectType.BiquadFilter);
  415. if (effect.IsEnabled)
  416. {
  417. bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
  418. (effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed());
  419. BiquadFilterParameter parameter = new BiquadFilterParameter();
  420. parameter.Enable = true;
  421. effect.Parameter.Denominator.ToSpan().CopyTo(parameter.Denominator.ToSpan());
  422. effect.Parameter.Numerator.ToSpan().CopyTo(parameter.Numerator.ToSpan());
  423. for (int i = 0; i < effect.Parameter.ChannelCount; i++)
  424. {
  425. _commandBuffer.GenerateBiquadFilter((int)bufferOffset, ref parameter, effect.State.Slice(i, 1),
  426. effect.Parameter.Input[i],
  427. effect.Parameter.Output[i],
  428. needInitialization,
  429. nodeId);
  430. }
  431. }
  432. else
  433. {
  434. for (int i = 0; i < effect.Parameter.ChannelCount; i++)
  435. {
  436. uint inputBufferIndex = bufferOffset + effect.Parameter.Input[i];
  437. uint outputBufferIndex = bufferOffset + effect.Parameter.Output[i];
  438. // If the input and output isn't the same, generate a command.
  439. if (inputBufferIndex != outputBufferIndex)
  440. {
  441. _commandBuffer.GenerateCopyMixBuffer(inputBufferIndex, outputBufferIndex, nodeId);
  442. }
  443. }
  444. }
  445. }
  446. private void GenerateEffect(ref MixState mix, BaseEffect effect)
  447. {
  448. int nodeId = mix.NodeId;
  449. bool isFinalMix = mix.MixId == Constants.FinalMixId;
  450. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  451. bool performanceInitialized = false;
  452. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, effect.GetPerformanceDetailType(),
  453. isFinalMix ? PerformanceEntryType.FinalMix : PerformanceEntryType.SubMix, nodeId))
  454. {
  455. performanceInitialized = true;
  456. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  457. }
  458. switch (effect.Type)
  459. {
  460. case EffectType.BufferMix:
  461. GenerateBufferMixerEffect((int)mix.BufferOffset, (BufferMixEffect)effect, nodeId);
  462. break;
  463. case EffectType.AuxiliaryBuffer:
  464. GenerateAuxEffect(mix.BufferOffset, (AuxiliaryBufferEffect)effect, nodeId);
  465. break;
  466. case EffectType.Delay:
  467. GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId);
  468. break;
  469. case EffectType.Reverb:
  470. GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported);
  471. break;
  472. case EffectType.Reverb3d:
  473. GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId);
  474. break;
  475. case EffectType.BiquadFilter:
  476. GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId);
  477. break;
  478. default:
  479. throw new NotImplementedException($"Unsupported effect type {effect.Type}");
  480. }
  481. if (performanceInitialized)
  482. {
  483. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  484. }
  485. effect.UpdateForCommandGeneration();
  486. }
  487. private void GenerateEffects(ref MixState mix)
  488. {
  489. ReadOnlySpan<int> effectProcessingOrderArray = mix.EffectProcessingOrderArray;
  490. Debug.Assert(_effectContext.GetCount() == 0 || !effectProcessingOrderArray.IsEmpty);
  491. for (int i = 0; i < _effectContext.GetCount(); i++)
  492. {
  493. int effectOrder = effectProcessingOrderArray[i];
  494. if (effectOrder == Constants.InvalidProcessingOrder)
  495. {
  496. break;
  497. }
  498. // BaseEffect is a class, we don't need to pass it by ref
  499. BaseEffect effect = _effectContext.GetEffect(effectOrder);
  500. Debug.Assert(effect.Type != EffectType.Invalid);
  501. Debug.Assert(effect.MixId == mix.MixId);
  502. if (!effect.ShouldSkip())
  503. {
  504. GenerateEffect(ref mix, effect);
  505. }
  506. }
  507. }
  508. private void GenerateMix(ref MixState mix)
  509. {
  510. if (mix.HasAnyDestination())
  511. {
  512. Debug.Assert(mix.DestinationMixId != Constants.UnusedMixId || mix.DestinationSplitterId != Constants.UnusedSplitterId);
  513. if (mix.DestinationMixId == Constants.UnusedMixId)
  514. {
  515. if (mix.DestinationSplitterId != Constants.UnusedSplitterId)
  516. {
  517. int destinationId = 0;
  518. while (true)
  519. {
  520. int destinationIndex = destinationId++;
  521. Span<SplitterDestination> destinationSpan = _splitterContext.GetDestination((int)mix.DestinationSplitterId, destinationIndex);
  522. if (destinationSpan.IsEmpty)
  523. {
  524. break;
  525. }
  526. ref SplitterDestination destination = ref destinationSpan[0];
  527. if (destination.IsConfigured())
  528. {
  529. int mixId = destination.DestinationId;
  530. if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
  531. {
  532. ref MixState destinationMix = ref _mixContext.GetState(mixId);
  533. uint inputBufferIndex = mix.BufferOffset + ((uint)destinationIndex % mix.BufferCount);
  534. for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
  535. {
  536. float volume = mix.Volume * destination.GetMixVolume((int)bufferDestinationIndex);
  537. if (volume != 0.0f)
  538. {
  539. _commandBuffer.GenerateMix(inputBufferIndex,
  540. destinationMix.BufferOffset + bufferDestinationIndex,
  541. mix.NodeId,
  542. volume);
  543. }
  544. }
  545. }
  546. }
  547. }
  548. }
  549. }
  550. else
  551. {
  552. ref MixState destinationMix = ref _mixContext.GetState(mix.DestinationMixId);
  553. for (uint bufferIndex = 0; bufferIndex < mix.BufferCount; bufferIndex++)
  554. {
  555. for (uint bufferDestinationIndex = 0; bufferDestinationIndex < destinationMix.BufferCount; bufferDestinationIndex++)
  556. {
  557. float volume = mix.Volume * mix.GetMixBufferVolume((int)bufferIndex, (int)bufferDestinationIndex);
  558. if (volume != 0.0f)
  559. {
  560. _commandBuffer.GenerateMix(mix.BufferOffset + bufferIndex,
  561. destinationMix.BufferOffset + bufferDestinationIndex,
  562. mix.NodeId,
  563. volume);
  564. }
  565. }
  566. }
  567. }
  568. }
  569. }
  570. private void GenerateSubMix(ref MixState subMix)
  571. {
  572. _commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
  573. subMix.BufferOffset,
  574. subMix.BufferCount,
  575. subMix.NodeId,
  576. subMix.SampleRate);
  577. GenerateEffects(ref subMix);
  578. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  579. int nodeId = subMix.NodeId;
  580. bool performanceInitialized = false;
  581. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.SubMix, nodeId))
  582. {
  583. performanceInitialized = true;
  584. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  585. }
  586. GenerateMix(ref subMix);
  587. if (performanceInitialized)
  588. {
  589. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  590. }
  591. }
  592. public void GenerateSubMixes()
  593. {
  594. for (int id = 0; id < _mixContext.GetCount(); id++)
  595. {
  596. ref MixState sortedState = ref _mixContext.GetSortedState(id);
  597. if (sortedState.IsUsed && sortedState.MixId != Constants.FinalMixId)
  598. {
  599. int nodeId = sortedState.NodeId;
  600. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  601. bool performanceInitialized = false;
  602. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.SubMix, nodeId))
  603. {
  604. performanceInitialized = true;
  605. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  606. }
  607. GenerateSubMix(ref sortedState);
  608. if (performanceInitialized)
  609. {
  610. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  611. }
  612. }
  613. }
  614. }
  615. private void GenerateFinalMix()
  616. {
  617. ref MixState finalMix = ref _mixContext.GetFinalState();
  618. _commandBuffer.GenerateDepopForMixBuffersCommand(_rendererContext.DepopBuffer,
  619. finalMix.BufferOffset,
  620. finalMix.BufferCount,
  621. finalMix.NodeId,
  622. finalMix.SampleRate);
  623. GenerateEffects(ref finalMix);
  624. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  625. int nodeId = finalMix.NodeId;
  626. bool performanceInitialized = false;
  627. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.Mix, PerformanceEntryType.FinalMix, nodeId))
  628. {
  629. performanceInitialized = true;
  630. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  631. }
  632. // Only generate volume command if the volume isn't 100%.
  633. if (finalMix.Volume != 1.0f)
  634. {
  635. for (uint bufferIndex = 0; bufferIndex < finalMix.BufferCount; bufferIndex++)
  636. {
  637. bool performanceSubInitialized = false;
  638. if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, PerformanceDetailType.VolumeRamp, PerformanceEntryType.FinalMix, nodeId))
  639. {
  640. performanceSubInitialized = true;
  641. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  642. }
  643. _commandBuffer.GenerateVolume(finalMix.Volume,
  644. finalMix.BufferOffset + bufferIndex,
  645. nodeId);
  646. if (performanceSubInitialized)
  647. {
  648. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  649. }
  650. }
  651. }
  652. if (performanceInitialized)
  653. {
  654. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  655. }
  656. }
  657. public void GenerateFinalMixes()
  658. {
  659. int nodeId = _mixContext.GetFinalState().NodeId;
  660. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  661. bool performanceInitialized = false;
  662. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.FinalMix, nodeId))
  663. {
  664. performanceInitialized = true;
  665. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, nodeId);
  666. }
  667. GenerateFinalMix();
  668. if (performanceInitialized)
  669. {
  670. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, nodeId);
  671. }
  672. }
  673. private void GenerateCircularBuffer(CircularBufferSink sink, ref MixState finalMix)
  674. {
  675. _commandBuffer.GenerateCircularBuffer(finalMix.BufferOffset, sink, Constants.InvalidNodeId);
  676. }
  677. private void GenerateDevice(DeviceSink sink, ref MixState finalMix)
  678. {
  679. if (_commandBuffer.CommandList.SampleRate != 48000 && sink.UpsamplerState == null)
  680. {
  681. sink.UpsamplerState = _rendererContext.UpsamplerManager.Allocate();
  682. }
  683. bool useCustomDownMixingCommand = _rendererContext.ChannelCount == 2 && sink.Parameter.DownMixParameterEnabled;
  684. if (useCustomDownMixingCommand)
  685. {
  686. _commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
  687. sink.Parameter.Input.ToSpan(),
  688. sink.Parameter.Input.ToSpan(),
  689. sink.DownMixCoefficients,
  690. Constants.InvalidNodeId);
  691. }
  692. // NOTE: We do the downmixing at the DSP level as it's easier that way.
  693. else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6)
  694. {
  695. _commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
  696. sink.Parameter.Input.ToSpan(),
  697. sink.Parameter.Input.ToSpan(),
  698. Constants.DefaultSurroundToStereoCoefficients,
  699. Constants.InvalidNodeId);
  700. }
  701. CommandList commandList = _commandBuffer.CommandList;
  702. if (sink.UpsamplerState != null)
  703. {
  704. _commandBuffer.GenerateUpsample(finalMix.BufferOffset,
  705. sink.UpsamplerState,
  706. sink.Parameter.InputCount,
  707. sink.Parameter.Input.ToSpan(),
  708. commandList.BufferCount,
  709. commandList.SampleCount,
  710. commandList.SampleRate,
  711. Constants.InvalidNodeId);
  712. }
  713. _commandBuffer.GenerateDeviceSink(finalMix.BufferOffset,
  714. sink,
  715. _rendererContext.SessionId,
  716. commandList.Buffers,
  717. Constants.InvalidNodeId);
  718. }
  719. private void GenerateSink(BaseSink sink, ref MixState finalMix)
  720. {
  721. bool performanceInitialized = false;
  722. PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
  723. if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.Sink, sink.NodeId))
  724. {
  725. performanceInitialized = true;
  726. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.Start, sink.NodeId);
  727. }
  728. if (!sink.ShouldSkip)
  729. {
  730. switch (sink.Type)
  731. {
  732. case SinkType.CircularBuffer:
  733. GenerateCircularBuffer((CircularBufferSink)sink, ref finalMix);
  734. break;
  735. case SinkType.Device:
  736. GenerateDevice((DeviceSink)sink, ref finalMix);
  737. break;
  738. default:
  739. throw new NotImplementedException($"Unsupported sink type {sink.Type}");
  740. }
  741. sink.UpdateForCommandGeneration();
  742. }
  743. if (performanceInitialized)
  744. {
  745. GeneratePerformance(ref performanceEntry, PerformanceCommand.Type.End, sink.NodeId);
  746. }
  747. }
  748. public void GenerateSinks()
  749. {
  750. ref MixState finalMix = ref _mixContext.GetFinalState();
  751. for (int i = 0; i < _sinkContext.GetCount(); i++)
  752. {
  753. // BaseSink is a class, we don't need to pass it by ref
  754. BaseSink sink = _sinkContext.GetSink(i);
  755. if (sink.IsUsed)
  756. {
  757. GenerateSink(sink, ref finalMix);
  758. }
  759. }
  760. }
  761. }
  762. }