From e2c1a51456160f0ab2c25e435aff0bba0f85540b Mon Sep 17 00:00:00 2001 From: Nitix Date: Fri, 26 Aug 2016 04:59:33 +0200 Subject: [PATCH] SongBuffer is now a stream, and the Song will now wiat correctly before sending --- src/NadekoBot/Modules/Music/Classes/Song.cs | 35 ++++---- .../Modules/Music/Classes/SongBuffer.cs | 81 +++++++++++++++++-- 2 files changed, 90 insertions(+), 26 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/Song.cs b/src/NadekoBot/Modules/Music/Classes/Song.cs index 45c0e185..f6827d2a 100644 --- a/src/NadekoBot/Modules/Music/Classes/Song.cs +++ b/src/NadekoBot/Modules/Music/Classes/Song.cs @@ -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); } diff --git a/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs b/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs index d9192940..a53d7d0b 100644 --- a/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs +++ b/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs @@ -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. /// - 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 /// /// Name of the file to read - 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(); + } } }