diff --git a/src/Discord.Net.Core/Audio/AudioOutStream.cs b/src/Discord.Net.Core/Audio/AudioOutStream.cs
new file mode 100644
index 000000000..dd91b71ee
--- /dev/null
+++ b/src/Discord.Net.Core/Audio/AudioOutStream.cs
@@ -0,0 +1,16 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Discord.Audio
+{
+ public abstract class AudioOutStream : Stream
+ {
+ public override bool CanRead => false;
+ public override bool CanSeek => false;
+ public override bool CanWrite => true;
+
+ public virtual void Clear() { }
+ public virtual Task ClearAsync(CancellationToken cancelToken) { return Task.Delay(0); }
+ }
+}
diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs
index 38e25d7b5..dfc6f60bd 100644
--- a/src/Discord.Net.Core/Audio/IAudioClient.cs
+++ b/src/Discord.Net.Core/Audio/IAudioClient.cs
@@ -22,26 +22,26 @@ namespace Discord.Audio
///
/// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively.
///
- Stream CreateOpusStream(int samplesPerFrame, int bufferMillis = 1000);
+ AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis = 1000);
///
/// Creates a new outgoing stream accepting Opus-encoded data. This is a direct stream with no internal timer.
///
/// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively.
///
- Stream CreateDirectOpusStream(int samplesPerFrame);
+ AudioOutStream CreateDirectOpusStream(int samplesPerFrame);
///
/// Creates a new outgoing stream accepting PCM (raw) data.
///
/// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively.
///
///
- Stream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null, int bufferMillis = 1000);
+ AudioOutStream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null, int bufferMillis = 1000);
///
/// Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer.
///
/// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively.
///
///
- Stream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null);
+ AudioOutStream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null);
}
}
diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs
index dd01aa2f9..784cdf194 100644
--- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs
+++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs
@@ -170,25 +170,25 @@ namespace Discord.Audio
await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false);
}
- public Stream CreateOpusStream(int samplesPerFrame, int bufferMillis)
+ public AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis)
{
CheckSamplesPerFrame(samplesPerFrame);
var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token);
return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc);
}
- public Stream CreateDirectOpusStream(int samplesPerFrame)
+ public AudioOutStream CreateDirectOpusStream(int samplesPerFrame)
{
CheckSamplesPerFrame(samplesPerFrame);
var target = new DirectAudioTarget(ApiClient);
return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc);
}
- public Stream CreatePCMStream(int samplesPerFrame, int channels, int? bitrate, int bufferMillis)
+ public AudioOutStream CreatePCMStream(int samplesPerFrame, int channels, int? bitrate, int bufferMillis)
{
CheckSamplesPerFrame(samplesPerFrame);
var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token);
return new OpusEncodeStream(target, _secretKey, channels, samplesPerFrame, _ssrc, bitrate);
}
- public Stream CreateDirectPCMStream(int samplesPerFrame, int channels, int? bitrate)
+ public AudioOutStream CreateDirectPCMStream(int samplesPerFrame, int channels, int? bitrate)
{
CheckSamplesPerFrame(samplesPerFrame);
var target = new DirectAudioTarget(ApiClient);
diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs
index 300cd194c..7ba95c591 100644
--- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs
+++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace Discord.Audio
{
- internal class RTPWriteStream : Stream
+ internal class RTPWriteStream : AudioOutStream
{
private readonly IAudioTarget _target;
private readonly byte[] _nonce, _secretKey;
@@ -14,10 +14,6 @@ namespace Discord.Audio
protected readonly byte[] _buffer;
- public override bool CanRead => false;
- public override bool CanSeek => false;
- public override bool CanWrite => true;
-
internal RTPWriteStream(IAudioTarget target, byte[] secretKey, int samplesPerFrame, uint ssrc)
{
_target = target;
@@ -40,6 +36,7 @@ namespace Discord.Audio
}
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
+ cancellationToken.ThrowIfCancellationRequested();
unchecked
{
if (_nonce[3]++ == byte.MaxValue)
@@ -63,7 +60,16 @@ namespace Discord.Audio
}
public override async Task FlushAsync(CancellationToken cancellationToken)
{
- await _target.FlushAsync().ConfigureAwait(false);
+ await _target.FlushAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ public override void Clear()
+ {
+ ClearAsync(CancellationToken.None).GetAwaiter().GetResult();
+ }
+ public override async Task ClearAsync(CancellationToken cancelToken)
+ {
+ await _target.ClearAsync(cancelToken).ConfigureAwait(false);
}
public override long Length { get { throw new NotSupportedException(); } }
diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs
index 65ab537e0..b27c5c8b3 100644
--- a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs
+++ b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs
@@ -87,17 +87,25 @@ namespace Discord.Audio
Buffer.BlockCopy(data, 0, buffer, 0, count);
_queuedFrames.Enqueue(new Frame(buffer, count));
}
-
- public async Task FlushAsync()
+
+ public async Task FlushAsync(CancellationToken cancelToken)
{
while (true)
{
+ cancelToken.ThrowIfCancellationRequested();
if (_queuedFrames.Count == 0)
return;
- await Task.Delay(250).ConfigureAwait(false);
+ await Task.Delay(250, cancelToken).ConfigureAwait(false);
}
}
-
+ public Task ClearAsync(CancellationToken cancelToken)
+ {
+ Frame ignored;
+ do
+ cancelToken.ThrowIfCancellationRequested();
+ while (_queuedFrames.TryDequeue(out ignored));
+ return Task.Delay(0);
+ }
protected void Dispose(bool disposing)
{
if (disposing)
diff --git a/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs
index c5b0983d0..2440fc0a8 100644
--- a/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs
+++ b/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System.Threading;
+using System.Threading.Tasks;
namespace Discord.Audio
{
@@ -13,7 +14,9 @@ namespace Discord.Audio
public Task SendAsync(byte[] buffer, int count)
=> _client.SendAsync(buffer, count);
- public Task FlushAsync()
+ public Task FlushAsync(CancellationToken cancelToken)
+ => Task.Delay(0);
+ public Task ClearAsync(CancellationToken cancelToken)
=> Task.Delay(0);
}
}
diff --git a/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs
index 8c4384550..1aa0d4ade 100644
--- a/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs
+++ b/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs
@@ -1,10 +1,12 @@
-using System.Threading.Tasks;
+using System.Threading;
+using System.Threading.Tasks;
namespace Discord.Audio
{
internal interface IAudioTarget
{
Task SendAsync(byte[] buffer, int count);
- Task FlushAsync();
+ Task FlushAsync(CancellationToken cancelToken);
+ Task ClearAsync(CancellationToken cancelToken);
}
}