tau_web
Version:
The Tau Engine for Web App
439 lines (359 loc) • 21.2 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 RECORDER_VERSION = '8.2.0'
const IS_RECORDER_PAUSED = 1;
const IS_RECORDER_RECORDING = 2;
const IS_RECORDER_STOPPED = 0;
function newRecorderInstance(aCallback, callbackTable) { return new TauCoreRecorder(aCallback, callbackTable); }
const CB_updateRecorderProgress = 0;
const CB_recordingData = 1;
const CB_startRecorderCompleted = 2;
const CB_pauseRecorderCompleted = 3;
const CB_resumeRecorderCompleted = 4;
const CB_stopRecorderCompleted = 5;
const CB_openRecorderCompleted = 6;
const CB_closeRecorderCompleted = 7;
const CB_recorder_log = 8;
class TauCoreRecorder {
static newInstance(aCallback, callbackTable) { return new TauCoreRecorder(aCallback, callbackTable); }
constructor(aCallback, callbackTable) {
this.callback = aCallback;
this.callbackTable = callbackTable;
this.subscriptionDuration = 0;
this.timerId = null;
this.deltaTime = 0;
this.currentRecordPath = '';
this.localObjects = [];
this.instanceNo = instanceNumber;
this.callbackTable[CB_recorder_log](this.callback, DBG, 'Instance Number : ' + this.instanceNo.toString())
++instanceNumber;
}
initializeFlautoRecorder(focus, category, mode, audioFlags, device) {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'initializeFlautoRecorder');
this.callbackTable[CB_openRecorderCompleted](this.callback, IS_RECORDER_STOPPED, true);
}
deleteObjects() {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'JS:---> deleteObjects ');
for (var url in this.localObjects) {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'deleteRecord : ' + url);
this.deleteRecord(url);
}
this.callbackTable[CB_recorder_log](this.callback, DBG, 'JS:<--- deleteObjects ');
}
releaseFlautoRecorder() {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'JS:---> releaseFlautoRecorder');
this.stop();
this.deleteObjects();
this.localObjects = [];
this.callbackTable[CB_closeRecorderCompleted](this.callback, IS_RECORDER_STOPPED, true);
this.callbackTable[CB_recorder_log](this.callback, DBG, 'JS:<--- releaseFlautoRecorder');
}
setAudioFocus(focus, category, mode, audioFlags, device) {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'setAudioFocus');
}
isEncoderSupported(codec) {
/*
for (var i in mime_types)
{
}
*/
var r = MediaRecorder.isTypeSupported(mime_types[codec]);
if (r)
this.callbackTable[CB_recorder_log](this.callback, DBG, 'mime_types[codec] encoder is supported');
else
this.callbackTable[CB_recorder_log](this.callback, DBG, 'mime_types[codec] encoder is NOT supported');
return r;
}
setSubscriptionDuration(duration) {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'setSubscriptionDuration');
this.subscriptionDuration = duration;
if (this.mediaRecorder != null)
this.startTimer();
}
_deleteRecord(aPath,) {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'deleteRecord: ' + aPath);
if ((aPath == null) || (aPath == '')) {
path = lasturl;
} else {
var path = aPath;
}
var myStorage;
if (path.substring(0, 1) == '/') {
myStorage = window.localStorage;
this.callbackTable[CB_recorder_log](this.callback, DBG, 'localStorage');
} else {
myStorage = window.sessionStorage;
this.callbackTable[CB_recorder_log](this.callback, DBG, 'sessionStorage');
}
var oldUrl = myStorage.getItem(path);
if (oldUrl != null && oldUrl != '') {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'Deleting object : ' + oldUrl.toString());
URL.revokeObjectURL(oldUrl);
return true;
}
return false;
}
deleteRecord(path) {
this._deleteRecord(path);
var found = this.localObjects.findIndex(element => element == path);
if (found != null && found >= 0) {
this.callbackTable[CB_recorder_log](this.callback, DBG, "Found : " + found);
this.localObjects[found] = null;
}
}
setRecordURL(aPath, newUrl) {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'setRecordUrl: ' + aPath + ' <- ' + newUrl);
var path = aPath;
var myStorage;
if ((path == null) || (path == '')) {
return null;
}
if (path.substring(0, 1) == '/') {
myStorage = window.localStorage;
this.callbackTable[CB_recorder_log](this.callback, DBG, 'localStorage');
} else {
myStorage = window.sessionStorage;
this.callbackTable[CB_recorder_log](this.callback, DBG, 'sessionStorage');
}
var oldUrl = myStorage.getItem(path);
if (oldUrl != null && oldUrl != '') {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'Deleting object ' + ' : ' + oldUrl.toString());
URL.revokeObjectURL(oldUrl);
} else {
}
lastUrl = aPath;
myStorage.setItem(path, newUrl);
this.callbackTable[CB_recorder_log](this.callback, DBG, '<--- setRecordURL ( ' + path + ' ) : ' + newUrl);
}
getRecordURL(aPath,) {
this.callbackTable[CB_recorder_log](this.callback, DBG, '---> getRecordURL : ' + aPath);
var r = getRecordURL(aPath);
if (r == null)
r = ''; // stopRecorder does not like a null
this.callbackTable[CB_recorder_log](this.callback, DBG, '<--- getRecordURL :' + r);
return r;
}
async startRecorder(path, sampleRate, numChannels, bitRate, codec, toStream, audioSource) {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'startRecorder');
//var constraints = { audio: true};
//var chunks ;//= [];
var me = this;
this.currentRecordPath = path;
var chunks = [];
var mediaStream;
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
me.mediaStream = mediaStream;
//navigator.mediaDevices.getUserMedia(constraints).then
//(function(mediaStream)
//{
/*
var audioCtx = new AudioContext();
var source = audioCtx.createMediaStreamSource(mediaStream);
//var dest = new audioCtx.createMediaStreamDestination();
var offlineCtx = new OfflineAudioContext(2,44100*40,44100);
var sourceOfflineCtx = offlineCtx.createBufferSource();
//source.connect(sourceOfflineCtx);
//source.connect(offlineCtx.source);
//source.start();
offlineCtx.startRendering().then(function(renderedBuffer)
{
//this.caller.toto(chunks);
onStop(renderedBuffer);
}
).catch(function(err)
{
// Note: The promise should reject when startRendering is called a second time on an OfflineAudioContext
}
);
*/
// var buffer = audioCtx.createBuffer(numChannels, tenMinutes, sampleRate); // Play back ???
// source.connect(audioCtx.destination); // This ouput to speaker // TODO : not yet supported
// ===========================================================================
var options =
{
audioBitsPerSecond: bitRate,
mimeType: mime_types[codec]
}
var mediaRecorder = new MediaRecorder(mediaStream, options);
me.mediaRecorder = mediaRecorder;
if (toStream) // not yet implemented !
mediaRecorder.start(30); // 30 milliseconds for a chunk
else
mediaRecorder.start();
me.callbackTable[CB_recorder_log](me.callback, DBG, "recorder started : " + mediaRecorder.state);
mediaRecorder.ondataavailable = function (e) {
if (e.data) {
if (toStream) // not yet implemented !
{
me.callbackTable[CB_recordingData](me.callback, e.data);
}
if (path != null && path != '') {
me.callbackTable[CB_recorder_log](me.callback, DBG, 'On data available : ' + e.data.constructor.name);
chunks.push(e.data);
}
}
}
mediaRecorder.onstart = function (e) {
me.callbackTable[CB_recorder_log](me.callback, DBG, '---> mediaRecorder OnStart');
me.deltaTime = 0;
me.startTimer();
me.callbackTable[CB_recorder_log](me.callback, DBG, '<---mediaRecorder OnStart : ' + me.mediaRecorder.state);
me.callbackTable[CB_startRecorderCompleted](me.callback, IS_RECORDER_RECORDING, true);
}
mediaRecorder.onerror = function (e) {
me.callbackTable[CB_recorder_log](me.callback, DBG, "mediaRecorder OnError : " + e.error);
me.stopRecorder()
}
mediaRecorder.onpause = function (e) {
me.callbackTable[CB_recorder_log](me.callback, DBG, '---> mediaRecorder onpause');
me.callbackTable[CB_pauseRecorderCompleted](me.callback, IS_RECORDER_PAUSED, true);
me.callbackTable[CB_recorder_log](me.callback, DBG, '<--- mediaRecorder onpause');
}
mediaRecorder.onresume = function (e) {
me.callbackTable[CB_recorder_log](me.callback, DBG, '---> mediaRecorder onresume');
me.callbackTable[CB_resumeRecorderCompleted](me.callback, IS_RECORDER_RECORDING, true);
me.callbackTable[CB_recorder_log](me.callback, DBG, '<--- mediaRecorder onresume');
}
mediaRecorder.onstop = function (e) {
me.callbackTable[CB_recorder_log](me.callback, DBG, '---> mediaRecorder onstop');
var blob = new Blob(chunks, { 'type': mime_types[codec] });
var url = URL.createObjectURL(blob);
me.callbackTable[CB_recorder_log](me.callback, DBG, 'Instance Number : ' + me.instanceNo.toString())
me.setRecordURL(path, url);
var found = me.localObjects.findIndex(element => element == path);
if (found != null && found >= 0) {
me.callbackTable[CB_recorder_log](me.callback, DBG, "Found : " + found);
me.localObjects[found] = path;
} else {
me.callbackTable[CB_recorder_log](me.callback, DBG, "NOT FOUND! : " + path);
me.localObjects.push(path);
}
/*
var xhr = new XMLHttpRequest();
var blob;
var fileReader = new FileReader();
xhr.open("GET", url, true);
xhr.responseType = "arraybuffer";
xhr.addEventListener("load", function ()
{
if (xhr.status === 200)
{
// Create a blob from the response
blob = new Blob([xhr.response], {type: "audio/webm\;codecs=opus"});
// onload needed since Google Chrome doesn't support addEventListener for FileReader
fileReader.onload = function (evt)
{
// Read out file contents as a Data URL
var result = evt.target.result;
// Set image src to Data URL
//rhino.setAttribute("src", result);
// Store Data URL in localStorage
try
{
//localStorage.setItem("rhino", result);
onStop(result);
myStorage.setItem(path, JSON.stringify(result));
} catch (e)
{
}
};
// Load blob as Data URL
fileReader.readAsDataURL(blob);
}
}, false);
// Send XHR
xhr.send();
*/
chunks = null;///[];
me.callbackTable[CB_recorder_log](me.callback, DBG, 'recorder stopped');
me.mediaRecorder = null;
me.callbackTable[CB_stopRecorderCompleted](me.callback, IS_RECORDER_STOPPED, true, me.getRecordURL(path));
me.callbackTable[CB_recorder_log](me.callback, DBG, '<--- mediaRecorder onstop');
}
//});
}
stop() {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'JS:---> stop()');
this.stopTimer();
if (this.mediaRecorder != null) {
this.mediaRecorder.stop();
this.mediaRecorder = null;
}
if (this.mediaStream != null) {
this.mediaStream.getTracks().forEach(track => track.stop());
this.mediaStream = null;
}
this.callbackTable[CB_recorder_log](this.callback, DBG, 'JS:<--- stop()');
}
stopRecorder() {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'JS:---> stopRecorder');
this.stopTimer();
if (this.mediaRecorder != null) {
this.mediaRecorder.stop();
} else {
this.callbackTable[CB_stopRecorderCompleted](this.callback, IS_RECORDER_STOPPED, /*false*/true, this.getRecordURL(this.currentRecordPath));
}
this.mediaRecorder = null;
if (this.mediaStream != null) {
this.mediaStream.getTracks().forEach(track => track.stop());
this.mediaStream = null;
}
this.callbackTable[CB_recorder_log](this.callback, DBG, "JS:<--- stopRecorder");
}
pauseRecorder() {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'pauseRecorder');
this.mediaRecorder.pause();
this.stopTimer();
this.callbackTable[CB_recorder_log](this.callback, DBG, "recorder paused : " + this.mediaRecorder.state);
}
resumeRecorder() {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'resumeRecorder');
this.mediaRecorder.resume();
this.startTimer();
this.callbackTable[CB_recorder_log](this.callback, DBG, "recorder resumed : " + this.mediaRecorder.state);
}
startTimer() {
this.callbackTable[CB_recorder_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;
me.callbackTable[CB_updateRecorderProgress](me.callback, me.deltaTime + distance, 0);
},
this.subscriptionDuration
);
}
}
stopTimer() {
this.callbackTable[CB_recorder_log](this.callback, DBG, 'JS:---> stopTimer()');
if (this.timerId != null) {
clearInterval(this.timerId);
var now = new Date().getTime();
var distance = now - this.countDownDate;
this.deltaTime += distance;
this.timerId = null;
}
this.callbackTable[CB_recorder_log](this.callback, DBG, 'JS:<--- stopTimer()');
}
}