Merge pull request #593 from Nitix/audio
SongBuffer is now a stream, and the Song will now wait correctly
This commit is contained in:
commit
e64dd6548f
@ -41,7 +41,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
return $"【{(int)time.TotalMinutes}m {time.Seconds}s】";
|
||||
}
|
||||
|
||||
const int milliseconds = 10;
|
||||
const int milliseconds = 20;
|
||||
const int samplesPerFrame = (48000 / 1000) * milliseconds;
|
||||
const int frameBytes = 3840; //16-bit, 2 channels
|
||||
|
||||
@ -84,10 +84,8 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
|
||||
|
||||
SongBuffer sb = new SongBuffer(filename, SongInfo, skipTo);
|
||||
var bufferTask = sb.BufferSong(cancelToken).ConfigureAwait(false);
|
||||
|
||||
var inStream = new FileStream(sb.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); ;
|
||||
SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo);
|
||||
var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
|
||||
|
||||
bytesSent = 0;
|
||||
|
||||
@ -95,7 +93,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
var attempt = 0;
|
||||
|
||||
var prebufferingTask = CheckPrebufferingAsync(inStream, sb, cancelToken);
|
||||
var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken);
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
|
||||
@ -112,9 +110,10 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
sw.Stop();
|
||||
_log.Debug("Prebuffering successfully completed in "+ sw.Elapsed);
|
||||
|
||||
|
||||
var outStream = voiceClient.CreatePCMStream(960);
|
||||
|
||||
int nextTime = Environment.TickCount + milliseconds;
|
||||
|
||||
byte[] buffer = new byte[frameBytes];
|
||||
while (!cancelToken.IsCancellationRequested)
|
||||
{
|
||||
@ -128,16 +127,9 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
if (read < frameBytes)
|
||||
{
|
||||
if (sb.IsNextFileReady())
|
||||
{
|
||||
inStream.Dispose();
|
||||
inStream = new FileStream(sb.GetNextFile(), FileMode.Open, FileAccess.Read, FileShare.Write);
|
||||
read += inStream.Read(buffer, read, buffer.Length - read);
|
||||
attempt = 0;
|
||||
}
|
||||
if (read == 0)
|
||||
{
|
||||
if (sb.BufferingCompleted)
|
||||
if (inStream.BufferingCompleted)
|
||||
break;
|
||||
if (attempt++ == 20)
|
||||
{
|
||||
@ -156,10 +148,14 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
while (this.MusicPlayer.Paused)
|
||||
await Task.Delay(200, cancelToken).ConfigureAwait(false);
|
||||
|
||||
//buffer = AdjustVolume(buffer, MusicPlayer.Volume);
|
||||
|
||||
buffer = AdjustVolume(buffer, MusicPlayer.Volume);
|
||||
if (read != frameBytes) continue;
|
||||
nextTime = unchecked(nextTime + milliseconds);
|
||||
int delayMillis = unchecked(nextTime - Environment.TickCount);
|
||||
if (delayMillis > 0)
|
||||
await Task.Delay(delayMillis, cancelToken).ConfigureAwait(false);
|
||||
await outStream.WriteAsync(buffer, 0, read);
|
||||
await Task.Delay(10);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@ -167,13 +163,12 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
await bufferTask;
|
||||
if(inStream != null)
|
||||
inStream.Dispose();
|
||||
sb.CleanFiles();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckPrebufferingAsync(Stream inStream, SongBuffer sb, CancellationToken cancelToken)
|
||||
private async Task CheckPrebufferingAsync(SongBuffer inStream, CancellationToken cancelToken)
|
||||
{
|
||||
while (!sb.BufferingCompleted && inStream.Length < 10.MiB())
|
||||
while (!inStream.BufferingCompleted && inStream.Length < 10.MiB())
|
||||
{
|
||||
await Task.Delay(100, cancelToken);
|
||||
}
|
||||
|
@ -15,23 +15,27 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
/// Create a buffer for a song file. It will create multiples files to ensure, that radio don't fill up disk space.
|
||||
/// It also help for large music by deleting files that are already seen.
|
||||
/// </summary>
|
||||
class SongBuffer
|
||||
class SongBuffer : Stream
|
||||
{
|
||||
|
||||
public SongBuffer(string basename, SongInfo songInfo, int skipTo)
|
||||
public SongBuffer(MusicPlayer musicPlayer, string basename, SongInfo songInfo, int skipTo)
|
||||
{
|
||||
MusicPlayer = musicPlayer;
|
||||
Basename = basename;
|
||||
SongInfo = songInfo;
|
||||
SkipTo = skipTo;
|
||||
CurrentFileStream = new FileStream(this.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
|
||||
}
|
||||
|
||||
MusicPlayer MusicPlayer;
|
||||
|
||||
private string Basename;
|
||||
|
||||
private SongInfo SongInfo;
|
||||
|
||||
private int SkipTo;
|
||||
|
||||
private static int MAX_FILE_SIZE = 20.MiB();
|
||||
private static int MAX_FILE_SIZE = 2.MiB();
|
||||
|
||||
private long FileNumber = -1;
|
||||
|
||||
@ -41,6 +45,8 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
|
||||
private ulong CurrentBufferSize = 0;
|
||||
|
||||
private FileStream CurrentFileStream;
|
||||
|
||||
public Task BufferSong(CancellationToken cancelToken) =>
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
@ -122,7 +128,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
||||
/// Return the next file to read, and delete the old one
|
||||
/// </summary>
|
||||
/// <returns>Name of the file to read</returns>
|
||||
public string GetNextFile()
|
||||
private string GetNextFile()
|
||||
{
|
||||
string filename = Basename + "-" + NextFileToRead;
|
||||
|
||||
@ -139,12 +145,12 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
||||
return filename;
|
||||
}
|
||||
|
||||
public bool IsNextFileReady()
|
||||
private bool IsNextFileReady()
|
||||
{
|
||||
return NextFileToRead <= FileNumber;
|
||||
}
|
||||
|
||||
public void CleanFiles()
|
||||
private void CleanFiles()
|
||||
{
|
||||
for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
|
||||
{
|
||||
@ -155,5 +161,68 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
//Stream part
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override long Length => (long) CurrentBufferSize;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush() { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int read = CurrentFileStream.Read(buffer, offset, count);
|
||||
if(read < count)
|
||||
{
|
||||
if (!BufferingCompleted || IsNextFileReady())
|
||||
{
|
||||
CurrentFileStream.Dispose();
|
||||
CurrentFileStream = new FileStream(GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
|
||||
read += CurrentFileStream.Read(buffer, read + offset, count - read);
|
||||
}
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public new void Dispose()
|
||||
{
|
||||
CurrentFileStream.Dispose();
|
||||
MusicPlayer.SongCancelSource.Cancel();
|
||||
CleanFiles();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user