App-MHFS
view release on metacpan or search on metacpan
share/public_html/static/music_worklet_inprogress/player/mhfsplayer.js view on Meta::CPAN
}
// runs gui updates
const UpdateTrack = function(bDraw) {
// determine if a queue update or draw needs to happen
let toDelete = 0;
for(let i = 0; i < that.AudioQueue.length; i++) {
const aqitem = that.AudioQueue[i];
// mark track as started
if(aqitem.needsstart && (aqitem._starttime <= that.ac.currentTime)) {
aqitem.needsstart = 0;
bDraw = 1;
}
// nothing more to do if track hasn't ended
if( (!aqitem.queued) || (aqitem.endTime && (aqitem.endTime > that.ac.currentTime))) {
break;
}
// mark ended track
toDelete++;
}
let QueueUpdate = {};
// draw if we started loading
if(that.AudioQueue[0] && that.AudioQueue[0].startedLoading) {
bDraw = 1;
that.AudioQueue[0].startedLoading = undefined;
QueueUpdate.trackstate = 'loading';
QueueUpdate.curtime = that.AudioQueue[0].skiptime;
}
// perform queue update
let track;
if(toDelete) {
bDraw = 1;
const lastTrack = that.AudioQueue[that.AudioQueue.length-1].track;
that.AudioQueue.splice(0, toDelete);
QueueUpdate.trackended = 1;
if(that.AudioQueue.length === 0) {
track = lastTrack;
QueueUpdate.trackstate = 'ended';
that.ac.suspend();
}
}
// no update occured, no need to draw
if(!bDraw) {
return;
}
// determine the current track if still unknown
track ||= that.AudioQueue[0].track;
that.playlistCursor = track;
QueueUpdate.track = track;
// show the track
that.gui.OnQueueUpdate(QueueUpdate);
}
// passes in the dest array, the maximum frames to read and when they will be played
const ReadAudioQueue = function (dest, count, when) {
UpdateTrack();
let framesWritten = 0;
let destoffset = 0;
for(let i = 0; that.AudioQueue[i]; i++) {
const item = that.AudioQueue[i];
if(item.queued) continue;
if(item.sampleCount === 0) break;
const toread = Math.min(count, item.sampleCount);
that.decoderdatareader.read(dest, toread, destoffset);
framesWritten += toread;
item.sampleCount -= toread;
ProcessTimes(item, toread, when);
item.queued = item.donedecode && (item.sampleCount === 0);
if(!item.queued) break;
count -= toread;
if(count === 0) break;
destoffset = framesWritten;
when += (toread / that.sampleRate);
}
return framesWritten;
};
// The Audio Pump
const PumpAudioData = [];
for(let i = 0; i < that.channels; i++) {
PumpAudioData[i] = new Float32Array(that.ARBLen);
}
const PumpAudioZeros = [];
for(let i = 0; i < that.channels; i++) {
PumpAudioZeros[i] = new Float32Array(that.ARBLen);
}
const PumpAudio = async function() {
while(1) {
do {
let bufferedTime = that._ab.gettime();
const mindelta = 0.1;
let space = that._ab.getspace();
if(space === 0) break;
// ensure we are queuing at least 100 ms in advance
if(bufferedTime < mindelta) {
const bufferFrames = 0.1 * that.sampleRate;
const towrite = Math.min(bufferFrames, space);
that._ab.write(PumpAudioZeros, towrite);
space -= towrite;
if(space === 0) break;
bufferedTime += (towrite / that.sampleRate);
}
const towrite = ReadAudioQueue(PumpAudioData, space, bufferedTime + that.ac.currentTime);
if(towrite > 0) {
that._ab.write(PumpAudioData, towrite);
}
} while(0);
const mysignal = that.FACAbortController.signal;
await abortablesleep(50, mysignal);
}
};
// Audio queuing / decoding
that.STATES = {
'NEED_FAQ' : 0,
'FAQ_RUNNING': 1
};
that.QState = that.STATES.NEED_FAQ;
that.Tracks_HEAD;
that.Tracks_TAIL;
that.playlistCursor;
const getNextTrack = function(currentTrack) {
if(that.pborder === "pborder_rptrack") {
return currentTrack;
}
else if(that.pborder === "pborder_rpplaylist") {
return currentTrack.next ? currentTrack.next : that.Tracks_HEAD;
}
else if(that.pborder === "pborder_random") {
// count the tracks
let tcount = 0;
for(let track = that.Tracks_HEAD; track; track = track.next) {
share/public_html/static/music_worklet_inprogress/player/mhfsplayer.js view on Meta::CPAN
const pbtrack = {
'track' : track,
'skiptime' : start_output_time,
'sampleCount' : 0,
'startedLoading' : 1
};
that.AudioQueue.push(pbtrack)
time = 0;
try {
const md = await decoder.openTrack(mysignal, track.md, start_output_time);
if(md) {
track.md.duration ??= md.duration;
track.md.mediametadata ??= md.mediametadata;
}
}
catch(error) {
pbtrack.startedLoading = undefined;
pbtrack.donedecode = 1;
pbtrack.queued = 1;
console.error(error);
if(mysignal.aborted) {
break;
}
if(firstFailedTrack === track) {
console.error("FAQ done, encountered same track failing again");
break;
}
firstFailedTrack ||= track;
continue;
}
firstFailedTrack = undefined;
// We better not modify the AQ if we're cancelled
if(mysignal.aborted) break;
// art
LoadTrackArt(track, decoder.track);
// decode the track
const todec = that.ac.sampleRate;
SAMPLELOOP: while(1) {
// yield so buffers can be queued
if(pbtrack.sampleCount > 0) {
if(!(await abortablesleep_status(0, mysignal)))
{
break TRACKLOOP;
}
}
// wait for there to be space
while(that.decoderdatawriter.getspace() < that.ac.sampleRate) {
if(!(await abortablesleep_status(250, mysignal)))
{
break TRACKLOOP;
}
}
// decode
let decdata;
try {
decdata = await decoder.read_pcm_frames_f32_arrs(todec, mysignal);
pbtrack.startedLoading = undefined;
if(!decdata) break SAMPLELOOP;
}
catch(error) {
console.error(error);
if(mysignal.aborted) {
break TRACKLOOP;
}
await decoder.closeCurrentTrack();
break SAMPLELOOP;
}
// We better not modify the AQ if we're cancelled
if(mysignal.aborted) break TRACKLOOP;
pbtrack.sampleCount += decdata.length;
that.decoderdatawriter.write(decdata.chanData);
// break out at end
if(decdata.length < todec) {
break SAMPLELOOP;
}
}
pbtrack.donedecode = 1;
pbtrack.queued = (pbtrack.sampleCount === 0);
}
decoder.flush();
unlock();
that.QState = that.STATES.NEED_FAQ;
}
let FAQPromise;
const StartQueue = function(track, time) {
FAQPromise = fillAudioQueue(track, time);
};
const StopQueue = async function() {
that.FACAbortController.abort();
await FAQPromise;
}
that.StartQueue = StartQueue;
that.StopQueue = StopQueue;
// Main playlist queuing. must be done when holding the USERMUTEX
that.USERMUTEX = new Mutex();
const Track = function(trackname) {
that.trackdb[trackname] ??= {'trackname' : trackname, 'url' : that.gui.geturl(trackname)}
return { md : that.trackdb[trackname]};
};
that._queuetracks = function(tracknames, after) {
// build a linked list of tracks to append
const HEAD = Track(tracknames[0]);
let TAIL = HEAD;
for(let i = 1; i < tracknames.length; i++) {
const track = Track(tracknames[i]);
TAIL.next = track;
track.prev = TAIL;
TAIL = track;
( run in 0.498 second using v1.01-cache-2.11-cpan-e1769b4cff6 )