using Amphora;
using System;
using System.Runtime.InteropServices.JavaScript;
namespace Susurrus.WebAudio
{
///
/// A streamed sound that hands the browser a quarter second at a time, which it schedules back to back. Every
/// chunk is decoded into the same array.
///
public class WebAudioStreamedSound : StreamedSound
{
///
/// The number of chunks kept scheduled ahead, so a pause of up to a second between updates is covered.
///
public const int ChunksAhead = 4;
private readonly byte[] chunk;
private readonly int bytesPerFrame;
///
/// Gets how many frames each chunk holds, which is about a quarter of a second.
///
public int ChunkFrames { get; }
public WebAudioStreamedSound(ISoundStream stream) : base(stream)
{
bytesPerFrame = ChannelCount * StreamFeed.BytesPerSample(stream.Format);
ChunkFrames = Math.Max(1, stream.SampleRate / 4);
chunk = new byte[ChunkFrames * bytesPerFrame];
}
///
/// Schedules chunks on the browser voice in until are
/// waiting, or the stream has nothing more to give. A failure, including a chunk the browser refuses, ends the
/// feeding.
///
public Status Refill(JSObject engine, int slot)
{
int queued = WebAudioInterop.StreamQueued(engine, slot);
while (!IsFed && queued < ChunksAhead)
{
StatusOr filled = Feed(new ByteView(chunk));
if (!filled.IsOk)
{
return filled.Status;
}
if (filled.Value == 0)
{
break;
}
try
{
WebAudioInterop.StreamQueue(engine, slot, chunk, filled.Value * bytesPerFrame,
Stream.SampleRate, ChannelCount, isFloat: Stream.Format == SampleFormat.Float32);
}
catch (JSException exception)
{
IsFed = true;
return WebAudioFailures.BufferRejected.Fail(exception.Message);
}
++queued;
}
return Status.Ok;
}
}
}