Skip to content

Commit f1b2006

Browse files
committed
pre-loading in processor (for wasm files)
1 parent 6de8e6a commit f1b2006

File tree

1 file changed

+65
-15
lines changed

1 file changed

+65
-15
lines changed

src/processor.js

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ if (!(typeof SepiaFW == "object")){
1717

1818
WebAudio.isStreamRecorderSupported = testStreamRecorderSupport(); //set once at start
1919
WebAudio.isNativeStreamResamplingSupported = true; //will be tested on first media-stream creation
20+
WebAudio.contentFetchTimeout = 8000; //used e.g. for WASM pre-loads etc.
2021

2122
WebAudio.defaultProcessorOptions = {
2223
moduleFolder: "modules",
@@ -218,7 +219,7 @@ if (!(typeof SepiaFW == "object")){
218219
}
219220
function getModuleInfo(module){
220221
//supports "string" (name) or "object" with options
221-
var moduleType, moduleName, moduleSetup;
222+
var moduleType, moduleName, moduleSetup, modulePreLoads;
222223
if (typeof module == "object"){
223224
//1: AudioWorklet, 2: Web Worker, 3: Script Processor (legacy), 4: Audio Node (legacy)
224225
if ((module.type && module.type == "worker") || module.isWorker){
@@ -232,12 +233,14 @@ if (!(typeof SepiaFW == "object")){
232233
}
233234
moduleName = module.name;
234235
moduleSetup = module.setup || module.settings;
236+
modulePreLoads = module.preLoad || {};
235237
}else{
236238
moduleType = 1;
237239
moduleName = module;
238240
moduleSetup = {};
241+
modulePreLoads = {};
239242
}
240-
return {moduleType: moduleType, moduleName: moduleName, moduleSetup: moduleSetup};
243+
return {moduleType: moduleType, moduleName: moduleName, moduleSetup: moduleSetup, modulePreLoads: modulePreLoads};
241244
}
242245

243246
//States
@@ -261,19 +264,49 @@ if (!(typeof SepiaFW == "object")){
261264

262265
//Add audio worklets
263266
function addModules(processNodes, sourceHasWorkletSupport, completeCallback){
267+
var startIndex = processNodes.length;
264268
if (options.modules && options.modules.length){
265269
var initInfo = new Array(options.modules.length);
266-
var n = options.modules.length;
267-
options.modules.forEach(function(module, i){
270+
var N = options.modules.length;
271+
options.modules.forEach(async function(module, i){
268272
addInitCondition("module-" + i);
269273

270274
var info = getModuleInfo(module);
271275
var moduleType = info.moduleType; //1: AudioWorklet, 2: Web Worker, 3: Script Processor (legacy), 4: Audio Node (legacy)
272276
var moduleName = info.moduleName;
273277
var moduleSetup = info.moduleSetup;
274278

279+
//pre-loads - NOTE: there might be room for optimizations here ...
280+
var preLoads = {};
281+
var preLoadKeys = Object.keys(info.modulePreLoads);
282+
await Promise.all(preLoadKeys.map(async function(plKey, j){
283+
var plPath = info.modulePreLoads[plKey]; //NOTE: this can be a string or an object ({type: 2, path: 'url'})
284+
var plType = 1; //1: text, 2: arraybuffer
285+
if (typeof plPath == "object"){
286+
plType = (plPath.type && (plPath.type == 2 || plPath.type.toLowerCase() == "arraybuffer"))? 2 : 1;
287+
plPath = plPath.path || plPath.url;
288+
}else if (plKey.indexOf("wasmFile") == 0){
289+
plType = 2;
290+
}
291+
try{
292+
var data;
293+
if (!plPath || plType > 2){
294+
throw {name: "PreLoadError", message: "Missing 'path' (url) or unsupported type (use 1=text or 2=arraybuffer)"};
295+
}else if (plType == 1){
296+
data = await textLoaderPromise(plPath);
297+
}else if (plType == 2){
298+
data = await arrayBufferLoaderPromise(plPath);
299+
}
300+
preLoads[plKey] = data;
301+
}catch (err){
302+
throw {name: "AddModuleError", message: ("Failed to pre-load data: " + plKey + " - name: " + moduleName), info: err};
303+
}
304+
}));
305+
275306
//add some context info
276307
var fullOptions = moduleSetup.options || {};
308+
fullOptions.preLoadResults = preLoads;
309+
277310
var thisProcessNode;
278311
function onMessage(event){
279312
if (!event || event.data == undefined){
@@ -286,7 +319,7 @@ if (!(typeof SepiaFW == "object")){
286319
moduleName: thisProcessNode.moduleName,
287320
moduleInfo: thisProcessNode.moduleInfo
288321
};
289-
if (--n == 0){
322+
if (--N == 0){
290323
completeCallback(initInfo);
291324
}
292325
}else if (event.data.moduleResponse){
@@ -297,12 +330,12 @@ if (!(typeof SepiaFW == "object")){
297330
if (event.data.moduleEvent){
298331
//EVENT
299332
moduleSetup.sendToModules.forEach(function(n){
300-
processNodes[n].sendToModule({ctrl: {action: "handle", data: event.data}});
333+
if (!processNodes[n].ignoreSendToModules) processNodes[n].sendToModule({ctrl: {action: "handle", data: event.data}});
301334
});
302335
}else{
303336
//PROCESS (default)
304337
moduleSetup.sendToModules.forEach(function(n){
305-
processNodes[n].sendToModule({ctrl: {action: "process", data: event.data}});
338+
if (!processNodes[n].ignoreSendToModules) processNodes[n].sendToModule({ctrl: {action: "process", data: event.data}});
306339
});
307340
}
308341
}
@@ -374,6 +407,7 @@ if (!(typeof SepiaFW == "object")){
374407
throw {name: "AddModuleError", message: "Unknown module type."};
375408
}
376409
thisProcessNode.moduleType = moduleType;
410+
thisProcessNode.ignoreSendToModules = false; //this is most useful for workers to prevent serialization if message is not processed anyway
377411
module.handle = thisProcessNode;
378412

379413
//adapt module to first non-worklet source?
@@ -395,8 +429,8 @@ if (!(typeof SepiaFW == "object")){
395429
}
396430
}
397431
}
398-
399-
processNodes.push(thisProcessNode);
432+
433+
processNodes[startIndex + i] = thisProcessNode;
400434
});
401435
}else{
402436
completeCallback([]);
@@ -428,6 +462,7 @@ if (!(typeof SepiaFW == "object")){
428462

429463
//Connect other modules (nodes/workers)?
430464
addModules(processNodes, sourceHasWorkletSupport, function(info){
465+
//complete the rest:
431466
modulesInitInfo = info;
432467
completeInitCondition("modulesSetup");
433468
thisProcessor.processNodes = processNodes;
@@ -1141,16 +1176,31 @@ if (!(typeof SepiaFW == "object")){
11411176
SepiaFW.files.fetch(fileUrl, successCallback, errorCallback, "arraybuffer");
11421177
}else{
11431178
//fallback
1144-
xmlHttpCallForArrayBuffer(fileUrl, successCallback, errorCallback);
1179+
xmlHttpCall('arraybuffer', fileUrl, successCallback, errorCallback);
11451180
}
11461181
}
1147-
1148-
//fallback for: SepiaFW.files.fetch(fileUrl, successCallback, errorCallback, "arraybuffer");
1149-
function xmlHttpCallForArrayBuffer(fileUrl, successCallback, errorCallback){
1182+
WebAudio.readFileAsText = function(fileUrl, successCallback, errorCallback){
1183+
if (SepiaFW && SepiaFW.files){
1184+
SepiaFW.files.fetch(fileUrl, successCallback, errorCallback); //default: text
1185+
}else{
1186+
xmlHttpCall('text', fileUrl, successCallback, errorCallback);
1187+
}
1188+
}
1189+
function arrayBufferLoaderPromise(url){
1190+
return new Promise(function(resolve, reject){
1191+
WebAudio.readFileAsBuffer(url, function(arraybuffer){ resolve(arraybuffer); }, function(err){ reject(err); });
1192+
});
1193+
}
1194+
function textLoaderPromise(url){
1195+
return new Promise(function(resolve, reject){
1196+
WebAudio.readFileAsText(url, function(text){ resolve(text); }, function(err){ reject(err); });
1197+
});
1198+
}
1199+
function xmlHttpCall(responseType, fileUrl, successCallback, errorCallback){
11501200
var request = new XMLHttpRequest();
11511201
request.open('GET', fileUrl);
1152-
request.responseType = 'arraybuffer';
1153-
request.timeout = 8000;
1202+
request.responseType = responseType; //'arraybuffer';
1203+
request.timeout = WebAudio.contentFetchTimeout;
11541204
request.onload = function(e){
11551205
if (request.status >= 200 && request.status < 300){
11561206
successCallback(request.response); //the arraybuffer is in request.response

0 commit comments

Comments
 (0)