diff --git a/NadekoBot/Classes/Music/PoopyBuffer.cs b/NadekoBot/Classes/Music/PoopyBuffer.cs
new file mode 100644
index 00000000..9d59d35b
--- /dev/null
+++ b/NadekoBot/Classes/Music/PoopyBuffer.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace NadekoBot.Classes.Music
+{
+
+ ///
+ /// 💩
+ ///
+ public class PoopyBuffer
+ {
+
+ private readonly byte[] ringBuffer;
+
+ public int WritePosition { get; private set; } = 0;
+ public int ReadPosition { get; private set; } = 0;
+
+ public int ContentLength => (WritePosition >= ReadPosition ?
+ WritePosition - ReadPosition :
+ (BufferSize - ReadPosition) + WritePosition);
+
+ public int BufferSize { get; }
+
+ private readonly object readWriteLock = new object();
+
+ public PoopyBuffer(int size)
+ {
+ if (size <= 0)
+ throw new ArgumentException();
+ BufferSize = size;
+ ringBuffer = new byte[size];
+ }
+
+ public int Read(byte[] buffer, int count)
+ {
+ if (buffer.Length < count)
+ throw new ArgumentException();
+ //Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
+ lock (readWriteLock)
+ {
+ //read as much as you can if you're reading too much
+ if (count > ContentLength)
+ count = ContentLength;
+ //if nothing to read, return 0
+ if (WritePosition == ReadPosition)
+ return 0;
+ // if buffer is in the "normal" state, just read
+ if (WritePosition > ReadPosition)
+ {
+ Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
+ ReadPosition += count;
+ //Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]");
+ return count;
+ }
+ //else ReadPos buffer.Length)
+ throw new ArgumentException();
+ while (ContentLength + count > BufferSize)
+ {
+ await Task.Delay(20, cancelToken);
+ if (cancelToken.IsCancellationRequested)
+ return;
+ }
+ //the while above assures that i cannot write past readposition with my write, so i don't have to check
+ // *unless its multithreaded or task is not awaited
+ lock (readWriteLock)
+ {
+ // if i can just write without hitting buffer.length, do it
+ if (WritePosition + count < BufferSize)
+ {
+ Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count);
+ WritePosition += count;
+ //Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]");
+ return;
+ }
+ // otherwise, i have to write to the end, then write the rest from the start
+
+ var wroteNormaly = BufferSize - WritePosition;
+ Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly);
+
+ //Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]");
+
+ var wroteFromStart = count - wroteNormaly;
+ Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart);
+
+ //Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}");
+
+ WritePosition = wroteFromStart;
+ }
+ }
+ }
+}
diff --git a/NadekoBot/Classes/Music/Song.cs b/NadekoBot/Classes/Music/Song.cs
index 54a001fc..dab40651 100644
--- a/NadekoBot/Classes/Music/Song.cs
+++ b/NadekoBot/Classes/Music/Song.cs
@@ -10,116 +10,17 @@ using System.Threading;
using System.Threading.Tasks;
using VideoLibrary;
-namespace NadekoBot.Classes.Music {
- public class SongInfo {
+namespace NadekoBot.Classes.Music
+{
+ public class SongInfo
+ {
public string Provider { get; internal set; }
public MusicType ProviderType { get; internal set; }
public string Title { get; internal set; }
public string Uri { get; internal set; }
}
- ///
- /// 💩
- ///
- public class PoopyBuffer {
-
- private readonly byte[] ringBuffer;
-
- public int WritePosition { get; private set; } = 0;
- public int ReadPosition { get; private set; } = 0;
-
- public int ContentLength => (WritePosition >= ReadPosition ?
- WritePosition - ReadPosition :
- (BufferSize - ReadPosition) + WritePosition);
-
- public int BufferSize { get; }
-
- private readonly object readWriteLock = new object();
-
- public PoopyBuffer(int size) {
- if (size <= 0)
- throw new ArgumentException();
- BufferSize = size;
- ringBuffer = new byte[size];
- }
-
- public int Read(byte[] buffer, int count) {
- if (buffer.Length < count)
- throw new ArgumentException();
- //Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
- lock (readWriteLock) {
- //read as much as you can if you're reading too much
- if (count > ContentLength)
- count = ContentLength;
- //if nothing to read, return 0
- if (WritePosition == ReadPosition)
- return 0;
- // if buffer is in the "normal" state, just read
- if (WritePosition > ReadPosition) {
- Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
- ReadPosition += count;
- //Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]");
- return count;
- }
- //else ReadPos buffer.Length)
- throw new ArgumentException();
- while (ContentLength + count > BufferSize) {
- await Task.Delay(20, cancelToken);
- if (cancelToken.IsCancellationRequested)
- return;
- }
- //the while above assures that i cannot write past readposition with my write, so i don't have to check
- // *unless its multithreaded or task is not awaited
- lock (readWriteLock) {
- // if i can just write without hitting buffer.length, do it
- if (WritePosition + count < BufferSize) {
- Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count);
- WritePosition += count;
- //Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]");
- return;
- }
- // otherwise, i have to write to the end, then write the rest from the start
-
- var wroteNormaly = BufferSize - WritePosition;
- Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly);
-
- //Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]");
-
- var wroteFromStart = count - wroteNormaly;
- Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart);
-
- //Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}");
-
- WritePosition = wroteFromStart;
- }
- }
- }
- public class Song {
+ public class Song
+ {
public StreamState State { get; internal set; }
public string PrettyName =>
$"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}`";
@@ -130,22 +31,27 @@ namespace NadekoBot.Classes.Music {
private bool prebufferingComplete { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; }
- public string PrettyCurrentTime() {
+ public string PrettyCurrentTime()
+ {
var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50);
return $"【{(int)time.TotalMinutes}m {time.Seconds}s】";
}
private ulong bytesSent { get; set; } = 0;
- private Song(SongInfo songInfo) {
+ private Song(SongInfo songInfo)
+ {
this.SongInfo = songInfo;
}
private Task BufferSong(CancellationToken cancelToken) =>
- Task.Run(async () => {
+ Task.Factory.StartNew(async () =>
+ {
Process p = null;
- try {
- p = Process.Start(new ProcessStartInfo {
+ try
+ {
+ p = Process.Start(new ProcessStartInfo
+ {
FileName = "ffmpeg",
Arguments = $"-i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
UseShellExecute = false,
@@ -156,11 +62,15 @@ namespace NadekoBot.Classes.Music {
const int blockSize = 3840;
var buffer = new byte[blockSize];
var attempt = 0;
- while (!cancelToken.IsCancellationRequested) {
+ while (!cancelToken.IsCancellationRequested)
+ {
var read = 0;
- try {
+ try
+ {
read = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize, cancelToken);
- } catch {
+ }
+ catch
+ {
return;
}
if (read == 0)
@@ -168,7 +78,8 @@ namespace NadekoBot.Classes.Music {
break;
else
await Task.Delay(100, cancelToken);
- else {
+ else
+ {
attempt = 0;
await Task.Delay(5, cancelToken);
}
@@ -176,24 +87,35 @@ namespace NadekoBot.Classes.Music {
if (songBuffer.ContentLength > 2.MB())
prebufferingComplete = true;
}
- } catch (Exception ex) {
+ }
+ catch (Exception ex)
+ {
Console.WriteLine($"Buffering errored: {ex.Message}");
- } finally {
+ }
+ finally
+ {
Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]");
- if (p != null) {
- try {
+ if (p != null)
+ {
+ try
+ {
p.Kill();
- } catch { }
+ }
+ catch { }
p.Dispose();
}
}
- });
+ }, TaskCreationOptions.LongRunning);
- internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) {
+ internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
+ {
var bufferTask = new ConfiguredTaskAwaitable();
- try {
+ try
+ {
bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
- } catch (Exception ex) {
+ }
+ catch (Exception ex)
+ {
var clr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"ERR BUFFER START : {ex.Message}\n{ex}");
@@ -202,26 +124,31 @@ namespace NadekoBot.Classes.Music {
var bufferAttempts = 0;
const int waitPerAttempt = 500;
var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9;
- while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes) {
+ while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes)
+ {
await Task.Delay(waitPerAttempt, cancelToken);
}
cancelToken.ThrowIfCancellationRequested();
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}");
const int blockSize = 3840;
var attempt = 0;
- while (!cancelToken.IsCancellationRequested) {
+ while (!cancelToken.IsCancellationRequested)
+ {
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
byte[] buffer = new byte[blockSize];
var read = songBuffer.Read(buffer, blockSize);
- unchecked {
+ unchecked
+ {
bytesSent += (ulong)read;
}
if (read == 0)
- if (attempt++ == 20) {
+ if (attempt++ == 20)
+ {
voiceClient.Wait();
Console.WriteLine($"Song finished. [{songBuffer.ContentLength}]");
break;
- } else
+ }
+ else
await Task.Delay(100, cancelToken);
else
attempt = 0;
@@ -239,11 +166,13 @@ namespace NadekoBot.Classes.Music {
}
//stackoverflow ftw
- private static byte[] AdjustVolume(byte[] audioSamples, float volume) {
+ private static byte[] AdjustVolume(byte[] audioSamples, float volume)
+ {
if (Math.Abs(volume - 1.0f) < 0.01f)
return audioSamples;
var array = new byte[audioSamples.Length];
- for (var i = 0; i < array.Length; i += 2) {
+ for (var i = 0; i < array.Length; i += 2)
+ {
// convert byte pair to int
short buf1 = audioSamples[i + 1];
@@ -263,35 +192,43 @@ namespace NadekoBot.Classes.Music {
return array;
}
- public static async Task ResolveSong(string query, MusicType musicType = MusicType.Normal) {
+ public static async Task ResolveSong(string query, MusicType musicType = MusicType.Normal)
+ {
if (string.IsNullOrWhiteSpace(query))
throw new ArgumentNullException(nameof(query));
- if (musicType != MusicType.Local && IsRadioLink(query)) {
+ if (musicType != MusicType.Local && IsRadioLink(query))
+ {
musicType = MusicType.Radio;
query = await HandleStreamContainers(query) ?? query;
}
- try {
- switch (musicType) {
+ try
+ {
+ switch (musicType)
+ {
case MusicType.Local:
- return new Song(new SongInfo {
+ return new Song(new SongInfo
+ {
Uri = "\"" + Path.GetFullPath(query) + "\"",
Title = Path.GetFileNameWithoutExtension(query),
Provider = "Local File",
ProviderType = musicType,
});
case MusicType.Radio:
- return new Song(new SongInfo {
+ return new Song(new SongInfo
+ {
Uri = query,
Title = $"{query}",
Provider = "Radio Stream",
ProviderType = musicType,
});
}
- if (SoundCloud.Default.IsSoundCloudLink(query)) {
+ if (SoundCloud.Default.IsSoundCloudLink(query))
+ {
var svideo = await SoundCloud.Default.GetVideoAsync(query);
- return new Song(new SongInfo {
+ return new Song(new SongInfo
+ {
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = svideo.StreamLink,
@@ -310,76 +247,99 @@ namespace NadekoBot.Classes.Music {
if (video == null) // do something with this error
throw new Exception("Could not load any video elements based on the query.");
- return new Song(new SongInfo {
+ return new Song(new SongInfo
+ {
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
Provider = "YouTube",
Uri = video.Uri,
ProviderType = musicType,
});
- } catch (Exception ex) {
+ }
+ catch (Exception ex)
+ {
Console.WriteLine($"Failed resolving the link.{ex.Message}");
return null;
}
}
- private static async Task HandleStreamContainers(string query) {
+ private static async Task HandleStreamContainers(string query)
+ {
string file = null;
- try {
+ try
+ {
file = await SearchHelper.GetResponseStringAsync(query);
- } catch {
+ }
+ catch
+ {
return query;
}
- if (query.Contains(".pls")) {
+ if (query.Contains(".pls"))
+ {
//File1=http://armitunes.com:8000/
//Regex.Match(query)
- try {
+ try
+ {
var m = Regex.Match(file, "File1=(?.*?)\\n");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
- } catch {
+ }
+ catch
+ {
Console.WriteLine($"Failed reading .pls:\n{file}");
return null;
}
}
- if (query.Contains(".m3u")) {
+ if (query.Contains(".m3u"))
+ {
/*
# This is a comment
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
*/
- try {
+ try
+ {
var m = Regex.Match(file, "(?^[^#].*)", RegexOptions.Multiline);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
- } catch {
+ }
+ catch
+ {
Console.WriteLine($"Failed reading .m3u:\n{file}");
return null;
}
}
- if (query.Contains(".asx")) {
+ if (query.Contains(".asx"))
+ {
//
- try {
+ try
+ {
var m = Regex.Match(file, "[.*?)\"");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
- } catch {
+ }
+ catch
+ {
Console.WriteLine($"Failed reading .asx:\n{file}");
return null;
}
}
- if (query.Contains(".xspf")) {
+ if (query.Contains(".xspf"))
+ {
/*
*/
- try {
+ try
+ {
var m = Regex.Match(file, "(?.*?)");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
- } catch {
+ }
+ catch
+ {
Console.WriteLine($"Failed reading .xspf:\n{file}");
return null;
}
diff --git a/NadekoBot/Commands/TriviaCommand.cs b/NadekoBot/Commands/TriviaCommand.cs
index c2398e81..865361f5 100644
--- a/NadekoBot/Commands/TriviaCommand.cs
+++ b/NadekoBot/Commands/TriviaCommand.cs
@@ -1,35 +1,41 @@
-using System;
-using System.Threading.Tasks;
-using Discord.Commands;
-using System.Collections.Concurrent;
-using Discord;
+using Discord.Commands;
using NadekoBot.Modules;
+using System;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
using TriviaGame = NadekoBot.Classes.Trivia.TriviaGame;
-namespace NadekoBot.Commands {
- internal class Trivia : DiscordCommand {
+namespace NadekoBot.Commands
+{
+ internal class Trivia : DiscordCommand
+ {
public static ConcurrentDictionary RunningTrivias = new ConcurrentDictionary();
- public Func DoFunc() => async e => {
+ public Func DoFunc() => async e =>
+ {
TriviaGame trivia;
- if (!RunningTrivias.TryGetValue(e.Server.Id, out trivia)) {
+ if (!RunningTrivias.TryGetValue(e.Server.Id, out trivia))
+ {
var triviaGame = new TriviaGame(e);
if (RunningTrivias.TryAdd(e.Server.Id, triviaGame))
await e.Channel.SendMessage("**Trivia game started!**\nFirst player to get to 10 points wins! You have 30 seconds per question.\nUse command `tq` if game was started by accident.**");
else
await triviaGame.StopGame();
- } else
+ }
+ else
await e.Channel.SendMessage("Trivia game is already running on this server.\n" + trivia.CurrentQuestion);
};
- internal override void Init(CommandGroupBuilder cgb) {
+ internal override void Init(CommandGroupBuilder cgb)
+ {
cgb.CreateCommand(Module.Prefix + "t")
.Description("Starts a game of trivia.")
.Do(DoFunc());
cgb.CreateCommand(Module.Prefix + "tl")
.Description("Shows a current trivia leaderboard.")
- .Do(async e=> {
+ .Do(async e =>
+ {
TriviaGame trivia;
if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
await e.Channel.SendMessage(trivia.GetLeaderboard());
@@ -39,15 +45,18 @@ namespace NadekoBot.Commands {
cgb.CreateCommand(Module.Prefix + "tq")
.Description("Quits current trivia after current question.")
- .Do(async e=> {
+ .Do(async e =>
+ {
TriviaGame trivia;
- if (RunningTrivias.TryGetValue(e.Server.Id, out trivia)) {
+ if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
+ {
await trivia.StopGame();
- } else
+ }
+ else
await e.Channel.SendMessage("No trivia is running on this server.");
});
}
- public Trivia(DiscordModule module) : base(module) {}
+ public Trivia(DiscordModule module) : base(module) { }
}
}
]