VoiceState.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  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.Parameter;
  20. using Ryujinx.Audio.Renderer.Server.MemoryPool;
  21. using Ryujinx.Common.Memory;
  22. using Ryujinx.Common.Utilities;
  23. using System;
  24. using System.Diagnostics;
  25. using System.Runtime.InteropServices;
  26. using static Ryujinx.Audio.Renderer.Common.BehaviourParameter;
  27. using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter;
  28. namespace Ryujinx.Audio.Renderer.Server.Voice
  29. {
  30. [StructLayout(LayoutKind.Sequential, Pack = Alignment)]
  31. public struct VoiceState
  32. {
  33. public const int Alignment = 0x10;
  34. /// <summary>
  35. /// Set to true if the voice is used.
  36. /// </summary>
  37. [MarshalAs(UnmanagedType.I1)]
  38. public bool InUse;
  39. /// <summary>
  40. /// Set to true if the voice is new.
  41. /// </summary>
  42. [MarshalAs(UnmanagedType.I1)]
  43. public bool IsNew;
  44. [MarshalAs(UnmanagedType.I1)]
  45. public bool WasPlaying;
  46. /// <summary>
  47. /// The <see cref="SampleFormat"/> of the voice.
  48. /// </summary>
  49. public SampleFormat SampleFormat;
  50. /// <summary>
  51. /// The sample rate of the voice.
  52. /// </summary>
  53. public uint SampleRate;
  54. /// <summary>
  55. /// The total channel count used.
  56. /// </summary>
  57. public uint ChannelsCount;
  58. /// <summary>
  59. /// Id of the voice.
  60. /// </summary>
  61. public int Id;
  62. /// <summary>
  63. /// Node id of the voice.
  64. /// </summary>
  65. public int NodeId;
  66. /// <summary>
  67. /// The target mix id of the voice.
  68. /// </summary>
  69. public int MixId;
  70. /// <summary>
  71. /// The current voice <see cref="Types.PlayState"/>.
  72. /// </summary>
  73. public Types.PlayState PlayState;
  74. /// <summary>
  75. /// The previous voice <see cref="Types.PlayState"/>.
  76. /// </summary>
  77. public Types.PlayState PreviousPlayState;
  78. /// <summary>
  79. /// The priority of the voice.
  80. /// </summary>
  81. public uint Priority;
  82. /// <summary>
  83. /// Target sorting position of the voice. (used to sort voice with the same <see cref="Priority"/>)
  84. /// </summary>
  85. public uint SortingOrder;
  86. /// <summary>
  87. /// The pitch used on the voice.
  88. /// </summary>
  89. public float Pitch;
  90. /// <summary>
  91. /// The output volume of the voice.
  92. /// </summary>
  93. public float Volume;
  94. /// <summary>
  95. /// The previous output volume of the voice.
  96. /// </summary>
  97. public float PreviousVolume;
  98. /// <summary>
  99. /// Biquad filters to apply to the output of the voice.
  100. /// </summary>
  101. public Array2<BiquadFilterParameter> BiquadFilters;
  102. /// <summary>
  103. /// Total count of <see cref="WaveBufferInternal"/> of the voice.
  104. /// </summary>
  105. public uint WaveBuffersCount;
  106. /// <summary>
  107. /// Current playing <see cref="WaveBufferInternal"/> of the voice.
  108. /// </summary>
  109. public uint WaveBuffersIndex;
  110. /// <summary>
  111. /// Change the behaviour of the voice.
  112. /// </summary>
  113. /// <remarks>This was added on REV5.</remarks>
  114. public DecodingBehaviour DecodingBehaviour;
  115. /// <summary>
  116. /// User state <see cref="AddressInfo"/> required by the data source.
  117. /// </summary>
  118. /// <remarks>Only used for <see cref="SampleFormat.Adpcm"/> as the GC-ADPCM coefficients.</remarks>
  119. public AddressInfo DataSourceStateAddressInfo;
  120. /// <summary>
  121. /// The wavebuffers of this voice.
  122. /// </summary>
  123. public Array4<WaveBuffer> WaveBuffers;
  124. /// <summary>
  125. /// The channel resource ids associated to the voice.
  126. /// </summary>
  127. public Array6<int> ChannelResourceIds;
  128. /// <summary>
  129. /// The target splitter id of the voice.
  130. /// </summary>
  131. public uint SplitterId;
  132. /// <summary>
  133. /// Change the Sample Rate Conversion (SRC) quality of the voice.
  134. /// </summary>
  135. /// <remarks>This was added on REV8.</remarks>
  136. public SampleRateConversionQuality SrcQuality;
  137. /// <summary>
  138. /// If set to true, the voice was dropped.
  139. /// </summary>
  140. [MarshalAs(UnmanagedType.I1)]
  141. public bool VoiceDropFlag;
  142. /// <summary>
  143. /// Set to true if the data source state work buffer wasn't mapped.
  144. /// </summary>
  145. [MarshalAs(UnmanagedType.I1)]
  146. public bool DataSourceStateUnmapped;
  147. /// <summary>
  148. /// Set to true if any of the <see cref="WaveBuffer.BufferAddressInfo"/> work buffer wasn't mapped.
  149. /// </summary>
  150. [MarshalAs(UnmanagedType.I1)]
  151. public bool BufferInfoUnmapped;
  152. /// <summary>
  153. /// The biquad filter initialization state storage.
  154. /// </summary>
  155. private BiquadFilterNeedInitializationArrayStruct _biquadFilterNeedInitialization;
  156. /// <summary>
  157. /// Flush the amount of wavebuffer specified. This will result in the wavebuffer being skipped and marked played.
  158. /// </summary>
  159. /// <remarks>This was added on REV5.</remarks>
  160. public byte FlushWaveBufferCount;
  161. [StructLayout(LayoutKind.Sequential, Size = Constants.VoiceBiquadFilterCount)]
  162. private struct BiquadFilterNeedInitializationArrayStruct { }
  163. /// <summary>
  164. /// The biquad filter initialization state array.
  165. /// </summary>
  166. public Span<bool> BiquadFilterNeedInitialization => SpanHelpers.AsSpan<BiquadFilterNeedInitializationArrayStruct, bool>(ref _biquadFilterNeedInitialization);
  167. /// <summary>
  168. /// Initialize the <see cref="VoiceState"/>.
  169. /// </summary>
  170. public void Initialize()
  171. {
  172. IsNew = false;
  173. VoiceDropFlag = false;
  174. DataSourceStateUnmapped = false;
  175. BufferInfoUnmapped = false;
  176. FlushWaveBufferCount = 0;
  177. PlayState = Types.PlayState.Stopped;
  178. Priority = Constants.VoiceLowestPriority;
  179. Id = 0;
  180. NodeId = 0;
  181. SampleRate = 0;
  182. SampleFormat = SampleFormat.Invalid;
  183. ChannelsCount = 0;
  184. Pitch = 0.0f;
  185. Volume= 0.0f;
  186. PreviousVolume = 0.0f;
  187. BiquadFilters.ToSpan().Fill(new BiquadFilterParameter());
  188. WaveBuffersCount = 0;
  189. WaveBuffersIndex = 0;
  190. MixId = Constants.UnusedMixId;
  191. SplitterId = Constants.UnusedSplitterId;
  192. DataSourceStateAddressInfo.Setup(0, 0);
  193. InitializeWaveBuffers();
  194. }
  195. /// <summary>
  196. /// Initialize the <see cref="WaveBuffer"/> in this <see cref="VoiceState"/>.
  197. /// </summary>
  198. private void InitializeWaveBuffers()
  199. {
  200. for (int i = 0; i < WaveBuffers.Length; i++)
  201. {
  202. WaveBuffers[i].StartSampleOffset = 0;
  203. WaveBuffers[i].EndSampleOffset = 0;
  204. WaveBuffers[i].ShouldLoop = false;
  205. WaveBuffers[i].IsEndOfStream = false;
  206. WaveBuffers[i].BufferAddressInfo.Setup(0, 0);
  207. WaveBuffers[i].ContextAddressInfo.Setup(0, 0);
  208. WaveBuffers[i].IsSendToAudioProcessor = true;
  209. }
  210. }
  211. /// <summary>
  212. /// Check if the voice needs to be skipped.
  213. /// </summary>
  214. /// <returns>Returns true if the voice needs to be skipped.</returns>
  215. public bool ShouldSkip()
  216. {
  217. return !InUse || WaveBuffersCount == 0 || DataSourceStateUnmapped || BufferInfoUnmapped || VoiceDropFlag;
  218. }
  219. /// <summary>
  220. /// Return true if the mix has any destinations.
  221. /// </summary>
  222. /// <returns>True if the mix has any destinations.</returns>
  223. public bool HasAnyDestination()
  224. {
  225. return MixId != Constants.UnusedMixId || SplitterId != Constants.UnusedSplitterId;
  226. }
  227. /// <summary>
  228. /// Indicate if the server voice information needs to be updated.
  229. /// </summary>
  230. /// <param name="parameter">The user parameter.</param>
  231. /// <returns>Return true, if the server voice information needs to be updated.</returns>
  232. private bool ShouldUpdateParameters(ref VoiceInParameter parameter)
  233. {
  234. if (DataSourceStateAddressInfo.CpuAddress == parameter.DataSourceStateAddress)
  235. {
  236. return DataSourceStateAddressInfo.Size != parameter.DataSourceStateSize;
  237. }
  238. return DataSourceStateAddressInfo.CpuAddress != parameter.DataSourceStateAddress ||
  239. DataSourceStateAddressInfo.Size != parameter.DataSourceStateSize ||
  240. DataSourceStateUnmapped;
  241. }
  242. /// <summary>
  243. /// Update the internal state from a user parameter.
  244. /// </summary>
  245. /// <param name="outErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param>
  246. /// <param name="parameter">The user parameter.</param>
  247. /// <param name="poolMapper">The mapper to use.</param>
  248. /// <param name="behaviourContext">The behaviour context.</param>
  249. public void UpdateParameters(out ErrorInfo outErrorInfo, ref VoiceInParameter parameter, ref PoolMapper poolMapper, ref BehaviourContext behaviourContext)
  250. {
  251. InUse = parameter.InUse;
  252. Id = parameter.Id;
  253. NodeId = parameter.NodeId;
  254. UpdatePlayState(parameter.PlayState);
  255. SrcQuality = parameter.SrcQuality;
  256. Priority = parameter.Priority;
  257. SortingOrder = parameter.SortingOrder;
  258. SampleRate = parameter.SampleRate;
  259. SampleFormat = parameter.SampleFormat;
  260. ChannelsCount = parameter.ChannelCount;
  261. Pitch = parameter.Pitch;
  262. Volume = parameter.Volume;
  263. parameter.BiquadFilters.ToSpan().CopyTo(BiquadFilters.ToSpan());
  264. WaveBuffersCount = parameter.WaveBuffersCount;
  265. WaveBuffersIndex = parameter.WaveBuffersIndex;
  266. if (behaviourContext.IsFlushVoiceWaveBuffersSupported())
  267. {
  268. FlushWaveBufferCount += parameter.FlushWaveBufferCount;
  269. }
  270. MixId = parameter.MixId;
  271. if (behaviourContext.IsSplitterSupported())
  272. {
  273. SplitterId = parameter.SplitterId;
  274. }
  275. else
  276. {
  277. SplitterId = Constants.UnusedSplitterId;
  278. }
  279. parameter.ChannelResourceIds.ToSpan().CopyTo(ChannelResourceIds.ToSpan());
  280. DecodingBehaviour behaviour = DecodingBehaviour.Default;
  281. if (behaviourContext.IsDecodingBehaviourFlagSupported())
  282. {
  283. behaviour = parameter.DecodingBehaviourFlags;
  284. }
  285. DecodingBehaviour = behaviour;
  286. if (parameter.ResetVoiceDropFlag)
  287. {
  288. VoiceDropFlag = false;
  289. }
  290. if (ShouldUpdateParameters(ref parameter))
  291. {
  292. DataSourceStateUnmapped = !poolMapper.TryAttachBuffer(out outErrorInfo, ref DataSourceStateAddressInfo, parameter.DataSourceStateAddress, parameter.DataSourceStateSize);
  293. }
  294. else
  295. {
  296. outErrorInfo = new ErrorInfo();
  297. }
  298. }
  299. /// <summary>
  300. /// Update the internal play state from user play state.
  301. /// </summary>
  302. /// <param name="userPlayState">The target user play state.</param>
  303. public void UpdatePlayState(PlayState userPlayState)
  304. {
  305. Types.PlayState oldServerPlayState = PlayState;
  306. PreviousPlayState = oldServerPlayState;
  307. Types.PlayState newServerPlayState;
  308. switch (userPlayState)
  309. {
  310. case Common.PlayState.Start:
  311. newServerPlayState = Types.PlayState.Started;
  312. break;
  313. case Common.PlayState.Stop:
  314. if (oldServerPlayState == Types.PlayState.Stopped)
  315. {
  316. return;
  317. }
  318. newServerPlayState = Types.PlayState.Stopping;
  319. break;
  320. case Common.PlayState.Pause:
  321. newServerPlayState = Types.PlayState.Paused;
  322. break;
  323. default:
  324. throw new NotImplementedException($"Unhandled PlayState.{userPlayState}");
  325. }
  326. PlayState = newServerPlayState;
  327. }
  328. /// <summary>
  329. /// Write the status of the voice to the given user output.
  330. /// </summary>
  331. /// <param name="outStatus">The given user output.</param>
  332. /// <param name="parameter">The user parameter.</param>
  333. /// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
  334. public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates)
  335. {
  336. #if DEBUG
  337. // Sanity check in debug mode of the internal state
  338. if (!parameter.IsNew && !IsNew)
  339. {
  340. for (int i = 1; i < ChannelsCount; i++)
  341. {
  342. ref VoiceUpdateState stateA = ref voiceUpdateStates[i - 1].Span[0];
  343. ref VoiceUpdateState stateB = ref voiceUpdateStates[i].Span[0];
  344. Debug.Assert(stateA.WaveBufferConsumed == stateB.WaveBufferConsumed);
  345. Debug.Assert(stateA.PlayedSampleCount == stateB.PlayedSampleCount);
  346. Debug.Assert(stateA.Offset == stateB.Offset);
  347. Debug.Assert(stateA.WaveBufferIndex == stateB.WaveBufferIndex);
  348. Debug.Assert(stateA.Fraction == stateB.Fraction);
  349. Debug.Assert(stateA.IsWaveBufferValid.SequenceEqual(stateB.IsWaveBufferValid));
  350. }
  351. }
  352. #endif
  353. if (parameter.IsNew || IsNew)
  354. {
  355. IsNew = true;
  356. outStatus.VoiceDropFlag = false;
  357. outStatus.PlayedWaveBuffersCount = 0;
  358. outStatus.PlayedSampleCount = 0;
  359. }
  360. else
  361. {
  362. ref VoiceUpdateState state = ref voiceUpdateStates[0].Span[0];
  363. outStatus.VoiceDropFlag = VoiceDropFlag;
  364. outStatus.PlayedWaveBuffersCount = state.WaveBufferConsumed;
  365. outStatus.PlayedSampleCount = state.PlayedSampleCount;
  366. }
  367. }
  368. /// <summary>
  369. /// Update the internal state of all the <see cref="WaveBuffer"/> of the <see cref="VoiceState"/>.
  370. /// </summary>
  371. /// <param name="errorInfos">An array of <see cref="ErrorInfo"/> used to report errors when mapping any of the <see cref="WaveBuffer"/>.</param>
  372. /// <param name="parameter">The user parameter.</param>
  373. /// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
  374. /// <param name="mapper">The mapper to use.</param>
  375. /// <param name="behaviourContext">The behaviour context.</param>
  376. public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
  377. {
  378. errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];
  379. if (parameter.IsNew)
  380. {
  381. InitializeWaveBuffers();
  382. for (int i = 0; i < parameter.ChannelCount; i++)
  383. {
  384. voiceUpdateStates[i].Span[0].IsWaveBufferValid.Fill(false);
  385. }
  386. }
  387. ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0];
  388. for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
  389. {
  390. UpdateWaveBuffer(errorInfos.AsSpan().Slice(i * 2, 2), ref WaveBuffers[i], ref parameter.WaveBuffers[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], ref mapper, ref behaviourContext);
  391. }
  392. }
  393. /// <summary>
  394. /// Update the internal state of one of the <see cref="WaveBuffer"/> of the <see cref="VoiceState"/>.
  395. /// </summary>
  396. /// <param name="errorInfos">A <see cref="Span{ErrorInfo}"/> used to report errors when mapping the <see cref="WaveBuffer"/>.</param>
  397. /// <param name="waveBuffer">The <see cref="WaveBuffer"/> to update.</param>
  398. /// <param name="inputWaveBuffer">The <see cref="WaveBufferInternal"/> from the user input.</param>
  399. /// <param name="sampleFormat">The <see cref="SampleFormat"/> from the user input.</param>
  400. /// <param name="isValid">If set to true, the server side wavebuffer is considered valid.</param>
  401. /// <param name="mapper">The mapper to use.</param>
  402. /// <param name="behaviourContext">The behaviour context.</param>
  403. private void UpdateWaveBuffer(Span<ErrorInfo> errorInfos, ref WaveBuffer waveBuffer, ref WaveBufferInternal inputWaveBuffer, SampleFormat sampleFormat, bool isValid, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
  404. {
  405. if (!isValid && waveBuffer.IsSendToAudioProcessor && waveBuffer.BufferAddressInfo.CpuAddress != 0)
  406. {
  407. mapper.ForceUnmap(ref waveBuffer.BufferAddressInfo);
  408. waveBuffer.BufferAddressInfo.Setup(0, 0);
  409. }
  410. if (!inputWaveBuffer.SentToServer || BufferInfoUnmapped)
  411. {
  412. if (inputWaveBuffer.IsSampleOffsetValid(sampleFormat))
  413. {
  414. Debug.Assert(waveBuffer.IsSendToAudioProcessor);
  415. waveBuffer.IsSendToAudioProcessor = false;
  416. waveBuffer.StartSampleOffset = inputWaveBuffer.StartSampleOffset;
  417. waveBuffer.EndSampleOffset = inputWaveBuffer.EndSampleOffset;
  418. waveBuffer.ShouldLoop = inputWaveBuffer.ShouldLoop;
  419. waveBuffer.IsEndOfStream = inputWaveBuffer.IsEndOfStream;
  420. waveBuffer.LoopStartSampleOffset = inputWaveBuffer.LoopFirstSampleOffset;
  421. waveBuffer.LoopEndSampleOffset = inputWaveBuffer.LoopLastSampleOffset;
  422. waveBuffer.LoopCount = inputWaveBuffer.LoopCount;
  423. BufferInfoUnmapped = !mapper.TryAttachBuffer(out ErrorInfo bufferInfoError, ref waveBuffer.BufferAddressInfo, inputWaveBuffer.Address, inputWaveBuffer.Size);
  424. errorInfos[0] = bufferInfoError;
  425. if (sampleFormat == SampleFormat.Adpcm && behaviourContext.IsAdpcmLoopContextBugFixed() && inputWaveBuffer.ContextAddress != 0)
  426. {
  427. bool adpcmLoopContextMapped = mapper.TryAttachBuffer(out ErrorInfo adpcmLoopContextInfoError,
  428. ref waveBuffer.ContextAddressInfo,
  429. inputWaveBuffer.ContextAddress,
  430. inputWaveBuffer.ContextSize);
  431. errorInfos[1] = adpcmLoopContextInfoError;
  432. if (adpcmLoopContextMapped)
  433. {
  434. BufferInfoUnmapped = DataSourceStateUnmapped;
  435. }
  436. else
  437. {
  438. BufferInfoUnmapped = true;
  439. }
  440. }
  441. else
  442. {
  443. waveBuffer.ContextAddressInfo.Setup(0, 0);
  444. }
  445. }
  446. else
  447. {
  448. errorInfos[0].ErrorCode = ResultCode.InvalidAddressInfo;
  449. errorInfos[0].ExtraErrorInfo = inputWaveBuffer.Address;
  450. }
  451. }
  452. }
  453. /// <summary>
  454. /// Reset the resources associated to this <see cref="VoiceState"/>.
  455. /// </summary>
  456. /// <param name="context">The voice context.</param>
  457. private void ResetResources(VoiceContext context)
  458. {
  459. for (int i = 0; i < ChannelsCount; i++)
  460. {
  461. int channelResourceId = ChannelResourceIds[i];
  462. ref VoiceChannelResource voiceChannelResource = ref context.GetChannelResource(channelResourceId);
  463. Debug.Assert(voiceChannelResource.IsUsed);
  464. Memory<VoiceUpdateState> dspSharedState = context.GetUpdateStateForDsp(channelResourceId);
  465. MemoryMarshal.Cast<VoiceUpdateState, byte>(dspSharedState.Span).Fill(0);
  466. voiceChannelResource.UpdateState();
  467. }
  468. }
  469. /// <summary>
  470. /// Flush a certain amount of <see cref="WaveBuffer"/>.
  471. /// </summary>
  472. /// <param name="waveBufferCount">The amount of wavebuffer to flush.</param>
  473. /// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
  474. /// <param name="channelCount">The channel count from user input.</param>
  475. private void FlushWaveBuffers(uint waveBufferCount, Memory<VoiceUpdateState>[] voiceUpdateStates, uint channelCount)
  476. {
  477. uint waveBufferIndex = WaveBuffersIndex;
  478. for (int i = 0; i < waveBufferCount; i++)
  479. {
  480. WaveBuffers[(int)waveBufferIndex].IsSendToAudioProcessor = true;
  481. for (int j = 0; j < channelCount; j++)
  482. {
  483. ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[j].Span[0];
  484. voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount;
  485. voiceUpdateState.WaveBufferConsumed++;
  486. voiceUpdateState.IsWaveBufferValid[(int)waveBufferIndex] = false;
  487. }
  488. waveBufferIndex = (waveBufferIndex + 1) % Constants.VoiceWaveBufferCount;
  489. }
  490. }
  491. /// <summary>
  492. /// Update the internal parameters for command generation.
  493. /// </summary>
  494. /// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
  495. /// <returns>Return true if this voice should be played.</returns>
  496. public bool UpdateParametersForCommandGeneration(Memory<VoiceUpdateState>[] voiceUpdateStates)
  497. {
  498. if (FlushWaveBufferCount != 0)
  499. {
  500. FlushWaveBuffers(FlushWaveBufferCount, voiceUpdateStates, ChannelsCount);
  501. FlushWaveBufferCount = 0;
  502. }
  503. switch (PlayState)
  504. {
  505. case Types.PlayState.Started:
  506. for (int i = 0; i < WaveBuffers.Length; i++)
  507. {
  508. ref WaveBuffer wavebuffer = ref WaveBuffers[i];
  509. if (!wavebuffer.IsSendToAudioProcessor)
  510. {
  511. for (int y = 0; y < ChannelsCount; y++)
  512. {
  513. Debug.Assert(!voiceUpdateStates[y].Span[0].IsWaveBufferValid[i]);
  514. voiceUpdateStates[y].Span[0].IsWaveBufferValid[i] = true;
  515. }
  516. wavebuffer.IsSendToAudioProcessor = true;
  517. }
  518. }
  519. WasPlaying = false;
  520. ref VoiceUpdateState primaryVoiceUpdateState = ref voiceUpdateStates[0].Span[0];
  521. for (int i = 0; i < primaryVoiceUpdateState.IsWaveBufferValid.Length; i++)
  522. {
  523. if (primaryVoiceUpdateState.IsWaveBufferValid[i])
  524. {
  525. return true;
  526. }
  527. }
  528. return false;
  529. case Types.PlayState.Stopping:
  530. for (int i = 0; i < WaveBuffers.Length; i++)
  531. {
  532. ref WaveBuffer wavebuffer = ref WaveBuffers[i];
  533. wavebuffer.IsSendToAudioProcessor = true;
  534. for (int j = 0; j < ChannelsCount; j++)
  535. {
  536. ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[j].Span[0];
  537. if (voiceUpdateState.IsWaveBufferValid[i])
  538. {
  539. voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount;
  540. voiceUpdateState.WaveBufferConsumed++;
  541. }
  542. voiceUpdateState.IsWaveBufferValid[i] = false;
  543. }
  544. }
  545. for (int i = 0; i < ChannelsCount; i++)
  546. {
  547. ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[i].Span[0];
  548. voiceUpdateState.Offset = 0;
  549. voiceUpdateState.PlayedSampleCount = 0;
  550. voiceUpdateState.Pitch.ToSpan().Fill(0);
  551. voiceUpdateState.Fraction = 0;
  552. voiceUpdateState.LoopContext = new Dsp.State.AdpcmLoopContext();
  553. }
  554. PlayState = Types.PlayState.Stopped;
  555. WasPlaying = PreviousPlayState == Types.PlayState.Started;
  556. return WasPlaying;
  557. case Types.PlayState.Stopped:
  558. case Types.PlayState.Paused:
  559. foreach (ref WaveBuffer wavebuffer in WaveBuffers.ToSpan())
  560. {
  561. wavebuffer.BufferAddressInfo.GetReference(true);
  562. wavebuffer.ContextAddressInfo.GetReference(true);
  563. }
  564. if (SampleFormat == SampleFormat.Adpcm)
  565. {
  566. if (DataSourceStateAddressInfo.CpuAddress != 0)
  567. {
  568. DataSourceStateAddressInfo.GetReference(true);
  569. }
  570. }
  571. WasPlaying = PreviousPlayState == Types.PlayState.Started;
  572. return WasPlaying;
  573. default:
  574. throw new NotImplementedException($"{PlayState}");
  575. }
  576. }
  577. /// <summary>
  578. /// Update the internal state for command generation.
  579. /// </summary>
  580. /// <param name="context">The voice context.</param>
  581. /// <returns>Return true if this voice should be played.</returns>
  582. public bool UpdateForCommandGeneration(VoiceContext context)
  583. {
  584. if (IsNew)
  585. {
  586. ResetResources(context);
  587. PreviousVolume = Volume;
  588. IsNew = false;
  589. }
  590. Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax];
  591. for (int i = 0; i < ChannelsCount; i++)
  592. {
  593. voiceUpdateStates[i] = context.GetUpdateStateForDsp(ChannelResourceIds[i]);
  594. }
  595. return UpdateParametersForCommandGeneration(voiceUpdateStates);
  596. }
  597. }
  598. }