Semi-working music fix, DO NOT USE YET

This commit is contained in:
Master Kwoth 2016-01-29 08:23:15 +01:00
parent 78da2599f1
commit 0c8934f12f
3 changed files with 94 additions and 62 deletions

View File

@ -3,6 +3,7 @@ using Discord.Audio;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.Commands;
namespace NadekoBot.Classes.Music { namespace NadekoBot.Classes.Music {
public class MusicControls { public class MusicControls {
@ -14,6 +15,9 @@ namespace NadekoBot.Classes.Music {
public StreamRequest CurrentSong; public StreamRequest CurrentSong;
public bool IsPaused { get; internal set; } public bool IsPaused { get; internal set; }
public IAudioClient VoiceClient;
private readonly object _voiceLock = new object();
public MusicControls() { public MusicControls() {
Task.Run(async () => { Task.Run(async () => {
@ -22,6 +26,7 @@ namespace NadekoBot.Classes.Music {
if (CurrentSong == null) { if (CurrentSong == null) {
if (SongQueue.Count > 0) if (SongQueue.Count > 0)
LoadNextSong(); LoadNextSong();
} else if (CurrentSong.State == StreamState.Completed) { } else if (CurrentSong.State == StreamState.Completed) {
LoadNextSong(); LoadNextSong();
} }
@ -33,25 +38,42 @@ namespace NadekoBot.Classes.Music {
}); });
} }
public MusicControls(Channel voiceChannel) : this() {
VoiceChannel = voiceChannel;
}
public void LoadNextSong() { public void LoadNextSong() {
Console.WriteLine("Loading next song."); Console.WriteLine("Loading next song.");
if (SongQueue.Count == 0) { lock (_voiceLock) {
CurrentSong = null; if (SongQueue.Count == 0) {
return; CurrentSong = null;
return;
}
CurrentSong = SongQueue[0];
SongQueue.RemoveAt(0);
} }
CurrentSong = SongQueue[0];
SongQueue.RemoveAt(0);
CurrentSong.Start(); CurrentSong.Start();
Console.WriteLine("Starting next song."); Console.WriteLine("Starting next song.");
} }
internal void RemoveAllSongs() { internal void RemoveAllSongs() {
lock (SongQueue) { lock (_voiceLock) {
foreach (var kvp in SongQueue) { foreach (var kvp in SongQueue) {
if(kvp != null) if(kvp != null)
kvp.Cancel(); kvp.Cancel();
} }
SongQueue.Clear(); 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);
} }
} }
} }

View File

@ -29,20 +29,23 @@ namespace NadekoBot.Classes.Music {
public User User { get; } public User User { get; }
public string Query { get; } public string Query { get; }
public string Title { get; internal set; } = String.Empty;
public IAudioClient VoiceClient { get; private set; }
private MusicStreamer musicStreamer = null; private MusicStreamer musicStreamer = null;
public StreamState State => musicStreamer?.State ?? StreamState.Resolving; public StreamState State => musicStreamer?.State ?? StreamState.Resolving;
public StreamRequest(CommandEventArgs e, string query) { public StreamRequest(CommandEventArgs e, string query, IAudioClient voiceClient) {
if (e == null) if (e == null)
throw new ArgumentNullException(nameof(e)); throw new ArgumentNullException(nameof(e));
if (query == null) if (query == null)
throw new ArgumentNullException(nameof(query)); throw new ArgumentNullException(nameof(query));
if (e.User.VoiceChannel == null) if (voiceClient == null)
throw new NullReferenceException("Voicechannel is null."); throw new NullReferenceException($"{nameof(voiceClient)} is null, bot didn't join any server.");
this.VoiceClient = voiceClient;
this.Server = e.Server; this.Server = e.Server;
this.Channel = e.User.VoiceChannel;
this.User = e.User;
this.Query = query; this.Query = query;
ResolveStreamLink(); ResolveStreamLink();
} }
@ -60,7 +63,8 @@ namespace NadekoBot.Classes.Music {
Title = video.Title; Title = video.Title;
musicStreamer = new MusicStreamer(this, video.DownloadUrl, Channel); musicStreamer = new MusicStreamer(this, video.DownloadUrl, Channel);
OnQueued(); if(OnQueued!=null)
OnQueued();
}); });
internal string PrintStats() => musicStreamer?.Stats(); internal string PrintStats() => musicStreamer?.Stats();
@ -69,8 +73,6 @@ namespace NadekoBot.Classes.Music {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public string Title { get; internal set; } = String.Empty;
public Action OnQueued = null; public Action OnQueued = null;
public Action OnBuffering = null; public Action OnBuffering = null;
public Action OnStarted = null; public Action OnStarted = null;
@ -146,18 +148,35 @@ namespace NadekoBot.Classes.Music {
CreateNoWindow = true, CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,
}); });
int attempt = 0;
while (true) { 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) { if (bufferCancelSource.Token.CanBeCanceled && !bufferCancelSource.IsCancellationRequested) {
bufferCancelSource.Cancel(); bufferCancelSource.Cancel();
Console.WriteLine("Canceling buffer token"); Console.WriteLine("Canceling buffer token");
} }
} catch (Exception ex) { Console.WriteLine($"Canceling buffer token failed {ex}"); }
await Task.Delay(1000); 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) { if (State == StreamState.Completed) {
try { try {
p.CancelOutputRead(); p.CancelOutputRead();
@ -167,40 +186,28 @@ namespace NadekoBot.Classes.Music {
return; 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]; var buf = new byte[1024];
int read = 0; int read = 0;
read = await p.StandardOutput.BaseStream.ReadAsync(buf, 0, 1024); read = await p.StandardOutput.BaseStream.ReadAsync(buf, 0, 1024);
//Console.WriteLine($"Read: {read}"); //Console.WriteLine($"Read: {read}");
if (read == 0) { if (read == 0) {
try { if (attempt == 2) {
p.CancelOutputRead(); try {
p.Close(); p.CancelOutputRead();
} catch (Exception) { } p.Close();
} catch (Exception) { }
Console.WriteLine("Didn't read anything from the stream"); Console.WriteLine($"Didn't read anything from the stream for {attempt} attempts. {buffer.Length/1.MB()}MB length");
return; 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() { internal async Task StartPlayback() {
@ -225,12 +232,10 @@ namespace NadekoBot.Classes.Music {
} }
//for now wait for 3 seconds before starting playback. //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; int blockSize = 1920 * NadekoBot.client.Audio().Config.Channels;
byte[] voiceBuffer = new byte[blockSize]; byte[] voiceBuffer = new byte[blockSize];
int attempt = 0;
while (!IsCanceled) { while (!IsCanceled) {
int readCount = 0; int readCount = 0;
lock (_bufferLock) { lock (_bufferLock) {
@ -238,21 +243,24 @@ namespace NadekoBot.Classes.Music {
} }
if (readCount == 0) { if (readCount == 0) {
Console.WriteLine("Nothing else to read."); if (attempt == 2) {
break; Console.WriteLine($"Failed to read {attempt} times. Stopping playback.");
} break;
} else {
++attempt;
await Task.Delay(10);
}
} else
attempt = 0;
if (State == StreamState.Completed) { if (State == StreamState.Completed) {
Console.WriteLine("Canceled"); Console.WriteLine("Canceled");
break; break;
} }
voiceClient.Send(voiceBuffer, 0, voiceBuffer.Length); parent.VoiceClient.Send(voiceBuffer, 0, voiceBuffer.Length);
} }
parent.VoiceClient.Wait();
voiceClient.Wait();
await voiceClient.Disconnect();
StopPlayback(); StopPlayback();
} }

View File

@ -104,11 +104,11 @@ namespace NadekoBot.Modules {
return; return;
} }
else 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]; var player = musicPlayers[e.Server];
try { try {
var sr = new StreamRequest(e, e.GetArg("query")); var sr = player.CreateStreamRequest(e, e.GetArg("query"), player.VoiceChannel);
Message msg = null; Message msg = null;
sr.OnQueued += async() => { sr.OnQueued += async() => {
msg = await e.Send($":musical_note:**Queued** {sr.Title}"); msg = await e.Send($":musical_note:**Queued** {sr.Title}");
@ -118,15 +118,17 @@ namespace NadekoBot.Modules {
}; };
sr.OnStarted += async () => { sr.OnStarted += async () => {
if (msg == null) if (msg == null)
await e.Send($":musical_note:**Started playing** {sr.Title}"); await e.Send($":musical_note:**Starting playback of** {sr.Title}");
else else
await msg.Edit($":musical_note:**Started playing** {sr.Title}"); await msg.Edit($":musical_note:**Starting playback of** {sr.Title}");
}; };
sr.OnBuffering += async () => { sr.OnBuffering += async () => {
if (msg != null) if (msg != null)
msg = await e.Send($":musical_note:**Buffering the song**...{sr.Title}"); 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) { } catch (Exception ex) {
Console.WriteLine(); Console.WriteLine();
await e.Send($"Error. :anger:\n{ex.Message}"); await e.Send($"Error. :anger:\n{ex.Message}");