Browse Source

Added AudioOutStream, exposed Clear/ClearAsync methods. Added support for cancelTokens.

tags/1.0-rc
RogueException 8 years ago
parent
commit
fc8d2b3155
7 changed files with 57 additions and 22 deletions
  1. +16
    -0
      src/Discord.Net.Core/Audio/AudioOutStream.cs
  2. +4
    -4
      src/Discord.Net.Core/Audio/IAudioClient.cs
  3. +4
    -4
      src/Discord.Net.WebSocket/Audio/AudioClient.cs
  4. +12
    -6
      src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs
  5. +12
    -4
      src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs
  6. +5
    -2
      src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs
  7. +4
    -2
      src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs

+ 16
- 0
src/Discord.Net.Core/Audio/AudioOutStream.cs View 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); }
}
}

+ 4
- 4
src/Discord.Net.Core/Audio/IAudioClient.cs View File

@@ -22,26 +22,26 @@ namespace Discord.Audio
/// </summary> /// </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="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> /// <returns></returns>
Stream CreateOpusStream(int samplesPerFrame, int bufferMillis = 1000);
AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis = 1000);
/// <summary> /// <summary>
/// Creates a new outgoing stream accepting Opus-encoded data. This is a direct stream with no internal timer. /// Creates a new outgoing stream accepting Opus-encoded data. This is a direct stream with no internal timer.
/// </summary> /// </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="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> /// <returns></returns>
Stream CreateDirectOpusStream(int samplesPerFrame);
AudioOutStream CreateDirectOpusStream(int samplesPerFrame);
/// <summary> /// <summary>
/// Creates a new outgoing stream accepting PCM (raw) data. /// Creates a new outgoing stream accepting PCM (raw) data.
/// </summary> /// </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="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> /// <param name="bitrate"></param>
/// <returns></returns> /// <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> /// <summary>
/// Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer. /// Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer.
/// </summary> /// </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="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> /// <param name="bitrate"></param>
/// <returns></returns> /// <returns></returns>
Stream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null);
AudioOutStream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null);
} }
} }

+ 4
- 4
src/Discord.Net.WebSocket/Audio/AudioClient.cs View File

@@ -170,25 +170,25 @@ namespace Discord.Audio
await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); 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); CheckSamplesPerFrame(samplesPerFrame);
var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token); var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token);
return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc);
} }
public Stream CreateDirectOpusStream(int samplesPerFrame)
public AudioOutStream CreateDirectOpusStream(int samplesPerFrame)
{ {
CheckSamplesPerFrame(samplesPerFrame); CheckSamplesPerFrame(samplesPerFrame);
var target = new DirectAudioTarget(ApiClient); var target = new DirectAudioTarget(ApiClient);
return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); 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); CheckSamplesPerFrame(samplesPerFrame);
var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token); var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token);
return new OpusEncodeStream(target, _secretKey, channels, samplesPerFrame, _ssrc, bitrate); 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); CheckSamplesPerFrame(samplesPerFrame);
var target = new DirectAudioTarget(ApiClient); var target = new DirectAudioTarget(ApiClient);


+ 12
- 6
src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs View File

@@ -5,7 +5,7 @@ using System.Threading.Tasks;


namespace Discord.Audio namespace Discord.Audio
{ {
internal class RTPWriteStream : Stream
internal class RTPWriteStream : AudioOutStream
{ {
private readonly IAudioTarget _target; private readonly IAudioTarget _target;
private readonly byte[] _nonce, _secretKey; private readonly byte[] _nonce, _secretKey;
@@ -14,10 +14,6 @@ namespace Discord.Audio


protected readonly byte[] _buffer; 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) internal RTPWriteStream(IAudioTarget target, byte[] secretKey, int samplesPerFrame, uint ssrc)
{ {
_target = target; _target = target;
@@ -40,6 +36,7 @@ namespace Discord.Audio
} }
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{ {
cancellationToken.ThrowIfCancellationRequested();
unchecked unchecked
{ {
if (_nonce[3]++ == byte.MaxValue) if (_nonce[3]++ == byte.MaxValue)
@@ -63,7 +60,16 @@ namespace Discord.Audio
} }
public override async Task FlushAsync(CancellationToken cancellationToken) 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(); } } public override long Length { get { throw new NotSupportedException(); } }


+ 12
- 4
src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs View File

@@ -87,17 +87,25 @@ namespace Discord.Audio
Buffer.BlockCopy(data, 0, buffer, 0, count); Buffer.BlockCopy(data, 0, buffer, 0, count);
_queuedFrames.Enqueue(new Frame(buffer, count)); _queuedFrames.Enqueue(new Frame(buffer, count));
} }
public async Task FlushAsync()
public async Task FlushAsync(CancellationToken cancelToken)
{ {
while (true) while (true)
{ {
cancelToken.ThrowIfCancellationRequested();
if (_queuedFrames.Count == 0) if (_queuedFrames.Count == 0)
return; 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) protected void Dispose(bool disposing)
{ {
if (disposing) if (disposing)


+ 5
- 2
src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;


namespace Discord.Audio namespace Discord.Audio
{ {
@@ -13,7 +14,9 @@ namespace Discord.Audio
public Task SendAsync(byte[] buffer, int count) public Task SendAsync(byte[] buffer, int count)
=> _client.SendAsync(buffer, count); => _client.SendAsync(buffer, count);


public Task FlushAsync()
public Task FlushAsync(CancellationToken cancelToken)
=> Task.Delay(0);
public Task ClearAsync(CancellationToken cancelToken)
=> Task.Delay(0); => Task.Delay(0);
} }
} }

+ 4
- 2
src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs View File

@@ -1,10 +1,12 @@
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;


namespace Discord.Audio namespace Discord.Audio
{ {
internal interface IAudioTarget internal interface IAudioTarget
{ {
Task SendAsync(byte[] buffer, int count); Task SendAsync(byte[] buffer, int count);
Task FlushAsync();
Task FlushAsync(CancellationToken cancelToken);
Task ClearAsync(CancellationToken cancelToken);
} }
} }

Loading…
Cancel
Save