tau_web
Version:
The Tau Engine for Web App
417 lines (344 loc) • 18.4 kB
JavaScript
/*
* Copyright 2021 Canardoux.
*
* This file is part of the τ Sound project.
*
* τ Sound is free software: you can redistribute it and/or modify
* it under the terms of the GNU Public License version 3 (GPL3.0),
* as published by the Free Software Foundation.
*
* τ Sound is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This Source Code Form is subject to the terms of the GNU Public
* License, v. 3.0. If a copy of the GPL was not distributed with this
* file, You can obtain one at https://www.gnu.org/licenses/.
*/
const PLAYER_VERSION = '8.2.0'
function newPlayerInstance(aCallback, callbackTable) { return new TauCorePlayer(aCallback, callbackTable); }
const IS_PLAYER_STOPPED = 0;
const IS_PLAYER_PLAYING = 1;
const IS_PLAYER_PAUSED = 2;
const CB_updateProgress = 0;
const CB_pause = 1;
const CB_resume = 2;
const CB_skipBackward = 3;
const CB_skipForward = 4;
const CB_updatePlaybackState = 5;
const CB_needSomeFood = 6;
const CB_audioPlayerFinished = 7;
const CB_startPlayerCompleted = 8;
const CB_pausePlayerCompleted = 9;
const CB_resumePlayerCompleted = 10;
const CB_stopPlayerCompleted = 11;
const CB_openPlayerCompleted = 12;
const CB_closePlayerCompleted = 13;
const CB_player_log = 14;
var instanceNumber = 1;
class TauCorePlayer {
static newInstance(aCallback, callbackTable) { return new FlutterSoundPlayer(aCallback, callbackTable); }
constructor(aCallback, callbackTable) {
this.callback = aCallback;
this.callbackTable = callbackTable;
this.howl = null;
this.temporaryBlob = null;
this.status = IS_PLAYER_STOPPED;
//this.deltaTime = 0;
this.subscriptionDuration = 0;
this.duration = 0;
this.instanceNo = instanceNumber;
this.callbackTable[CB_player_log](this.callback, DBG, 'Instance Number : ' + this.instanceNo.toString())
++instanceNumber;
}
initializeMediaPlayer(focus, category, mode, audioFlags, device, withUI) {
//this.callback.openAudioSessionCompleted(true);
this.status = IS_PLAYER_STOPPED;
this.callbackTable[CB_openPlayerCompleted](this.callback, this.getPlayerState(), true);
return this.getPlayerState();
}
releaseMediaPlayer() {
this.status = IS_PLAYER_STOPPED;
this.callbackTable[CB_closePlayerCompleted](this.callback, this.getPlayerState(), true);
return this.getPlayerState();
}
playAudioFromURL(path, codec) {
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: ---> playAudioFromURL : ' + path);
var me = this;
var howl = new Howl
({
src: [path],
format: tabFormat[codec],
onload: function () {
me.callbackTable[CB_player_log](me.callback, DBG, 'onload');
},
onplay: function () {
me.callbackTable[CB_player_log](me.callback, DBG, 'onplay');
me.duration = Math.ceil(howl.duration() * 1000);
me.status = IS_PLAYER_PLAYING;
if (me.pauseResume != IS_PLAYER_PAUSED) {
me.callbackTable[CB_startPlayerCompleted](me.callback, me.getPlayerState(), true, me.duration); // Duration is unknown
} else {
me.callbackTable[CB_resumePlayerCompleted](me.callback, me.getPlayerState(), true);
}
me.startTimer();
},
onplayerror: function () {
me.callbackTable[CB_player_log](me.callback, ERROR, 'onplayerror');
me.stop();
},
onend: function () {
me.callbackTable[CB_player_log](me.callback, DBG, 'onend');
me.stop();
me.status = IS_PLAYER_STOPPED;
me.callbackTable[CB_audioPlayerFinished](me.callback, me.getPlayerState());
},
onloaderror: function () {
me.callbackTable[CB_player_log](me.callback, ERROR, 'onloaderror');
me.stop()
},
onpause: function () {
me.callbackTable[CB_player_log](me.callback, DBG, 'onpause');
me.status = IS_PLAYER_PAUSED;
me.callbackTable[CB_pausePlayerCompleted](me.callback, me.getPlayerState(), true);
},
onstop: function () {
me.callbackTable[CB_player_log](me.callback, DBG, 'onstop');
me.status = IS_PLAYER_STOPPED;
me.howl = null;
me.callbackTable[CB_stopPlayerCompleted](me.callback, me.getPlayerState(), true);
},
onseek: function () {
//me.callbackTable[CB_player_log](me.callback, DBG, 'onseek');
},
});
this.howl = howl;
if (this.latentVolume != null && this.latentVolume >= 0)
this.howl.volume(this.latentVolume);
if (this.latentSpeed != null && this.latentSpeed >= 0)
this.howl.rate(this.latentSpeed);
if (this.latentSeek != null && this.latentSeek >= 0)
this.seekToPlayer(this.latentSeek);
this.pauseResume = IS_PLAYER_PLAYING;
howl.play();
//this.callback.startPlayerCompleted(howl.duration());
//this.status = IS_PLAYER_PLAYING; // Not very good : in fact the player is not really yet playing
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: <--- playAudioFromURL');
return this.getPlayerState();
}
/* ACTUALLY NOT USED
playAudioFromBuffer(dataBuffer) // Actually not used
{
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var source = audioCtx.createBufferSource();
me.callbackTable[CB_player_log](me.callback, DBG, dataBuffer.constructor.name)
audioCtx.decodeAudioData
(
dataBuffer, //dataBuffer.buffer,
function(buffer)
{
source.buffer = buffer;
source.connect(audioCtx.destination);
source.loop = false;
// start the source playing
source.start();
},
function(e){ me.callbackTable[CB_player_log](me.callback, DBG, "Error with decoding audio data" + e.err); }
);
this.callback.startPlayerCompleted(777);
return 0; // playAudioFromBuffer() does not support sound Duration
}
*/
setAudioFocus(focus, category, mode, audioFlags, device,) {
return this.getPlayerState();
}
isDecoderSupported(codec,) {
return true; // TODO
}
setSubscriptionDuration(duration) {
this.callbackTable[CB_player_log](this.callback, DBG, 'setSubscriptionDuration');
this.subscriptionDuration = duration;
if (duration > 0 && this.howl != null)
this.startTimer();
return this.getPlayerState();
}
getRecordURL(path,) {
var myStorage;
if ((path == null) || (path == '')) {
return null;
}
if (path.includes("/"))
return path;
if (path.substring(0, 1) == '/') {
myStorage = window.localStorage;
this.callbackTable[CB_player_log](this.callback, DBG, 'localStorage');
} else {
myStorage = window.sessionStorage;
this.callbackTable[CB_player_log](this.callback, DBG, 'sessionStorage');
}
var url = myStorage.getItem(path);
return url
}
startPlayer(codec, fromDataBuffer, fromURI, numChannels, sampleRate) {
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: ---> startPlayer');
this.stop();
if (this.temporaryBlob != null) {
URL.revokeObjectURL(this.temporaryBlob);
this.temporaryBlob = null;
}
if (fromDataBuffer != null) {
this.callbackTable[CB_player_log](this.callback, DBG, 'startPlayer : ' + fromDataBuffer.constructor.name);
var anArray = [fromDataBuffer]; // new Array(fromDataBuffer);
// return this.playAudioFromBuffer(fromDataBuffer.buffer); // playAudioFromBuffer() is ctually not used
var blob = new Blob(anArray, { 'type': mime_types[codec] });
fromURI = URL.createObjectURL(blob);
this.temporaryBlob = fromURI;
}
if (fromURI == null || fromURI == '') {
fromURI = lastUrl;
this.callbackTable[CB_player_log](this.callback, DBG, 'Playing lastUrl : ' + lastUrl);
}
this.callbackTable[CB_player_log](this.callback, DBG, 'startPlayer : ' + fromURI);
var url = this.getRecordURL(fromURI);
if (url != null) {
this.callbackTable[CB_player_log](this.callback, DBG, 'startPlayer : ' + url.constructor.name);
fromURI = url;
}
//this.deltaTime = 0;
this.pauseResume = IS_PLAYER_PLAYING; // Maybe too early
this.playAudioFromURL(url, codec);
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: <--- startPlayer');
return this.getPlayerState();
}
feed(data,) {
return this.getPlayerState();
}
startPlayerFromTrack(progress, duration, track, canPause, canSkipForward, canSkipBackward, defaultPauseResume, removeUIWhenStopped,) {
return 0; // TODO
}
nowPlaying(progress, duration, track, canPause, canSkipForward, canSkipBackward, defaultPauseResume,) {
return this.getPlayerState();
}
stop() {
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: ---> stop');
this.stopTimer();
if (this.temporaryBlob != null)
URL.revokeObjectURL(this.temporaryBlob);
this.temporaryBlob = null;
if (this.howl != null) {
this.howl.stop();
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: <--- stop');
return true;
}
else {
this.status = IS_PLAYER_STOPPED; // Maybe too early ?
//this.callbackTable[CB_stopPlayerCompleted](this.callback, IS_PLAYER_STOPPED, true);
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: <--- stop');
return false;
}
}
stopPlayer() {
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: ---> stopPlayer');
//if (this.howl == null)
//this.callbackTable[CB_stopPlayerCompleted](this.callback, IS_PLAYER_STOPPED, true);
if (!this.stop())
this.callbackTable[CB_stopPlayerCompleted](this.callback, this.getPlayerState(), true);
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: <--- stopPlayer');
return this.getPlayerState();
}
getPlayerState() {
if (this.howl == null) {
this.status = IS_PLAYER_STOPPED;
}
return this.status;
}
pausePlayer() {
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: ---> pausePlayer');
this.stopTimer();
if (this.getPlayerState() == IS_PLAYER_PLAYING) {
//this.status = IS_PLAYER_PAUSED; // Maybe too early
this.howl.pause();
} else {
this.callbackTable[CB_pausePlayerCompleted](this.callback, this.getPlayerState(), false);
}
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: <--- pausePlayer');
return this.getPlayerState();
}
resumePlayer() {
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: ---> resumePlayer');
if (this.getPlayerState() == IS_PLAYER_PAUSED) {
//this.status = IS_PLAYER_PLAYING; // Maybe too early
this.pauseResume = IS_PLAYER_PAUSED;
this.howl.play();
} else {
this.callbackTable[CB_resumePlayerCompleted](this.callback, this.getPlayerState(), false);
}
this.startTimer();
this.callbackTable[CB_player_log](this.callback, DBG, 'JS: <--- resumePlayer');
return this.getPlayerState();
}
seekToPlayer(duration) {
this.callbackTable[CB_player_log](this.callback, DBG, '---> seekToPlayer()');
if (this.howl != null) {
this.latentSeek = 0;
this.countDownDate = new Date().getTime() - duration;
//this.deltaTime = 0;
this.howl.seek(duration / 1000);
} else
this.latentSeek = duration;
this.callbackTable[CB_player_log](this.callback, DBG, '<--- seekToPlayer()');
return this.getPlayerState();
}
setVolume(volume) {
this.callbackTable[CB_player_log](this.callback, DBG, '---> setVolume()');
this.latentVolume = volume;
if (this.howl != null)
this.howl.volume(volume);
this.callbackTable[CB_player_log](this.callback, DBG, '<--- setVolume()');
return this.getPlayerState();
}
setSpeed(speed) {
this.callbackTable[CB_player_log](this.callback, DBG, '---> setSpeed()');
this.latentSpeed = speed;
if (this.howl != null)
this.howl.rate(speed);
this.callbackTable[CB_player_log](this.callback, DBG, '<--- setSpeed()');
return this.getPlayerState();
}
setUIProgressBar(duration, progress) {
return this.getPlayerState();
}
startTimer() {
this.callbackTable[CB_player_log](this.callback, DBG, '---> startTimer()');
this.stopTimer();
var me = this;
if (this.subscriptionDuration > 0) {
this.countDownDate = new Date().getTime();
this.timerId = setInterval
(
function () {
//var now = new Date().getTime();
//var distance = now - me.countDownDate;
//distance += me.deltaTime;
var pos = Math.floor(me.howl.seek() * 1000);
if (pos > me.duration)
pos = me.duration;
me.callbackTable[CB_updateProgress](me.callback, pos/*me.deltaTime + distance*/, me.duration);
},
this.subscriptionDuration
);
}
this.callbackTable[CB_player_log](this.callback, DBG, '<--- startTimer()');
}
stopTimer() {
this.callbackTable[CB_player_log](this.callback, DBG, 'stopTimer()');
if (this.timerId != null) {
clearInterval(this.timerId);
//var now = new Date().getTime();
//var distance = now - this.countDownDate;
//this.deltaTime += distance;
this.timerId = null;
}
}
}