OpenALAudioTrack.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using OpenTK.Audio.OpenAL;
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. namespace Ryujinx.Audio
  6. {
  7. internal class OpenALAudioTrack : IDisposable
  8. {
  9. public int SourceId { get; private set; }
  10. public int SampleRate { get; private set; }
  11. public ALFormat Format { get; private set; }
  12. public PlaybackState State { get; set; }
  13. public float Volume { get; private set; }
  14. public int HardwareChannels { get; }
  15. public int VirtualChannels { get; }
  16. public uint BufferCount => (uint)_buffers.Count;
  17. public ulong PlayedSampleCount { get; set; }
  18. private ReleaseCallback _callback;
  19. private ConcurrentDictionary<long, int> _buffers;
  20. private Queue<long> _queuedTagsQueue;
  21. private Queue<long> _releasedTagsQueue;
  22. private bool _disposed;
  23. public OpenALAudioTrack(int sampleRate, ALFormat format, int hardwareChannels, int virtualChannels, ReleaseCallback callback)
  24. {
  25. SampleRate = sampleRate;
  26. Format = format;
  27. State = PlaybackState.Stopped;
  28. SourceId = AL.GenSource();
  29. HardwareChannels = hardwareChannels;
  30. VirtualChannels = virtualChannels;
  31. _callback = callback;
  32. _buffers = new ConcurrentDictionary<long, int>();
  33. _queuedTagsQueue = new Queue<long>();
  34. _releasedTagsQueue = new Queue<long>();
  35. }
  36. public bool ContainsBuffer(long tag)
  37. {
  38. foreach (long queuedTag in _queuedTagsQueue)
  39. {
  40. if (queuedTag == tag)
  41. {
  42. return true;
  43. }
  44. }
  45. return false;
  46. }
  47. public long[] GetReleasedBuffers(int count)
  48. {
  49. AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int releasedCount);
  50. releasedCount += _releasedTagsQueue.Count;
  51. if (count > releasedCount)
  52. {
  53. count = releasedCount;
  54. }
  55. List<long> tags = new List<long>();
  56. while (count-- > 0 && _releasedTagsQueue.TryDequeue(out long tag))
  57. {
  58. tags.Add(tag);
  59. }
  60. while (count-- > 0 && _queuedTagsQueue.TryDequeue(out long tag))
  61. {
  62. AL.SourceUnqueueBuffers(SourceId, 1);
  63. tags.Add(tag);
  64. }
  65. return tags.ToArray();
  66. }
  67. public int AppendBuffer(long tag)
  68. {
  69. if (_disposed)
  70. {
  71. throw new ObjectDisposedException(GetType().Name);
  72. }
  73. int id = AL.GenBuffer();
  74. _buffers.AddOrUpdate(tag, id, (key, oldId) =>
  75. {
  76. AL.DeleteBuffer(oldId);
  77. return id;
  78. });
  79. _queuedTagsQueue.Enqueue(tag);
  80. return id;
  81. }
  82. public void CallReleaseCallbackIfNeeded()
  83. {
  84. AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int releasedCount);
  85. if (releasedCount > 0)
  86. {
  87. // If we signal, then we also need to have released buffers available
  88. // to return when GetReleasedBuffers is called.
  89. // If playback needs to be re-started due to all buffers being processed,
  90. // then OpenAL zeros the counts (ReleasedCount), so we keep it on the queue.
  91. while (releasedCount-- > 0 && _queuedTagsQueue.TryDequeue(out long tag))
  92. {
  93. AL.SourceUnqueueBuffers(SourceId, 1);
  94. _releasedTagsQueue.Enqueue(tag);
  95. }
  96. _callback();
  97. }
  98. }
  99. public bool FlushBuffers()
  100. {
  101. while (_queuedTagsQueue.TryDequeue(out long tag))
  102. {
  103. _releasedTagsQueue.Enqueue(tag);
  104. }
  105. _callback();
  106. foreach (var buffer in _buffers)
  107. {
  108. AL.DeleteBuffer(buffer.Value);
  109. }
  110. bool heldBuffers = _buffers.Count > 0;
  111. _buffers.Clear();
  112. return heldBuffers;
  113. }
  114. public void SetVolume(float volume)
  115. {
  116. Volume = volume;
  117. AL.Source(SourceId, ALSourcef.Gain, Volume);
  118. }
  119. public void Dispose()
  120. {
  121. Dispose(true);
  122. }
  123. protected virtual void Dispose(bool disposing)
  124. {
  125. if (disposing && !_disposed)
  126. {
  127. _disposed = true;
  128. AL.DeleteSource(SourceId);
  129. foreach (int id in _buffers.Values)
  130. {
  131. AL.DeleteBuffer(id);
  132. }
  133. }
  134. }
  135. }
  136. }