OpenALAudioTrack.cs 4.8 KB

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