From 0c8934f12f0a1cf09b2c5c45776f2a3dae6eb694 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Fri, 29 Jan 2016 08:23:15 +0100 Subject: [PATCH] Semi-working music fix, DO NOT USE YET --- NadekoBot/Classes/Music/MusicControls.cs | 34 +++++-- NadekoBot/Classes/Music/StreamRequest.cs | 110 ++++++++++++----------- NadekoBot/Modules/Music.cs | 12 +-- 3 files changed, 94 insertions(+), 62 deletions(-) diff --git a/NadekoBot/Classes/Music/MusicControls.cs b/NadekoBot/Classes/Music/MusicControls.cs index bee9bd54..4bbd0b01 100644 --- a/NadekoBot/Classes/Music/MusicControls.cs +++ b/NadekoBot/Classes/Music/MusicControls.cs @@ -3,6 +3,7 @@ using Discord.Audio; using System; using System.Collections.Generic; using System.Threading.Tasks; +using Discord.Commands; namespace NadekoBot.Classes.Music { public class MusicControls { @@ -14,6 +15,9 @@ namespace NadekoBot.Classes.Music { public StreamRequest CurrentSong; public bool IsPaused { get; internal set; } + public IAudioClient VoiceClient; + + private readonly object _voiceLock = new object(); public MusicControls() { Task.Run(async () => { @@ -22,6 +26,7 @@ namespace NadekoBot.Classes.Music { if (CurrentSong == null) { if (SongQueue.Count > 0) LoadNextSong(); + } else if (CurrentSong.State == StreamState.Completed) { LoadNextSong(); } @@ -33,25 +38,42 @@ namespace NadekoBot.Classes.Music { }); } + public MusicControls(Channel voiceChannel) : this() { + VoiceChannel = voiceChannel; + } + public void LoadNextSong() { Console.WriteLine("Loading next song."); - if (SongQueue.Count == 0) { - CurrentSong = null; - return; + lock (_voiceLock) { + if (SongQueue.Count == 0) { + CurrentSong = null; + return; + } + CurrentSong = SongQueue[0]; + SongQueue.RemoveAt(0); } - CurrentSong = SongQueue[0]; - SongQueue.RemoveAt(0); CurrentSong.Start(); + Console.WriteLine("Starting next song."); } internal void RemoveAllSongs() { - lock (SongQueue) { + lock (_voiceLock) { foreach (var kvp in SongQueue) { if(kvp != null) kvp.Cancel(); } SongQueue.Clear(); + VoiceClient.Disconnect(); + VoiceClient = null; + } + } + + internal StreamRequest CreateStreamRequest(CommandEventArgs e, string query, Channel voiceChannel) { + lock (_voiceLock) { + if (VoiceClient == null) + VoiceClient = NadekoBot.client.Audio().Join(VoiceChannel).Result; + return new StreamRequest(e, query, VoiceClient); } } } diff --git a/NadekoBot/Classes/Music/StreamRequest.cs b/NadekoBot/Classes/Music/StreamRequest.cs index 46b671f5..002cc3ac 100644 --- a/NadekoBot/Classes/Music/StreamRequest.cs +++ b/NadekoBot/Classes/Music/StreamRequest.cs @@ -29,20 +29,23 @@ namespace NadekoBot.Classes.Music { public User User { get; } public string Query { get; } + + public string Title { get; internal set; } = String.Empty; + public IAudioClient VoiceClient { get; private set; } + private MusicStreamer musicStreamer = null; public StreamState State => musicStreamer?.State ?? StreamState.Resolving; - public StreamRequest(CommandEventArgs e, string query) { + public StreamRequest(CommandEventArgs e, string query, IAudioClient voiceClient) { if (e == null) throw new ArgumentNullException(nameof(e)); if (query == null) throw new ArgumentNullException(nameof(query)); - if (e.User.VoiceChannel == null) - throw new NullReferenceException("Voicechannel is null."); + if (voiceClient == null) + throw new NullReferenceException($"{nameof(voiceClient)} is null, bot didn't join any server."); + this.VoiceClient = voiceClient; this.Server = e.Server; - this.Channel = e.User.VoiceChannel; - this.User = e.User; this.Query = query; ResolveStreamLink(); } @@ -60,7 +63,8 @@ namespace NadekoBot.Classes.Music { Title = video.Title; musicStreamer = new MusicStreamer(this, video.DownloadUrl, Channel); - OnQueued(); + if(OnQueued!=null) + OnQueued(); }); internal string PrintStats() => musicStreamer?.Stats(); @@ -69,8 +73,6 @@ namespace NadekoBot.Classes.Music { throw new NotImplementedException(); } - public string Title { get; internal set; } = String.Empty; - public Action OnQueued = null; public Action OnBuffering = null; public Action OnStarted = null; @@ -146,17 +148,34 @@ namespace NadekoBot.Classes.Music { CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, }); - + int attempt = 0; while (true) { - while (buffer.writePos - buffer.readPos > 2.MB() && State != StreamState.Completed) { - try { + + while (buffer.writePos - buffer.readPos > 5.MB() && State != StreamState.Completed) { if (bufferCancelSource.Token.CanBeCanceled && !bufferCancelSource.IsCancellationRequested) { bufferCancelSource.Cancel(); Console.WriteLine("Canceling buffer token"); } - } catch (Exception ex) { Console.WriteLine($"Canceling buffer token failed {ex}"); } + await Task.Delay(1000); } + + if (buffer.readPos > 5.MiB()) { // if buffer is over 5 MiB, create new one + Console.WriteLine("Buffer over 5 megs, clearing."); + + var skip = 5.MB(); //remove only 5 MB, just in case + var newBuffer = new DualStream(); + + lock (_bufferLock) { + byte[] data = buffer.ToArray().Skip(skip).ToArray(); + var newReadPos = buffer.readPos - skip; + var newPos = buffer.Position - skip; + buffer = newBuffer; + buffer.Write(data, 0, data.Length); + buffer.readPos = newReadPos; + buffer.Position = newPos; + } + } if (State == StreamState.Completed) { try { @@ -165,42 +184,30 @@ namespace NadekoBot.Classes.Music { } catch (Exception) { } Console.WriteLine("Buffering canceled, stream is completed."); return; - } - - if (buffer.readPos > 5.MiB()) { // if buffer is over 5 MiB, create new one - Console.WriteLine("Buffer over 5 megs, clearing."); - - var skip = 5.MB(); //remove only 10 MB, just in case - byte[] data = buffer.ToArray().Skip(skip).ToArray(); - - var newBuffer = new DualStream(); - lock (_bufferLock) { - var newReadPos = buffer.readPos - skip; - var newPos = buffer.Position - skip; - buffer = newBuffer; - buffer.Write(data, 0, data.Length); - buffer.readPos = newReadPos; - buffer.Position = newPos; - } - - } + } var buf = new byte[1024]; int read = 0; read = await p.StandardOutput.BaseStream.ReadAsync(buf, 0, 1024); //Console.WriteLine($"Read: {read}"); if (read == 0) { - try { - p.CancelOutputRead(); - p.Close(); - } catch (Exception) { } + if (attempt == 2) { + try { + p.CancelOutputRead(); + p.Close(); + } catch (Exception) { } - Console.WriteLine("Didn't read anything from the stream"); - return; + Console.WriteLine($"Didn't read anything from the stream for {attempt} attempts. {buffer.Length/1.MB()}MB length"); + return; + } else { + ++attempt; + await Task.Delay(10); + } + } else { + attempt = 0; + await buffer.WriteAsync(buf, 0, read); } - await buffer.WriteAsync(buf, 0, read); } - } internal async Task StartPlayback() { @@ -224,13 +231,11 @@ namespace NadekoBot.Classes.Music { Console.WriteLine("Didn't buffer jack shit."); } //for now wait for 3 seconds before starting playback. - - var audio = NadekoBot.client.Audio(); - - var voiceClient = await audio.Join(channel); + int blockSize = 1920 * NadekoBot.client.Audio().Config.Channels; byte[] voiceBuffer = new byte[blockSize]; + int attempt = 0; while (!IsCanceled) { int readCount = 0; lock (_bufferLock) { @@ -238,21 +243,24 @@ namespace NadekoBot.Classes.Music { } if (readCount == 0) { - Console.WriteLine("Nothing else to read."); - break; - } + if (attempt == 2) { + Console.WriteLine($"Failed to read {attempt} times. Stopping playback."); + break; + } else { + ++attempt; + await Task.Delay(10); + } + } else + attempt = 0; if (State == StreamState.Completed) { Console.WriteLine("Canceled"); break; } - voiceClient.Send(voiceBuffer, 0, voiceBuffer.Length); + parent.VoiceClient.Send(voiceBuffer, 0, voiceBuffer.Length); } - - voiceClient.Wait(); - await voiceClient.Disconnect(); - + parent.VoiceClient.Wait(); StopPlayback(); } diff --git a/NadekoBot/Modules/Music.cs b/NadekoBot/Modules/Music.cs index de2f9cfd..993db9b9 100644 --- a/NadekoBot/Modules/Music.cs +++ b/NadekoBot/Modules/Music.cs @@ -104,11 +104,11 @@ namespace NadekoBot.Modules { return; } else - musicPlayers.TryAdd(e.Server, new MusicControls()); + (musicPlayers as System.Collections.IDictionary).Add(e.Server, new MusicControls(e.User.VoiceChannel)); var player = musicPlayers[e.Server]; try { - var sr = new StreamRequest(e, e.GetArg("query")); + var sr = player.CreateStreamRequest(e, e.GetArg("query"), player.VoiceChannel); Message msg = null; sr.OnQueued += async() => { msg = await e.Send($":musical_note:**Queued** {sr.Title}"); @@ -118,15 +118,17 @@ namespace NadekoBot.Modules { }; sr.OnStarted += async () => { if (msg == null) - await e.Send($":musical_note:**Started playing** {sr.Title}"); + await e.Send($":musical_note:**Starting playback of** {sr.Title}"); else - await msg.Edit($":musical_note:**Started playing** {sr.Title}"); + await msg.Edit($":musical_note:**Starting playback of** {sr.Title}"); }; sr.OnBuffering += async () => { if (msg != null) msg = await e.Send($":musical_note:**Buffering the song**...{sr.Title}"); }; - player.SongQueue.Add(sr); + lock (player.SongQueue) { + player.SongQueue.Add(sr); + } } catch (Exception ex) { Console.WriteLine(); await e.Send($"Error. :anger:\n{ex.Message}");