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】";
|
return $"【{(int)time.TotalMinutes}m {time.Seconds}s】";
|
||||||
}
|
}
|
||||||
|
|
||||||
const int milliseconds = 10;
|
const int milliseconds = 20;
|
||||||
const int samplesPerFrame = (48000 / 1000) * milliseconds;
|
const int samplesPerFrame = (48000 / 1000) * milliseconds;
|
||||||
const int frameBytes = 3840; //16-bit, 2 channels
|
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());
|
var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
|
||||||
|
|
||||||
SongBuffer sb = new SongBuffer(filename, SongInfo, skipTo);
|
SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo);
|
||||||
var bufferTask = sb.BufferSong(cancelToken).ConfigureAwait(false);
|
var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var inStream = new FileStream(sb.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); ;
|
|
||||||
|
|
||||||
bytesSent = 0;
|
bytesSent = 0;
|
||||||
|
|
||||||
@ -95,7 +93,7 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
var attempt = 0;
|
var attempt = 0;
|
||||||
|
|
||||||
var prebufferingTask = CheckPrebufferingAsync(inStream, sb, cancelToken);
|
var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken);
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
|
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
|
||||||
@ -112,9 +110,10 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
sw.Stop();
|
sw.Stop();
|
||||||
_log.Debug("Prebuffering successfully completed in "+ sw.Elapsed);
|
_log.Debug("Prebuffering successfully completed in "+ sw.Elapsed);
|
||||||
|
|
||||||
|
|
||||||
var outStream = voiceClient.CreatePCMStream(960);
|
var outStream = voiceClient.CreatePCMStream(960);
|
||||||
|
|
||||||
|
int nextTime = Environment.TickCount + milliseconds;
|
||||||
|
|
||||||
byte[] buffer = new byte[frameBytes];
|
byte[] buffer = new byte[frameBytes];
|
||||||
while (!cancelToken.IsCancellationRequested)
|
while (!cancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@ -128,16 +127,9 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
}
|
}
|
||||||
if (read < frameBytes)
|
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 (read == 0)
|
||||||
{
|
{
|
||||||
if (sb.BufferingCompleted)
|
if (inStream.BufferingCompleted)
|
||||||
break;
|
break;
|
||||||
if (attempt++ == 20)
|
if (attempt++ == 20)
|
||||||
{
|
{
|
||||||
@ -156,10 +148,14 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
while (this.MusicPlayer.Paused)
|
while (this.MusicPlayer.Paused)
|
||||||
await Task.Delay(200, cancelToken).ConfigureAwait(false);
|
await Task.Delay(200, cancelToken).ConfigureAwait(false);
|
||||||
|
|
||||||
//buffer = AdjustVolume(buffer, MusicPlayer.Volume);
|
|
||||||
|
buffer = AdjustVolume(buffer, MusicPlayer.Volume);
|
||||||
if (read != frameBytes) continue;
|
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 outStream.WriteAsync(buffer, 0, read);
|
||||||
await Task.Delay(10);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -167,13 +163,12 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
await bufferTask;
|
await bufferTask;
|
||||||
if(inStream != null)
|
if(inStream != null)
|
||||||
inStream.Dispose();
|
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);
|
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.
|
/// 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.
|
/// It also help for large music by deleting files that are already seen.
|
||||||
/// </summary>
|
/// </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;
|
Basename = basename;
|
||||||
SongInfo = songInfo;
|
SongInfo = songInfo;
|
||||||
SkipTo = skipTo;
|
SkipTo = skipTo;
|
||||||
|
CurrentFileStream = new FileStream(this.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MusicPlayer MusicPlayer;
|
||||||
|
|
||||||
private string Basename;
|
private string Basename;
|
||||||
|
|
||||||
private SongInfo SongInfo;
|
private SongInfo SongInfo;
|
||||||
|
|
||||||
private int SkipTo;
|
private int SkipTo;
|
||||||
|
|
||||||
private static int MAX_FILE_SIZE = 20.MiB();
|
private static int MAX_FILE_SIZE = 2.MiB();
|
||||||
|
|
||||||
private long FileNumber = -1;
|
private long FileNumber = -1;
|
||||||
|
|
||||||
@ -41,6 +45,8 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
|
|
||||||
private ulong CurrentBufferSize = 0;
|
private ulong CurrentBufferSize = 0;
|
||||||
|
|
||||||
|
private FileStream CurrentFileStream;
|
||||||
|
|
||||||
public Task BufferSong(CancellationToken cancelToken) =>
|
public Task BufferSong(CancellationToken cancelToken) =>
|
||||||
Task.Factory.StartNew(async () =>
|
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
|
/// Return the next file to read, and delete the old one
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Name of the file to read</returns>
|
/// <returns>Name of the file to read</returns>
|
||||||
public string GetNextFile()
|
private string GetNextFile()
|
||||||
{
|
{
|
||||||
string filename = Basename + "-" + NextFileToRead;
|
string filename = Basename + "-" + NextFileToRead;
|
||||||
|
|
||||||
@ -139,12 +145,12 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
|||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsNextFileReady()
|
private bool IsNextFileReady()
|
||||||
{
|
{
|
||||||
return NextFileToRead <= FileNumber;
|
return NextFileToRead <= FileNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CleanFiles()
|
private void CleanFiles()
|
||||||
{
|
{
|
||||||
for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
|
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 { }
|
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