MixState.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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.Renderer.Common;
  18. using Ryujinx.Audio.Renderer.Parameter;
  19. using Ryujinx.Audio.Renderer.Server.Effect;
  20. using Ryujinx.Audio.Renderer.Server.Splitter;
  21. using Ryujinx.Common.Utilities;
  22. using System;
  23. using System.Diagnostics;
  24. using System.Runtime.CompilerServices;
  25. using System.Runtime.InteropServices;
  26. using static Ryujinx.Audio.Renderer.RendererConstants;
  27. namespace Ryujinx.Audio.Renderer.Server.Mix
  28. {
  29. /// <summary>
  30. /// Server state for a mix.
  31. /// </summary>
  32. [StructLayout(LayoutKind.Sequential, Size = 0x940, Pack = Alignment)]
  33. public struct MixState
  34. {
  35. public const uint InvalidDistanceFromFinalMix = 0x80000000;
  36. public const int Alignment = 0x10;
  37. /// <summary>
  38. /// Base volume of the mix.
  39. /// </summary>
  40. public float Volume;
  41. /// <summary>
  42. /// Target sample rate of the mix.
  43. /// </summary>
  44. public uint SampleRate;
  45. /// <summary>
  46. /// Target buffer count.
  47. /// </summary>
  48. public uint BufferCount;
  49. /// <summary>
  50. /// Set to true if in use.
  51. /// </summary>
  52. [MarshalAs(UnmanagedType.I1)]
  53. public bool IsUsed;
  54. /// <summary>
  55. /// The id of the mix.
  56. /// </summary>
  57. public int MixId;
  58. /// <summary>
  59. /// The mix node id.
  60. /// </summary>
  61. public int NodeId;
  62. /// <summary>
  63. /// the buffer offset to use for command generation.
  64. /// </summary>
  65. public uint BufferOffset;
  66. /// <summary>
  67. /// The distance of the mix from the final mix.
  68. /// </summary>
  69. public uint DistanceFromFinalMix;
  70. /// <summary>
  71. /// The effect processing order storage.
  72. /// </summary>
  73. private IntPtr _effectProcessingOrderArrayPointer;
  74. /// <summary>
  75. /// The max element count that can be found in the effect processing order storage.
  76. /// </summary>
  77. public uint EffectProcessingOrderArrayMaxCount;
  78. /// <summary>
  79. /// The mix to output the result of this mix.
  80. /// </summary>
  81. public int DestinationMixId;
  82. /// <summary>
  83. /// Mix buffer volumes storage.
  84. /// </summary>
  85. private MixVolumeArray _mixVolumeArray;
  86. /// <summary>
  87. /// The splitter to output the result of this mix.
  88. /// </summary>
  89. public uint DestinationSplitterId;
  90. /// <summary>
  91. /// If set to true, the long size pre-delay is supported on the reverb command.
  92. /// </summary>
  93. [MarshalAs(UnmanagedType.I1)]
  94. public bool IsLongSizePreDelaySupported;
  95. [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)]
  96. private struct MixVolumeArray
  97. {
  98. private const int Size = 4 * MixBufferCountMax * MixBufferCountMax;
  99. }
  100. /// <summary>
  101. /// Mix buffer volumes.
  102. /// </summary>
  103. /// <remarks>Used when no splitter id is specified.</remarks>
  104. public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixVolumeArray, float>(ref _mixVolumeArray);
  105. /// <summary>
  106. /// Get the volume for a given connection destination.
  107. /// </summary>
  108. /// <param name="sourceIndex">The source node index.</param>
  109. /// <param name="destinationIndex">The destination node index</param>
  110. /// <returns>The volume for the given connection destination.</returns>
  111. public float GetMixBufferVolume(int sourceIndex, int destinationIndex)
  112. {
  113. return MixBufferVolume[sourceIndex * MixBufferCountMax + destinationIndex];
  114. }
  115. /// <summary>
  116. /// The array used to order effects associated to this mix.
  117. /// </summary>
  118. public Span<int> EffectProcessingOrderArray
  119. {
  120. get
  121. {
  122. if (_effectProcessingOrderArrayPointer == IntPtr.Zero)
  123. {
  124. return Span<int>.Empty;
  125. }
  126. unsafe
  127. {
  128. return new Span<int>((void*)_effectProcessingOrderArrayPointer, (int)EffectProcessingOrderArrayMaxCount);
  129. }
  130. }
  131. }
  132. /// <summary>
  133. /// Create a new <see cref="MixState"/>
  134. /// </summary>
  135. /// <param name="effectProcessingOrderArray"></param>
  136. /// <param name="behaviourContext"></param>
  137. public MixState(Memory<int> effectProcessingOrderArray, ref BehaviourContext behaviourContext) : this()
  138. {
  139. MixId = UnusedMixId;
  140. DistanceFromFinalMix = InvalidDistanceFromFinalMix;
  141. DestinationMixId = UnusedMixId;
  142. DestinationSplitterId = UnusedSplitterId;
  143. unsafe
  144. {
  145. // SAFETY: safe as effectProcessingOrderArray comes from the work buffer memory that is pinned.
  146. _effectProcessingOrderArrayPointer = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetReference(effectProcessingOrderArray.Span));
  147. }
  148. EffectProcessingOrderArrayMaxCount = (uint)effectProcessingOrderArray.Length;
  149. IsLongSizePreDelaySupported = behaviourContext.IsLongSizePreDelaySupported();
  150. ClearEffectProcessingOrder();
  151. }
  152. /// <summary>
  153. /// Clear the <see cref="DistanceFromFinalMix"/> value to its default state.
  154. /// </summary>
  155. public void ClearDistanceFromFinalMix()
  156. {
  157. DistanceFromFinalMix = InvalidDistanceFromFinalMix;
  158. }
  159. /// <summary>
  160. /// Clear the <see cref="EffectProcessingOrderArray"/> to its default state.
  161. /// </summary>
  162. public void ClearEffectProcessingOrder()
  163. {
  164. EffectProcessingOrderArray.Fill(-1);
  165. }
  166. /// <summary>
  167. /// Return true if the mix has any destinations.
  168. /// </summary>
  169. /// <returns>True if the mix has any destinations.</returns>
  170. public bool HasAnyDestination()
  171. {
  172. return DestinationMixId != UnusedMixId || DestinationSplitterId != UnusedSplitterId;
  173. }
  174. /// <summary>
  175. /// Update the mix connection on the adjacency matrix.
  176. /// </summary>
  177. /// <param name="edgeMatrix">The adjacency matrix.</param>
  178. /// <param name="parameter">The input parameter of the mix.</param>
  179. /// <param name="splitterContext">The splitter context.</param>
  180. /// <returns>Return true, new connections were done on the adjacency matrix.</returns>
  181. private bool UpdateConnection(EdgeMatrix edgeMatrix, ref MixParameter parameter, ref SplitterContext splitterContext)
  182. {
  183. bool hasNewConnections;
  184. if (DestinationSplitterId == UnusedSplitterId)
  185. {
  186. hasNewConnections = false;
  187. }
  188. else
  189. {
  190. ref SplitterState splitter = ref splitterContext.GetState((int)DestinationSplitterId);
  191. hasNewConnections = splitter.HasNewConnection;
  192. }
  193. if (DestinationMixId == parameter.DestinationMixId && DestinationSplitterId == parameter.DestinationSplitterId && !hasNewConnections)
  194. {
  195. return false;
  196. }
  197. edgeMatrix.RemoveEdges(MixId);
  198. if (parameter.DestinationMixId == UnusedMixId)
  199. {
  200. if (parameter.DestinationSplitterId != UnusedSplitterId)
  201. {
  202. ref SplitterState splitter = ref splitterContext.GetState((int)parameter.DestinationSplitterId);
  203. for (int i = 0; i < splitter.DestinationCount; i++)
  204. {
  205. Span<SplitterDestination> destination = splitter.GetData(i);
  206. if (!destination.IsEmpty)
  207. {
  208. int destinationMixId = destination[0].DestinationId;
  209. if (destinationMixId != UnusedMixId)
  210. {
  211. edgeMatrix.Connect(MixId, destinationMixId);
  212. }
  213. }
  214. }
  215. }
  216. }
  217. else
  218. {
  219. edgeMatrix.Connect(MixId, parameter.DestinationMixId);
  220. }
  221. DestinationMixId = parameter.DestinationMixId;
  222. DestinationSplitterId = parameter.DestinationSplitterId;
  223. return true;
  224. }
  225. /// <summary>
  226. /// Update the mix from user information.
  227. /// </summary>
  228. /// <param name="edgeMatrix">The adjacency matrix.</param>
  229. /// <param name="parameter">The input parameter of the mix.</param>
  230. /// <param name="effectContext">The effect context.</param>
  231. /// <param name="splitterContext">The splitter context.</param>
  232. /// <param name="behaviourContext">The behaviour context.</param>
  233. /// <returns>Return true if the mix was changed.</returns>
  234. public bool Update(EdgeMatrix edgeMatrix, ref MixParameter parameter, EffectContext effectContext, SplitterContext splitterContext, BehaviourContext behaviourContext)
  235. {
  236. bool isDirty;
  237. Volume = parameter.Volume;
  238. SampleRate = parameter.SampleRate;
  239. BufferCount = parameter.BufferCount;
  240. IsUsed = parameter.IsUsed;
  241. MixId = parameter.MixId;
  242. NodeId = parameter.NodeId;
  243. parameter.MixBufferVolume.CopyTo(MixBufferVolume);
  244. if (behaviourContext.IsSplitterSupported())
  245. {
  246. isDirty = UpdateConnection(edgeMatrix, ref parameter, ref splitterContext);
  247. }
  248. else
  249. {
  250. isDirty = DestinationMixId != parameter.DestinationMixId;
  251. if (DestinationMixId != parameter.DestinationMixId)
  252. {
  253. DestinationMixId = parameter.DestinationMixId;
  254. }
  255. DestinationSplitterId = UnusedSplitterId;
  256. }
  257. ClearEffectProcessingOrder();
  258. for (int i = 0; i < effectContext.GetCount(); i++)
  259. {
  260. ref BaseEffect effect = ref effectContext.GetEffect(i);
  261. if (effect.MixId == MixId)
  262. {
  263. Debug.Assert(effect.ProcessingOrder <= EffectProcessingOrderArrayMaxCount);
  264. if (effect.ProcessingOrder > EffectProcessingOrderArrayMaxCount)
  265. {
  266. return isDirty;
  267. }
  268. EffectProcessingOrderArray[(int)effect.ProcessingOrder] = i;
  269. }
  270. }
  271. return isDirty;
  272. }
  273. }
  274. }