AudioDeviceSession.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. using Ryujinx.Audio.Integration;
  2. using Ryujinx.Common;
  3. using System;
  4. using System.Diagnostics;
  5. namespace Ryujinx.Audio.Common
  6. {
  7. /// <summary>
  8. /// An audio device session.
  9. /// </summary>
  10. class AudioDeviceSession : IDisposable
  11. {
  12. /// <summary>
  13. /// The volume of the <see cref="AudioDeviceSession"/>.
  14. /// </summary>
  15. private float _volume;
  16. /// <summary>
  17. /// The state of the <see cref="AudioDeviceSession"/>.
  18. /// </summary>
  19. private AudioDeviceState _state;
  20. /// <summary>
  21. /// Array of all buffers currently used or released.
  22. /// </summary>
  23. private AudioBuffer[] _buffers;
  24. /// <summary>
  25. /// The server index inside <see cref="_buffers"/> (appended but not queued to device driver).
  26. /// </summary>
  27. private uint _serverBufferIndex;
  28. /// <summary>
  29. /// The hardware index inside <see cref="_buffers"/> (queued to device driver).
  30. /// </summary>
  31. private uint _hardwareBufferIndex;
  32. /// <summary>
  33. /// The released index inside <see cref="_buffers"/> (released by the device driver).
  34. /// </summary>
  35. private uint _releasedBufferIndex;
  36. /// <summary>
  37. /// The count of buffer appended (server side).
  38. /// </summary>
  39. private uint _bufferAppendedCount;
  40. /// <summary>
  41. /// The count of buffer registered (driver side).
  42. /// </summary>
  43. private uint _bufferRegisteredCount;
  44. /// <summary>
  45. /// The count of buffer released (released by the driver side).
  46. /// </summary>
  47. private uint _bufferReleasedCount;
  48. /// <summary>
  49. /// The released buffer event.
  50. /// </summary>
  51. private IWritableEvent _bufferEvent;
  52. /// <summary>
  53. /// The session on the device driver.
  54. /// </summary>
  55. private IHardwareDeviceSession _hardwareDeviceSession;
  56. /// <summary>
  57. /// Max number of buffers that can be registered to the device driver at a time.
  58. /// </summary>
  59. private uint _bufferRegisteredLimit;
  60. /// <summary>
  61. /// Create a new <see cref="AudioDeviceSession"/>.
  62. /// </summary>
  63. /// <param name="deviceSession">The device driver session associated</param>
  64. /// <param name="bufferEvent">The release buffer event</param>
  65. /// <param name="bufferRegisteredLimit">The max number of buffers that can be registered to the device driver at a time</param>
  66. public AudioDeviceSession(IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent, uint bufferRegisteredLimit = 4)
  67. {
  68. _bufferEvent = bufferEvent;
  69. _hardwareDeviceSession = deviceSession;
  70. _bufferRegisteredLimit = bufferRegisteredLimit;
  71. _buffers = new AudioBuffer[Constants.AudioDeviceBufferCountMax];
  72. _serverBufferIndex = 0;
  73. _hardwareBufferIndex = 0;
  74. _releasedBufferIndex = 0;
  75. _bufferAppendedCount = 0;
  76. _bufferRegisteredCount = 0;
  77. _bufferReleasedCount = 0;
  78. _volume = deviceSession.GetVolume();
  79. _state = AudioDeviceState.Stopped;
  80. }
  81. /// <summary>
  82. /// Get the released buffer event.
  83. /// </summary>
  84. /// <returns>The released buffer event</returns>
  85. public IWritableEvent GetBufferEvent()
  86. {
  87. return _bufferEvent;
  88. }
  89. /// <summary>
  90. /// Get the state of the session.
  91. /// </summary>
  92. /// <returns>The state of the session</returns>
  93. public AudioDeviceState GetState()
  94. {
  95. Debug.Assert(_state == AudioDeviceState.Started || _state == AudioDeviceState.Stopped);
  96. return _state;
  97. }
  98. /// <summary>
  99. /// Get the total buffer count (server + driver + released).
  100. /// </summary>
  101. /// <returns>Return the total buffer count</returns>
  102. private uint GetTotalBufferCount()
  103. {
  104. uint bufferCount = _bufferAppendedCount + _bufferRegisteredCount + _bufferReleasedCount;
  105. Debug.Assert(bufferCount <= Constants.AudioDeviceBufferCountMax);
  106. return bufferCount;
  107. }
  108. /// <summary>
  109. /// Register a new <see cref="AudioBuffer"/> on the server side.
  110. /// </summary>
  111. /// <param name="buffer">The <see cref="AudioBuffer"/> to register</param>
  112. /// <returns>True if the operation succeeded</returns>
  113. private bool RegisterBuffer(AudioBuffer buffer)
  114. {
  115. if (GetTotalBufferCount() == Constants.AudioDeviceBufferCountMax)
  116. {
  117. return false;
  118. }
  119. _buffers[_serverBufferIndex] = buffer;
  120. _serverBufferIndex = (_serverBufferIndex + 1) % Constants.AudioDeviceBufferCountMax;
  121. _bufferAppendedCount++;
  122. return true;
  123. }
  124. /// <summary>
  125. /// Flush server buffers to hardware.
  126. /// </summary>
  127. private void FlushToHardware()
  128. {
  129. uint bufferToFlushCount = Math.Min(Math.Min(_bufferAppendedCount, 4), _bufferRegisteredLimit - _bufferRegisteredCount);
  130. AudioBuffer[] buffersToFlush = new AudioBuffer[bufferToFlushCount];
  131. uint hardwareBufferIndex = _hardwareBufferIndex;
  132. for (int i = 0; i < buffersToFlush.Length; i++)
  133. {
  134. buffersToFlush[i] = _buffers[hardwareBufferIndex];
  135. _bufferAppendedCount--;
  136. _bufferRegisteredCount++;
  137. hardwareBufferIndex = (hardwareBufferIndex + 1) % Constants.AudioDeviceBufferCountMax;
  138. }
  139. _hardwareBufferIndex = hardwareBufferIndex;
  140. for (int i = 0; i < buffersToFlush.Length; i++)
  141. {
  142. _hardwareDeviceSession.QueueBuffer(buffersToFlush[i]);
  143. }
  144. }
  145. /// <summary>
  146. /// Get the current index of the <see cref="AudioBuffer"/> playing on the driver side.
  147. /// </summary>
  148. /// <param name="playingIndex">The output index of the <see cref="AudioBuffer"/> playing on the driver side</param>
  149. /// <returns>True if any buffer is playing</returns>
  150. private bool TryGetPlayingBufferIndex(out uint playingIndex)
  151. {
  152. if (_bufferRegisteredCount > 0)
  153. {
  154. playingIndex = (_hardwareBufferIndex - _bufferRegisteredCount) % Constants.AudioDeviceBufferCountMax;
  155. return true;
  156. }
  157. playingIndex = 0;
  158. return false;
  159. }
  160. /// <summary>
  161. /// Try to pop the <see cref="AudioBuffer"/> playing on the driver side.
  162. /// </summary>
  163. /// <param name="buffer">The output <see cref="AudioBuffer"/> playing on the driver side</param>
  164. /// <returns>True if any buffer is playing</returns>
  165. private bool TryPopPlayingBuffer(out AudioBuffer buffer)
  166. {
  167. if (_bufferRegisteredCount > 0)
  168. {
  169. uint bufferIndex = (_hardwareBufferIndex - _bufferRegisteredCount) % Constants.AudioDeviceBufferCountMax;
  170. buffer = _buffers[bufferIndex];
  171. _buffers[bufferIndex] = null;
  172. _bufferRegisteredCount--;
  173. return true;
  174. }
  175. buffer = null;
  176. return false;
  177. }
  178. /// <summary>
  179. /// Try to pop a <see cref="AudioBuffer"/> released by the driver side.
  180. /// </summary>
  181. /// <param name="buffer">The output <see cref="AudioBuffer"/> released by the driver side</param>
  182. /// <returns>True if any buffer has been released</returns>
  183. public bool TryPopReleasedBuffer(out AudioBuffer buffer)
  184. {
  185. if (_bufferReleasedCount > 0)
  186. {
  187. uint bufferIndex = (_releasedBufferIndex - _bufferReleasedCount) % Constants.AudioDeviceBufferCountMax;
  188. buffer = _buffers[bufferIndex];
  189. _buffers[bufferIndex] = null;
  190. _bufferReleasedCount--;
  191. return true;
  192. }
  193. buffer = null;
  194. return false;
  195. }
  196. /// <summary>
  197. /// Release a <see cref="AudioBuffer"/>.
  198. /// </summary>
  199. /// <param name="buffer">The <see cref="AudioBuffer"/> to release</param>
  200. private void ReleaseBuffer(AudioBuffer buffer)
  201. {
  202. buffer.PlayedTimestamp = (ulong)PerformanceCounter.ElapsedNanoseconds;
  203. _bufferRegisteredCount--;
  204. _bufferReleasedCount++;
  205. _releasedBufferIndex = (_releasedBufferIndex + 1) % Constants.AudioDeviceBufferCountMax;
  206. }
  207. /// <summary>
  208. /// Update the released buffers.
  209. /// </summary>
  210. /// <param name="updateForStop">True if the session is currently stopping</param>
  211. private void UpdateReleaseBuffers(bool updateForStop = false)
  212. {
  213. bool wasAnyBuffersReleased = false;
  214. while (TryGetPlayingBufferIndex(out uint playingIndex))
  215. {
  216. if (!updateForStop && !_hardwareDeviceSession.WasBufferFullyConsumed(_buffers[playingIndex]))
  217. {
  218. break;
  219. }
  220. if (updateForStop)
  221. {
  222. _hardwareDeviceSession.UnregisterBuffer(_buffers[playingIndex]);
  223. }
  224. ReleaseBuffer(_buffers[playingIndex]);
  225. wasAnyBuffersReleased = true;
  226. }
  227. if (wasAnyBuffersReleased)
  228. {
  229. _bufferEvent.Signal();
  230. }
  231. }
  232. /// <summary>
  233. /// Append a new <see cref="AudioBuffer"/>.
  234. /// </summary>
  235. /// <param name="buffer">The <see cref="AudioBuffer"/> to append</param>
  236. /// <returns>True if the buffer was appended</returns>
  237. public bool AppendBuffer(AudioBuffer buffer)
  238. {
  239. if (_hardwareDeviceSession.RegisterBuffer(buffer))
  240. {
  241. if (RegisterBuffer(buffer))
  242. {
  243. FlushToHardware();
  244. return true;
  245. }
  246. _hardwareDeviceSession.UnregisterBuffer(buffer);
  247. }
  248. return false;
  249. }
  250. public bool AppendUacBuffer(AudioBuffer buffer, uint handle)
  251. {
  252. // NOTE: On hardware, there is another RegisterBuffer method taking an handle.
  253. // This variant of the call always return false (stubbed?) as a result this logic will never succeed.
  254. return false;
  255. }
  256. /// <summary>
  257. /// Start the audio session.
  258. /// </summary>
  259. /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
  260. public ResultCode Start()
  261. {
  262. if (_state == AudioDeviceState.Started)
  263. {
  264. return ResultCode.OperationFailed;
  265. }
  266. _hardwareDeviceSession.Start();
  267. _state = AudioDeviceState.Started;
  268. FlushToHardware();
  269. _hardwareDeviceSession.SetVolume(_volume);
  270. return ResultCode.Success;
  271. }
  272. /// <summary>
  273. /// Stop the audio session.
  274. /// </summary>
  275. /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
  276. public ResultCode Stop()
  277. {
  278. if (_state == AudioDeviceState.Started)
  279. {
  280. _hardwareDeviceSession.Stop();
  281. UpdateReleaseBuffers(true);
  282. _state = AudioDeviceState.Stopped;
  283. }
  284. return ResultCode.Success;
  285. }
  286. /// <summary>
  287. /// Get the volume of the session.
  288. /// </summary>
  289. /// <returns>The volume of the session</returns>
  290. public float GetVolume()
  291. {
  292. return _hardwareDeviceSession.GetVolume();
  293. }
  294. /// <summary>
  295. /// Set the volume of the session.
  296. /// </summary>
  297. /// <param name="volume">The new volume to set</param>
  298. public void SetVolume(float volume)
  299. {
  300. _volume = volume;
  301. if (_state == AudioDeviceState.Started)
  302. {
  303. _hardwareDeviceSession.SetVolume(volume);
  304. }
  305. }
  306. /// <summary>
  307. /// Get the count of buffer currently in use (server + driver side).
  308. /// </summary>
  309. /// <returns>The count of buffer currently in use</returns>
  310. public uint GetBufferCount()
  311. {
  312. return _bufferAppendedCount + _bufferRegisteredCount;
  313. }
  314. /// <summary>
  315. /// Check if a buffer is present.
  316. /// </summary>
  317. /// <param name="bufferTag">The unique tag of the buffer</param>
  318. /// <returns>Return true if a buffer is present</returns>
  319. public bool ContainsBuffer(ulong bufferTag)
  320. {
  321. uint bufferIndex = (_releasedBufferIndex - _bufferReleasedCount) % Constants.AudioDeviceBufferCountMax;
  322. for (int i = 0; i < GetTotalBufferCount(); i++)
  323. {
  324. if (_buffers[bufferIndex].BufferTag == bufferTag)
  325. {
  326. return true;
  327. }
  328. bufferIndex = (bufferIndex + 1) % Constants.AudioDeviceBufferCountMax;
  329. }
  330. return false;
  331. }
  332. /// <summary>
  333. /// Get the count of sample played in this session.
  334. /// </summary>
  335. /// <returns>The count of sample played in this session</returns>
  336. public ulong GetPlayedSampleCount()
  337. {
  338. if (_state == AudioDeviceState.Stopped)
  339. {
  340. return 0;
  341. }
  342. else
  343. {
  344. return _hardwareDeviceSession.GetPlayedSampleCount();
  345. }
  346. }
  347. /// <summary>
  348. /// Flush all buffers to the initial state.
  349. /// </summary>
  350. /// <returns>True if any buffer was flushed</returns>
  351. public bool FlushBuffers()
  352. {
  353. if (_state == AudioDeviceState.Stopped)
  354. {
  355. return false;
  356. }
  357. uint bufferCount = GetBufferCount();
  358. while (TryPopReleasedBuffer(out AudioBuffer buffer))
  359. {
  360. _hardwareDeviceSession.UnregisterBuffer(buffer);
  361. }
  362. while (TryPopPlayingBuffer(out AudioBuffer buffer))
  363. {
  364. _hardwareDeviceSession.UnregisterBuffer(buffer);
  365. }
  366. if (_bufferRegisteredCount == 0 || (_bufferReleasedCount + _bufferAppendedCount) > Constants.AudioDeviceBufferCountMax)
  367. {
  368. return false;
  369. }
  370. _bufferReleasedCount += _bufferAppendedCount;
  371. _releasedBufferIndex = (_releasedBufferIndex + _bufferAppendedCount) % Constants.AudioDeviceBufferCountMax;
  372. _bufferAppendedCount = 0;
  373. _hardwareBufferIndex = _serverBufferIndex;
  374. if (bufferCount > 0)
  375. {
  376. _bufferEvent.Signal();
  377. }
  378. return true;
  379. }
  380. /// <summary>
  381. /// Update the session.
  382. /// </summary>
  383. public void Update()
  384. {
  385. if (_state == AudioDeviceState.Started)
  386. {
  387. UpdateReleaseBuffers();
  388. FlushToHardware();
  389. }
  390. }
  391. public void Dispose()
  392. {
  393. Dispose(true);
  394. }
  395. protected virtual void Dispose(bool disposing)
  396. {
  397. if (disposing)
  398. {
  399. // Tell the hardware session that we are ending.
  400. _hardwareDeviceSession.PrepareToClose();
  401. // Unregister all buffers
  402. while (TryPopReleasedBuffer(out AudioBuffer buffer))
  403. {
  404. _hardwareDeviceSession.UnregisterBuffer(buffer);
  405. }
  406. while (TryPopPlayingBuffer(out AudioBuffer buffer))
  407. {
  408. _hardwareDeviceSession.UnregisterBuffer(buffer);
  409. }
  410. // Finally dispose hardware session.
  411. _hardwareDeviceSession.Dispose();
  412. _bufferEvent.Signal();
  413. }
  414. }
  415. }
  416. }