Merge pull request #593 from Nitix/audio
SongBuffer is now a stream, and the Song will now wait correctly
This commit is contained in:
		| @@ -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); | ||||
|             } | ||||
|   | ||||
| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user