@@ -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