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】";
 | 
					            return $"【{(int)time.TotalMinutes}m {time.Seconds}s】";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const int milliseconds = 10;
 | 
					        const int milliseconds = 20;
 | 
				
			||||||
        const int samplesPerFrame = (48000 / 1000) * milliseconds;
 | 
					        const int samplesPerFrame = (48000 / 1000) * milliseconds;
 | 
				
			||||||
        const int frameBytes = 3840; //16-bit, 2 channels
 | 
					        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());
 | 
					            var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            SongBuffer sb = new SongBuffer(filename, SongInfo, skipTo);
 | 
					            SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo);
 | 
				
			||||||
            var bufferTask = sb.BufferSong(cancelToken).ConfigureAwait(false);
 | 
					            var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            var inStream = new FileStream(sb.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); ;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            bytesSent = 0;
 | 
					            bytesSent = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,7 +93,7 @@ namespace NadekoBot.Modules.Music.Classes
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                var attempt = 0;             
 | 
					                var attempt = 0;             
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var prebufferingTask = CheckPrebufferingAsync(inStream, sb, cancelToken);
 | 
					                var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken);
 | 
				
			||||||
                var sw = new Stopwatch();
 | 
					                var sw = new Stopwatch();
 | 
				
			||||||
                sw.Start();
 | 
					                sw.Start();
 | 
				
			||||||
                var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
 | 
					                var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
 | 
				
			||||||
@@ -112,9 +110,10 @@ namespace NadekoBot.Modules.Music.Classes
 | 
				
			|||||||
                sw.Stop();
 | 
					                sw.Stop();
 | 
				
			||||||
                _log.Debug("Prebuffering successfully completed in "+ sw.Elapsed);
 | 
					                _log.Debug("Prebuffering successfully completed in "+ sw.Elapsed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
                var outStream = voiceClient.CreatePCMStream(960);
 | 
					                var outStream = voiceClient.CreatePCMStream(960);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                int nextTime = Environment.TickCount + milliseconds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                byte[] buffer = new byte[frameBytes];
 | 
					                byte[] buffer = new byte[frameBytes];
 | 
				
			||||||
                while (!cancelToken.IsCancellationRequested)
 | 
					                while (!cancelToken.IsCancellationRequested)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -128,16 +127,9 @@ namespace NadekoBot.Modules.Music.Classes
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    if (read < frameBytes)
 | 
					                    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 (read == 0)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            if (sb.BufferingCompleted)
 | 
					                            if (inStream.BufferingCompleted)
 | 
				
			||||||
                                break;
 | 
					                                break;
 | 
				
			||||||
                            if (attempt++ == 20)
 | 
					                            if (attempt++ == 20)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
@@ -156,10 +148,14 @@ namespace NadekoBot.Modules.Music.Classes
 | 
				
			|||||||
                    while (this.MusicPlayer.Paused)
 | 
					                    while (this.MusicPlayer.Paused)
 | 
				
			||||||
                        await Task.Delay(200, cancelToken).ConfigureAwait(false);
 | 
					                        await Task.Delay(200, cancelToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    //buffer = AdjustVolume(buffer, MusicPlayer.Volume);
 | 
					
 | 
				
			||||||
 | 
					                    buffer = AdjustVolume(buffer, MusicPlayer.Volume);
 | 
				
			||||||
                    if (read != frameBytes) continue;
 | 
					                    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 outStream.WriteAsync(buffer, 0, read);
 | 
				
			||||||
                    await Task.Delay(10);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            finally
 | 
					            finally
 | 
				
			||||||
@@ -167,13 +163,12 @@ namespace NadekoBot.Modules.Music.Classes
 | 
				
			|||||||
                await bufferTask;
 | 
					                await bufferTask;
 | 
				
			||||||
                if(inStream != null)
 | 
					                if(inStream != null)
 | 
				
			||||||
                    inStream.Dispose();
 | 
					                    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);
 | 
					                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.
 | 
					    /// 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.
 | 
					    /// It also help for large music by deleting files that are already seen.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </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;
 | 
					            Basename = basename;
 | 
				
			||||||
            SongInfo = songInfo;
 | 
					            SongInfo = songInfo;
 | 
				
			||||||
            SkipTo = skipTo;
 | 
					            SkipTo = skipTo;
 | 
				
			||||||
 | 
					            CurrentFileStream = new FileStream(this.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MusicPlayer MusicPlayer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string Basename;
 | 
					        private string Basename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private SongInfo SongInfo;
 | 
					        private SongInfo SongInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private int SkipTo;
 | 
					        private int SkipTo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static int MAX_FILE_SIZE = 20.MiB();
 | 
					        private static int MAX_FILE_SIZE = 2.MiB();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private long FileNumber = -1;
 | 
					        private long FileNumber = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,6 +45,8 @@ namespace NadekoBot.Modules.Music.Classes
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private ulong CurrentBufferSize = 0;
 | 
					        private ulong CurrentBufferSize = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private FileStream CurrentFileStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Task BufferSong(CancellationToken cancelToken) =>
 | 
					        public Task BufferSong(CancellationToken cancelToken) =>
 | 
				
			||||||
           Task.Factory.StartNew(async () =>
 | 
					           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
 | 
					        /// Return the next file to read, and delete the old one
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <returns>Name of the file to read</returns>
 | 
					        /// <returns>Name of the file to read</returns>
 | 
				
			||||||
        public string GetNextFile()
 | 
					        private string GetNextFile()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            string filename = Basename + "-" + NextFileToRead;
 | 
					            string filename = Basename + "-" + NextFileToRead;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
@@ -139,12 +145,12 @@ Check the guides for your platform on how to setup ffmpeg correctly:
 | 
				
			|||||||
            return filename;
 | 
					            return filename;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool IsNextFileReady()
 | 
					        private bool IsNextFileReady()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return NextFileToRead <= FileNumber;
 | 
					            return NextFileToRead <= FileNumber;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void CleanFiles()
 | 
					        private void CleanFiles()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
 | 
					            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 { }
 | 
					                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