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.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);
}
}
}

View File

@ -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();
}

View File

@ -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}");