136 lines
No EOL
4.5 KiB
C#
136 lines
No EOL
4.5 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Buffers;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Ayu.Discord.Voice
|
|
{
|
|
public sealed class PoopyBufferImmortalized : ISongBuffer
|
|
{
|
|
private readonly byte[] _buffer;
|
|
private readonly byte[] _outputArray;
|
|
private CancellationToken _cancellationToken;
|
|
private bool _isStopped;
|
|
|
|
public int ReadPosition { get; private set; }
|
|
public int WritePosition { get; private set; }
|
|
|
|
public int ContentLength => WritePosition >= ReadPosition
|
|
? WritePosition - ReadPosition
|
|
: (_buffer.Length - ReadPosition) + WritePosition;
|
|
|
|
public int FreeSpace => _buffer.Length - ContentLength;
|
|
|
|
public bool Stopped => _cancellationToken.IsCancellationRequested || _isStopped;
|
|
|
|
public PoopyBufferImmortalized(int frameSize)
|
|
{
|
|
_buffer = ArrayPool<byte>.Shared.Rent(1_000_000);
|
|
_outputArray = new byte[frameSize];
|
|
|
|
ReadPosition = 0;
|
|
WritePosition = 0;
|
|
}
|
|
|
|
public void Stop()
|
|
=> _isStopped = true;
|
|
|
|
// this method needs a rewrite
|
|
public Task<bool> BufferAsync(ITrackDataSource source, CancellationToken cancellationToken)
|
|
{
|
|
_cancellationToken = cancellationToken;
|
|
var bufferingCompleted = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
Task.Run(async () =>
|
|
{
|
|
var output = ArrayPool<byte>.Shared.Rent(38400);
|
|
try
|
|
{
|
|
int read;
|
|
while (!Stopped && (read = source.Read(output)) > 0)
|
|
{
|
|
while (!Stopped && FreeSpace <= read)
|
|
{
|
|
bufferingCompleted.TrySetResult(true);
|
|
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
if (Stopped)
|
|
break;
|
|
|
|
Write(output, read);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
ArrayPool<byte>.Shared.Return(output);
|
|
bufferingCompleted.TrySetResult(true);
|
|
}
|
|
}, cancellationToken);
|
|
|
|
return bufferingCompleted.Task;
|
|
}
|
|
|
|
private void Write(byte[] input, int writeCount)
|
|
{
|
|
if (WritePosition + writeCount < _buffer.Length)
|
|
{
|
|
Buffer.BlockCopy(input, 0, _buffer, WritePosition, writeCount);
|
|
WritePosition += writeCount;
|
|
return;
|
|
}
|
|
|
|
var wroteNormally = _buffer.Length - WritePosition;
|
|
Buffer.BlockCopy(input, 0, _buffer, WritePosition, wroteNormally);
|
|
var wroteFromStart = writeCount - wroteNormally;
|
|
Buffer.BlockCopy(input, wroteNormally, _buffer, 0, wroteFromStart);
|
|
WritePosition = wroteFromStart;
|
|
}
|
|
|
|
public Span<byte> Read(int count, out int length)
|
|
{
|
|
var toRead = Math.Min(ContentLength, count);
|
|
var wp = WritePosition;
|
|
|
|
if (ContentLength == 0)
|
|
{
|
|
length = 0;
|
|
return Span<byte>.Empty;
|
|
}
|
|
|
|
if (wp > ReadPosition || ReadPosition + toRead <= _buffer.Length)
|
|
{
|
|
// thsi can be achieved without copying if
|
|
// writer never writes until the end,
|
|
// but leaves a single chunk free
|
|
Span<byte> toReturn = _outputArray;
|
|
((Span<byte>) _buffer).Slice(ReadPosition, toRead).CopyTo(toReturn);
|
|
ReadPosition += toRead;
|
|
length = toRead;
|
|
return toReturn;
|
|
}
|
|
else
|
|
{
|
|
Span<byte> toReturn = _outputArray;
|
|
var toEnd = _buffer.Length - ReadPosition;
|
|
var bufferSpan = (Span<byte>) _buffer;
|
|
|
|
bufferSpan.Slice(ReadPosition, toEnd).CopyTo(toReturn);
|
|
var fromStart = toRead - toEnd;
|
|
bufferSpan.Slice(0, fromStart).CopyTo(toReturn.Slice(toEnd));
|
|
ReadPosition = fromStart;
|
|
length = toEnd + fromStart;
|
|
return toReturn;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
=> ArrayPool<byte>.Shared.Return(_buffer);
|
|
|
|
public void Reset()
|
|
{
|
|
ReadPosition = 0;
|
|
WritePosition = 0;
|
|
}
|
|
}
|
|
} |