Added AudioOutStream, exposed Clear/ClearAsync methods. Added support for cancelTokens.
This commit is contained in:
16
src/Discord.Net.Core/Audio/AudioOutStream.cs
Normal file
16
src/Discord.Net.Core/Audio/AudioOutStream.cs
Normal file
@@ -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); }
|
||||
}
|
||||
}
|
||||
@@ -22,26 +22,26 @@ namespace Discord.Audio
|
||||
/// </summary>
|
||||
/// <param name="samplesPerFrame">Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively.</param>
|
||||
/// <returns></returns>
|
||||
Stream CreateOpusStream(int samplesPerFrame, int bufferMillis = 1000);
|
||||
AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis = 1000);
|
||||
/// <summary>
|
||||
/// Creates a new outgoing stream accepting Opus-encoded data. This is a direct stream with no internal timer.
|
||||
/// </summary>
|
||||
/// <param name="samplesPerFrame">Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively.</param>
|
||||
/// <returns></returns>
|
||||
Stream CreateDirectOpusStream(int samplesPerFrame);
|
||||
AudioOutStream CreateDirectOpusStream(int samplesPerFrame);
|
||||
/// <summary>
|
||||
/// Creates a new outgoing stream accepting PCM (raw) data.
|
||||
/// </summary>
|
||||
/// <param name="samplesPerFrame">Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively.</param>
|
||||
/// <param name="bitrate"></param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
/// <summary>
|
||||
/// Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer.
|
||||
/// </summary>
|
||||
/// <param name="samplesPerFrame">Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively.</param>
|
||||
/// <param name="bitrate"></param>
|
||||
/// <returns></returns>
|
||||
Stream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null);
|
||||
AudioOutStream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(); } }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user