CommandGenerator.cs 43 KB

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