Merge branch 'music-rewrite' into dev
This commit is contained in:
commit
31c4e315c6
@ -2,7 +2,9 @@
|
|||||||
using Discord.Audio;
|
using Discord.Audio;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
namespace NadekoBot.Modules.Music.Classes
|
namespace NadekoBot.Modules.Music.Classes
|
||||||
@ -20,7 +22,6 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
Resolving,
|
Resolving,
|
||||||
Queued,
|
Queued,
|
||||||
Buffering, //not using it atm
|
|
||||||
Playing,
|
Playing,
|
||||||
Completed
|
Completed
|
||||||
}
|
}
|
||||||
@ -33,7 +34,7 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
public IReadOnlyCollection<Song> Playlist => playlist;
|
public IReadOnlyCollection<Song> Playlist => playlist;
|
||||||
private readonly object playlistLock = new object();
|
private readonly object playlistLock = new object();
|
||||||
|
|
||||||
public Song CurrentSong { get; set; } = default(Song);
|
public Song CurrentSong { get; private set; }
|
||||||
private CancellationTokenSource SongCancelSource { get; set; }
|
private CancellationTokenSource SongCancelSource { get; set; }
|
||||||
private CancellationToken cancelToken { get; set; }
|
private CancellationToken cancelToken { get; set; }
|
||||||
|
|
||||||
@ -52,6 +53,8 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
public bool Autoplay { get; set; } = false;
|
public bool Autoplay { get; set; } = false;
|
||||||
public uint MaxQueueSize { get; set; } = 0;
|
public uint MaxQueueSize { get; set; } = 0;
|
||||||
|
|
||||||
|
private ConcurrentQueue<Action> actionQueue { get; set; } = new ConcurrentQueue<Action>();
|
||||||
|
|
||||||
public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume)
|
public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume)
|
||||||
{
|
{
|
||||||
if (startingVoiceChannel == null)
|
if (startingVoiceChannel == null)
|
||||||
@ -66,85 +69,110 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
while (!Destroyed)
|
try
|
||||||
{
|
{
|
||||||
try
|
while (!Destroyed)
|
||||||
{
|
|
||||||
if (audioClient?.State != ConnectionState.Connected)
|
|
||||||
audioClient = await PlaybackVoiceChannel.JoinAudio().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
await Task.Delay(1000).ConfigureAwait(false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
CurrentSong = GetNextSong();
|
|
||||||
var curSong = CurrentSong;
|
|
||||||
if (curSong != null)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
OnStarted(this, curSong);
|
Action action;
|
||||||
await curSong.Play(audioClient, cancelToken).ConfigureAwait(false);
|
if (actionQueue.TryDequeue(out action))
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
finally
|
||||||
{
|
{
|
||||||
Console.WriteLine("Song canceled");
|
await Task.Delay(100).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Exception in PlaySong: {ex}");
|
|
||||||
}
|
|
||||||
OnCompleted(this, curSong);
|
|
||||||
curSong = CurrentSong; //to check if its null now
|
|
||||||
if (curSong != null)
|
|
||||||
if (RepeatSong)
|
|
||||||
playlist.Insert(0, curSong);
|
|
||||||
else if (RepeatPlaylist)
|
|
||||||
playlist.Insert(playlist.Count, curSong);
|
|
||||||
SongCancelSource = new CancellationTokenSource();
|
|
||||||
cancelToken = SongCancelSource.Token;
|
|
||||||
}
|
}
|
||||||
await Task.Delay(1000).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
});
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Action queue crashed");
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var t = new Thread(new ThreadStart(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!Destroyed)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (audioClient?.State != ConnectionState.Connected)
|
||||||
|
{
|
||||||
|
audioClient = await PlaybackVoiceChannel.JoinAudio();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentSong = GetNextSong();
|
||||||
|
RemoveSongAt(0);
|
||||||
|
|
||||||
|
if (CurrentSong == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OnStarted(this, CurrentSong);
|
||||||
|
await CurrentSong.Play(audioClient, cancelToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Song canceled");
|
||||||
|
SongCancelSource = new CancellationTokenSource();
|
||||||
|
cancelToken = SongCancelSource.Token;
|
||||||
|
}
|
||||||
|
OnCompleted(this, CurrentSong);
|
||||||
|
|
||||||
|
if (RepeatPlaylist)
|
||||||
|
AddSong(CurrentSong, CurrentSong.QueuerName);
|
||||||
|
|
||||||
|
if (RepeatSong)
|
||||||
|
AddSong(CurrentSong, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await Task.Delay(300).ConfigureAwait(false);
|
||||||
|
CurrentSong = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Console.WriteLine("Music thread crashed.");
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
t.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Next()
|
public void Next()
|
||||||
{
|
{
|
||||||
lock (playlistLock)
|
actionQueue.Enqueue(() =>
|
||||||
{
|
{
|
||||||
if (!SongCancelSource.IsCancellationRequested)
|
Paused = false;
|
||||||
{
|
SongCancelSource.Cancel();
|
||||||
Paused = false;
|
});
|
||||||
SongCancelSource.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
lock (playlistLock)
|
actionQueue.Enqueue(() =>
|
||||||
{
|
{
|
||||||
playlist.Clear();
|
|
||||||
CurrentSong = null;
|
|
||||||
RepeatPlaylist = false;
|
RepeatPlaylist = false;
|
||||||
RepeatSong = false;
|
RepeatSong = false;
|
||||||
|
playlist.Clear();
|
||||||
if (!SongCancelSource.IsCancellationRequested)
|
if (!SongCancelSource.IsCancellationRequested)
|
||||||
SongCancelSource.Cancel();
|
SongCancelSource.Cancel();
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TogglePause() => Paused = !Paused;
|
public void TogglePause() => Paused = !Paused;
|
||||||
|
|
||||||
public void Shuffle()
|
|
||||||
{
|
|
||||||
lock (playlistLock)
|
|
||||||
{
|
|
||||||
playlist.Shuffle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int SetVolume(int volume)
|
public int SetVolume(int volume)
|
||||||
{
|
{
|
||||||
if (volume < 0)
|
if (volume < 0)
|
||||||
@ -156,16 +184,15 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Song GetNextSong()
|
private Song GetNextSong() =>
|
||||||
|
playlist.FirstOrDefault();
|
||||||
|
|
||||||
|
public void Shuffle()
|
||||||
{
|
{
|
||||||
lock (playlistLock)
|
actionQueue.Enqueue(() =>
|
||||||
{
|
{
|
||||||
if (playlist.Count == 0)
|
playlist.Shuffle();
|
||||||
return null;
|
});
|
||||||
var toReturn = playlist[0];
|
|
||||||
playlist.RemoveAt(0);
|
|
||||||
return toReturn;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddSong(Song s, string username)
|
public void AddSong(Song s, string username)
|
||||||
@ -173,42 +200,64 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
if (s == null)
|
if (s == null)
|
||||||
throw new ArgumentNullException(nameof(s));
|
throw new ArgumentNullException(nameof(s));
|
||||||
ThrowIfQueueFull();
|
ThrowIfQueueFull();
|
||||||
lock (playlistLock)
|
actionQueue.Enqueue(() =>
|
||||||
{
|
{
|
||||||
s.MusicPlayer = this;
|
s.MusicPlayer = this;
|
||||||
s.QueuerName = username.TrimTo(10);
|
s.QueuerName = username.TrimTo(10);
|
||||||
playlist.Add(s);
|
playlist.Add(s);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddSong(Song s, int index)
|
public void AddSong(Song s, int index)
|
||||||
{
|
{
|
||||||
if (s == null)
|
if (s == null)
|
||||||
throw new ArgumentNullException(nameof(s));
|
throw new ArgumentNullException(nameof(s));
|
||||||
lock (playlistLock)
|
actionQueue.Enqueue(() =>
|
||||||
{
|
{
|
||||||
playlist.Insert(index, s);
|
playlist.Insert(index, s);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveSong(Song s)
|
public void RemoveSong(Song s)
|
||||||
{
|
{
|
||||||
if (s == null)
|
if (s == null)
|
||||||
throw new ArgumentNullException(nameof(s));
|
throw new ArgumentNullException(nameof(s));
|
||||||
lock (playlistLock)
|
actionQueue.Enqueue(() =>
|
||||||
{
|
{
|
||||||
playlist.Remove(s);
|
playlist.Remove(s);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveSongAt(int index)
|
public void RemoveSongAt(int index)
|
||||||
{
|
{
|
||||||
lock (playlistLock)
|
actionQueue.Enqueue(() =>
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= playlist.Count)
|
if (index < 0 || index >= playlist.Count)
|
||||||
throw new ArgumentException("Invalid index");
|
return;
|
||||||
playlist.RemoveAt(index);
|
playlist.RemoveAt(index);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ClearQueue()
|
||||||
|
{
|
||||||
|
actionQueue.Enqueue(() =>
|
||||||
|
{
|
||||||
|
playlist.Clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Destroy()
|
||||||
|
{
|
||||||
|
actionQueue.Enqueue(() =>
|
||||||
|
{
|
||||||
|
RepeatPlaylist = false;
|
||||||
|
RepeatSong = false;
|
||||||
|
Destroyed = true;
|
||||||
|
playlist.Clear();
|
||||||
|
if (!SongCancelSource.IsCancellationRequested)
|
||||||
|
SongCancelSource.Cancel();
|
||||||
|
audioClient.Disconnect();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task MoveToVoiceChannel(Channel voiceChannel)
|
internal Task MoveToVoiceChannel(Channel voiceChannel)
|
||||||
@ -219,27 +268,6 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
return PlaybackVoiceChannel.JoinAudio();
|
return PlaybackVoiceChannel.JoinAudio();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ClearQueue()
|
|
||||||
{
|
|
||||||
lock (playlistLock)
|
|
||||||
{
|
|
||||||
playlist.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Destroy()
|
|
||||||
{
|
|
||||||
lock (playlistLock)
|
|
||||||
{
|
|
||||||
playlist.Clear();
|
|
||||||
Destroyed = true;
|
|
||||||
CurrentSong = null;
|
|
||||||
if (!SongCancelSource.IsCancellationRequested)
|
|
||||||
SongCancelSource.Cancel();
|
|
||||||
audioClient.Disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong;
|
internal bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong;
|
||||||
|
|
||||||
internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist;
|
internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist;
|
||||||
|
@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
public SongInfo SongInfo { get; }
|
public SongInfo SongInfo { get; }
|
||||||
public string QueuerName { get; set; }
|
public string QueuerName { get; set; }
|
||||||
|
|
||||||
private PoopyBuffer songBuffer { get; } = new PoopyBuffer(NadekoBot.Config.BufferSize);
|
private PoopyBuffer songBuffer { get; set; }
|
||||||
|
|
||||||
private bool prebufferingComplete { get; set; } = false;
|
private bool prebufferingComplete { get; set; } = false;
|
||||||
public MusicPlayer MusicPlayer { get; set; }
|
public MusicPlayer MusicPlayer { get; set; }
|
||||||
@ -137,6 +137,9 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
|
|
||||||
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
|
// initialize the buffer here because if this song was playing before (requeued), we must delete old buffer data
|
||||||
|
songBuffer = new PoopyBuffer(NadekoBot.Config.BufferSize);
|
||||||
|
|
||||||
var bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
|
var bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
|
||||||
var bufferAttempts = 0;
|
var bufferAttempts = 0;
|
||||||
const int waitPerAttempt = 500;
|
const int waitPerAttempt = 500;
|
||||||
@ -145,7 +148,6 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false);
|
await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
cancelToken.ThrowIfCancellationRequested();
|
|
||||||
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}");
|
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}");
|
||||||
const int blockSize = 3840;
|
const int blockSize = 3840;
|
||||||
var attempt = 0;
|
var attempt = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user