Merge pull request #593 from Nitix/audio

SongBuffer is now a stream, and the Song will now wait correctly
This commit is contained in:
Master Kwoth 2016-08-26 16:55:19 +02:00 committed by GitHub
commit e64dd6548f
2 changed files with 90 additions and 26 deletions

View File

@ -41,7 +41,7 @@ namespace NadekoBot.Modules.Music.Classes
return $"【{(int)time.TotalMinutes}m {time.Seconds}s】";
}
const int milliseconds = 10;
const int milliseconds = 20;
const int samplesPerFrame = (48000 / 1000) * milliseconds;
const int frameBytes = 3840; //16-bit, 2 channels
@ -84,10 +84,8 @@ namespace NadekoBot.Modules.Music.Classes
{
var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
SongBuffer sb = new SongBuffer(filename, SongInfo, skipTo);
var bufferTask = sb.BufferSong(cancelToken).ConfigureAwait(false);
var inStream = new FileStream(sb.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); ;
SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo);
var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
bytesSent = 0;
@ -95,7 +93,7 @@ namespace NadekoBot.Modules.Music.Classes
{
var attempt = 0;
var prebufferingTask = CheckPrebufferingAsync(inStream, sb, cancelToken);
var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken);
var sw = new Stopwatch();
sw.Start();
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
@ -112,9 +110,10 @@ namespace NadekoBot.Modules.Music.Classes
sw.Stop();
_log.Debug("Prebuffering successfully completed in "+ sw.Elapsed);
var outStream = voiceClient.CreatePCMStream(960);
int nextTime = Environment.TickCount + milliseconds;
byte[] buffer = new byte[frameBytes];
while (!cancelToken.IsCancellationRequested)
{
@ -128,16 +127,9 @@ namespace NadekoBot.Modules.Music.Classes
}
if (read < frameBytes)
{
if (sb.IsNextFileReady())
{
inStream.Dispose();
inStream = new FileStream(sb.GetNextFile(), FileMode.Open, FileAccess.Read, FileShare.Write);
read += inStream.Read(buffer, read, buffer.Length - read);
attempt = 0;
}
if (read == 0)
{
if (sb.BufferingCompleted)
if (inStream.BufferingCompleted)
break;
if (attempt++ == 20)
{
@ -156,10 +148,14 @@ namespace NadekoBot.Modules.Music.Classes
while (this.MusicPlayer.Paused)
await Task.Delay(200, cancelToken).ConfigureAwait(false);
//buffer = AdjustVolume(buffer, MusicPlayer.Volume);
buffer = AdjustVolume(buffer, MusicPlayer.Volume);
if (read != frameBytes) continue;
nextTime = unchecked(nextTime + milliseconds);
int delayMillis = unchecked(nextTime - Environment.TickCount);
if (delayMillis > 0)
await Task.Delay(delayMillis, cancelToken).ConfigureAwait(false);
await outStream.WriteAsync(buffer, 0, read);
await Task.Delay(10);
}
}
finally
@ -167,13 +163,12 @@ namespace NadekoBot.Modules.Music.Classes
await bufferTask;
if(inStream != null)
inStream.Dispose();
sb.CleanFiles();
}
}
private async Task CheckPrebufferingAsync(Stream inStream, SongBuffer sb, CancellationToken cancelToken)
private async Task CheckPrebufferingAsync(SongBuffer inStream, CancellationToken cancelToken)
{
while (!sb.BufferingCompleted && inStream.Length < 10.MiB())
while (!inStream.BufferingCompleted && inStream.Length < 10.MiB())
{
await Task.Delay(100, cancelToken);
}

View File

@ -15,23 +15,27 @@ namespace NadekoBot.Modules.Music.Classes
/// Create a buffer for a song file. It will create multiples files to ensure, that radio don't fill up disk space.
/// It also help for large music by deleting files that are already seen.
/// </summary>
class SongBuffer
class SongBuffer : Stream
{
public SongBuffer(string basename, SongInfo songInfo, int skipTo)
public SongBuffer(MusicPlayer musicPlayer, string basename, SongInfo songInfo, int skipTo)
{
MusicPlayer = musicPlayer;
Basename = basename;
SongInfo = songInfo;
SkipTo = skipTo;
CurrentFileStream = new FileStream(this.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
}
MusicPlayer MusicPlayer;
private string Basename;
private SongInfo SongInfo;
private int SkipTo;
private static int MAX_FILE_SIZE = 20.MiB();
private static int MAX_FILE_SIZE = 2.MiB();
private long FileNumber = -1;
@ -41,6 +45,8 @@ namespace NadekoBot.Modules.Music.Classes
private ulong CurrentBufferSize = 0;
private FileStream CurrentFileStream;
public Task BufferSong(CancellationToken cancelToken) =>
Task.Factory.StartNew(async () =>
{
@ -122,7 +128,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
/// Return the next file to read, and delete the old one
/// </summary>
/// <returns>Name of the file to read</returns>
public string GetNextFile()
private string GetNextFile()
{
string filename = Basename + "-" + NextFileToRead;
@ -139,12 +145,12 @@ Check the guides for your platform on how to setup ffmpeg correctly:
return filename;
}
public bool IsNextFileReady()
private bool IsNextFileReady()
{
return NextFileToRead <= FileNumber;
}
public void CleanFiles()
private void CleanFiles()
{
for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
{
@ -155,5 +161,68 @@ Check the guides for your platform on how to setup ffmpeg correctly:
catch { }
}
}
//Stream part
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => (long) CurrentBufferSize;
public override long Position
{
get
{
return 0;
}
set
{
}
}
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count)
{
int read = CurrentFileStream.Read(buffer, offset, count);
if(read < count)
{
if (!BufferingCompleted || IsNextFileReady())
{
CurrentFileStream.Dispose();
CurrentFileStream = new FileStream(GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
read += CurrentFileStream.Read(buffer, read + offset, count - read);
}
}
return read;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public new void Dispose()
{
CurrentFileStream.Dispose();
MusicPlayer.SongCancelSource.Cancel();
CleanFiles();
base.Dispose();
}
}
}