You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

VoiceBuffer.cs 3.6 kB

10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using System;
  2. using System.Threading;
  3. namespace Discord.Audio
  4. {
  5. internal class VoiceBuffer
  6. {
  7. private readonly int _frameSize, _frameCount, _bufferSize;
  8. private readonly byte[] _buffer;
  9. private readonly byte[] _blankFrame;
  10. private ushort _readCursor, _writeCursor;
  11. private ManualResetEventSlim _notOverflowEvent;
  12. private bool _isClearing;
  13. public int FrameSize => _frameSize;
  14. public int FrameCount => _frameCount;
  15. public ushort ReadPos => _readCursor;
  16. public ushort WritePos => _readCursor;
  17. public VoiceBuffer(int frameCount, int frameSize)
  18. {
  19. _frameSize = frameSize;
  20. _frameCount = frameCount;
  21. _bufferSize = _frameSize * _frameCount;
  22. _readCursor = 0;
  23. _writeCursor = 0;
  24. _buffer = new byte[_bufferSize];
  25. _blankFrame = new byte[_frameSize];
  26. _notOverflowEvent = new ManualResetEventSlim(); //Notifies when an overflow is solved
  27. }
  28. public void Push(byte[] buffer, int bytes, CancellationToken cancelToken)
  29. {
  30. if (cancelToken.IsCancellationRequested)
  31. throw new OperationCanceledException("Client is disconnected.", cancelToken);
  32. int wholeFrames = bytes / _frameSize;
  33. int expectedBytes = wholeFrames * _frameSize;
  34. int lastFrameSize = bytes - expectedBytes;
  35. lock (this)
  36. {
  37. for (int i = 0, pos = 0; i <= wholeFrames; i++, pos += _frameSize)
  38. {
  39. //If the read cursor is in the next position, wait for it to move.
  40. ushort nextPosition = _writeCursor;
  41. AdvanceCursorPos(ref nextPosition);
  42. if (_readCursor == nextPosition)
  43. {
  44. _notOverflowEvent.Reset();
  45. try
  46. {
  47. _notOverflowEvent.Wait(cancelToken);
  48. }
  49. catch (OperationCanceledException ex)
  50. {
  51. throw new OperationCanceledException("Client is disconnected.", ex, cancelToken);
  52. }
  53. }
  54. if (i == wholeFrames)
  55. {
  56. //If there are no partial frames, skip this step
  57. if (lastFrameSize == 0)
  58. break;
  59. //Copy partial frame
  60. Buffer.BlockCopy(buffer, pos, _buffer, _writeCursor * _frameSize, lastFrameSize);
  61. //Wipe the end of the buffer
  62. Buffer.BlockCopy(_blankFrame, 0, _buffer, _writeCursor * _frameSize + lastFrameSize, _frameSize - lastFrameSize);
  63. }
  64. else
  65. {
  66. //Copy full frame
  67. Buffer.BlockCopy(buffer, pos, _buffer, _writeCursor * _frameSize, _frameSize);
  68. }
  69. //Advance the write cursor to the next position
  70. AdvanceCursorPos(ref _writeCursor);
  71. }
  72. }
  73. }
  74. public bool Pop(byte[] buffer)
  75. {
  76. if (_writeCursor == _readCursor)
  77. {
  78. _notOverflowEvent.Set();
  79. return false;
  80. }
  81. bool isClearing = _isClearing;
  82. if (!isClearing)
  83. Buffer.BlockCopy(_buffer, _readCursor * _frameSize, buffer, 0, _frameSize);
  84. //Advance the read cursor to the next position
  85. AdvanceCursorPos(ref _readCursor);
  86. _notOverflowEvent.Set();
  87. return !isClearing;
  88. }
  89. public void Clear(CancellationToken cancelToken)
  90. {
  91. lock (this)
  92. {
  93. _isClearing = true;
  94. for (int i = 0; i < _frameCount; i++)
  95. Buffer.BlockCopy(_blankFrame, 0, _buffer, i * _frameCount, i++);
  96. try
  97. {
  98. Wait(cancelToken);
  99. }
  100. catch (OperationCanceledException) { }
  101. _writeCursor = 0;
  102. _readCursor = 0;
  103. _isClearing = false;
  104. }
  105. }
  106. public void Wait(CancellationToken cancelToken)
  107. {
  108. while (true)
  109. {
  110. _notOverflowEvent.Wait(cancelToken);
  111. if (_writeCursor == _readCursor)
  112. break;
  113. }
  114. }
  115. private void AdvanceCursorPos(ref ushort pos)
  116. {
  117. pos++;
  118. if (pos == _frameCount)
  119. pos = 0;
  120. }
  121. }
  122. }