VoiceState.cs 27 KB

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