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>
/// <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);
}
}

+ 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);
}

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);


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

@@ -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(); } }


+ 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);
_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)


+ 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
{
@@ -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);
}
}

+ 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
{
internal interface IAudioTarget
{
Task SendAsync(byte[] buffer, int count);
Task FlushAsync();
Task FlushAsync(CancellationToken cancelToken);
Task ClearAsync(CancellationToken cancelToken);
}
}

Loading…
Cancel
Save