@grame/faustwasm 0.4.5 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/COPYING.txt +520 -0
  2. package/README.md +4 -4
  3. package/assets/standalone/create-node.js +78 -0
  4. package/assets/standalone/faust-ui/index.css +8 -0
  5. package/assets/standalone/faust-ui/index.css.map +1 -1
  6. package/assets/standalone/faust-ui/index.js +56 -30
  7. package/assets/standalone/faust-ui/index.js.map +1 -1
  8. package/assets/standalone/faustwasm/index.d.ts +18 -10
  9. package/assets/standalone/faustwasm/index.js +81 -56
  10. package/assets/standalone/faustwasm/index.js.map +2 -2
  11. package/assets/standalone/index-template.html +17 -0
  12. package/assets/standalone/index-template.js +48 -0
  13. package/assets/standalone/index.html +4 -3
  14. package/assets/standalone/index.js +14 -71
  15. package/assets/standalone/manifest.json +5 -6
  16. package/assets/standalone/service-worker.js +51 -27
  17. package/dist/cjs/index.d.ts +18 -10
  18. package/dist/cjs/index.js +81 -56
  19. package/dist/cjs/index.js.map +2 -2
  20. package/dist/cjs-bundle/index.d.ts +18 -10
  21. package/dist/cjs-bundle/index.js +81 -56
  22. package/dist/cjs-bundle/index.js.map +2 -2
  23. package/dist/esm/index.d.ts +18 -10
  24. package/dist/esm/index.js +81 -56
  25. package/dist/esm/index.js.map +2 -2
  26. package/dist/esm-bundle/index.d.ts +18 -10
  27. package/dist/esm-bundle/index.js +81 -56
  28. package/dist/esm-bundle/index.js.map +2 -2
  29. package/fileutils.js +19 -21
  30. package/package.json +4 -2
  31. package/scripts/faust2wasm.js +10 -5
  32. package/src/FaustAudioWorkletNode.ts +18 -38
  33. package/src/FaustAudioWorkletProcessor.ts +28 -1
  34. package/src/FaustDspGenerator.ts +18 -9
  35. package/src/FaustFFTAudioWorkletProcessor.ts +27 -0
  36. package/src/FaustScriptProcessorNode.ts +5 -14
  37. package/src/copyWebStandaloneAssets.d.ts +2 -2
  38. package/src/copyWebStandaloneAssets.js +30 -19
  39. package/src/faust2wasmFiles.js +5 -5
  40. package/test/faustlive-wasm/faust-ui/index.css +8 -0
  41. package/test/faustlive-wasm/faust-ui/index.css.map +1 -1
  42. package/test/faustlive-wasm/faust-ui/index.js +56 -30
  43. package/test/faustlive-wasm/faust-ui/index.js.map +1 -1
  44. package/test/faustlive-wasm/faustwasm/index.d.ts +18 -10
  45. package/test/faustlive-wasm/faustwasm/index.js +81 -56
  46. package/test/faustlive-wasm/faustwasm/index.js.map +2 -2
  47. package/assets/standalone/index-poly.js +0 -234
  48. package/assets/standalone/service-worker-poly.js +0 -98
  49. package/assets/standalone/template-poly.html +0 -60
  50. package/assets/standalone/template.html +0 -50
  51. package/assets/standalone/template.js +0 -63
  52. package/assets/standalone/types.d.ts +0 -9
package/fileutils.js CHANGED
@@ -15,7 +15,10 @@ const __filename = fileURLToPath(import.meta.url);
15
15
  * @param {string} dest - The destination path.
16
16
  */
17
17
  const cpSync = (src, dest) => {
18
- if (!fs.existsSync(src)) return;
18
+ if (!fs.existsSync(src)) {
19
+ console.error(`${src} does not exist.`)
20
+ return;
21
+ }
19
22
  if (fs.lstatSync(src).isDirectory()) {
20
23
  if (!fs.existsSync(dest)) fs.mkdirSync(dest);
21
24
  fs.readdirSync(src).forEach(child => cpSync(path.join(src, child), path.join(dest, child)));
@@ -27,29 +30,24 @@ const cpSync = (src, dest) => {
27
30
  /**
28
31
  * @param {string} src - The source path of the file to modify and copy.
29
32
  * @param {string} dest - The destination path.
30
- * @param {string} find - The string to find.
31
- * @param {string} replace - The string to replace.
33
+ * @param {string[]} findAndReplace - The string to find and to replace pairs.
32
34
  */
33
- const cpSyncModify = (src, dest, find, replace) => {
34
- fs.readFile(src, 'utf8', function (err, data) {
35
- if (err) {
36
- console.error(err);
37
- return;
38
- }
39
-
35
+ const cpSyncModify = (src, dest, ...findAndReplace) => {
36
+ if (!fs.existsSync(src)) {
37
+ console.error(`${src} does not exist.`)
38
+ return;
39
+ }
40
+ let data = fs.readFileSync(src, "utf-8");
41
+ for (let i = 0; i < findAndReplace.length; i += 2) {
42
+ const find = findAndReplace[i];
43
+ const replace = findAndReplace[i + 1];
40
44
  // Create a regular expression from the 'find' string
41
- const regex = new RegExp(find, 'g');
42
-
45
+ const regex = new RegExp(find, "g");
43
46
  // Replace 'find' with 'replace'
44
- let modifiedData = data.replace(regex, replace);
45
-
46
- // Write the modified data to a new file
47
- fs.writeFile(dest, modifiedData, 'utf8', function (err) {
48
- if (err) {
49
- console.error(err);
50
- }
51
- });
52
- });
47
+ data = data.replace(regex, replace);
48
+ }
49
+ // Write the modified data to a new file
50
+ fs.writeFileSync(dest, data, "utf-8");
53
51
  }
54
52
  /**
55
53
  * @param {string} dir - The directory to remove.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grame/faustwasm",
3
- "version": "0.4.5",
3
+ "version": "0.4.8",
4
4
  "description": "WebAssembly version of Faust Compiler",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/esm/index.d.ts",
@@ -39,9 +39,11 @@
39
39
  "homepage": "https://github.com/grame-cncm/faustwasm#readme",
40
40
  "devDependencies": {
41
41
  "@aws-crypto/sha256-js": "^5.2.0",
42
- "@shren/faust-ui": "^1.1.11",
42
+ "@shren/faust-ui": "^1.1.12",
43
43
  "@types/node": "^20.12.7",
44
44
  "@types/webmidi": "^2.0.10",
45
+ "@webaudiomodules/api": "^2.0.0-alpha.6",
46
+ "@webaudiomodules/sdk-parammgr": "^0.0.13",
45
47
  "dts-bundle-generator": "^9.5.1",
46
48
  "esbuild": "^0.20.2",
47
49
  "typescript": "^5.4.5"
@@ -8,7 +8,7 @@ const argv = process.argv.slice(2);
8
8
 
9
9
  if (argv[0] === "-help" || argv[0] === "-h") {
10
10
  console.log(`
11
- faust2wasm.js <file.dsp> <outputDir> [-poly] [-standalone]
11
+ faust2wasm.js <file.dsp> <outputDir> [-poly] [-standalone] [-no-template]
12
12
  Generates WebAssembly and metadata JSON files of a given Faust DSP.
13
13
  `);
14
14
  process.exit();
@@ -22,15 +22,20 @@ const $standalone = argv.indexOf("-standalone");
22
22
  const standalone = $standalone !== -1;
23
23
  if (standalone) argv.splice($standalone, 1);
24
24
 
25
+ const $noTemplate = argv.indexOf("-no-template");
26
+ const noTemplate = $noTemplate !== -1;
27
+ if (noTemplate) argv.splice($noTemplate, 1);
28
+
25
29
  const [inputFile, outputDir, ...argvFaust] = argv;
26
30
  const fileName = inputFile.split('/').pop();
31
+ if (!fileName) throw new Error("No input DSP file");
27
32
  const dspName = fileName.replace(/\.dsp$/, '');
28
33
 
29
34
  (async () => {
30
- await faust2wasmFiles(inputFile, outputDir, argvFaust, poly);
35
+ const { dspMeta, effectMeta } = await faust2wasmFiles(inputFile, outputDir, argvFaust, poly);
31
36
  if (standalone) {
32
- copyWebStandaloneAssets(outputDir, dspName, poly);
33
- } else {
34
- copyWebTemplateAssets(outputDir, dspName, poly);
37
+ copyWebStandaloneAssets(outputDir, dspName, poly, !!effectMeta);
38
+ } else if (!noTemplate) {
39
+ copyWebTemplateAssets(outputDir, dspName, poly, !!effectMeta);
35
40
  }
36
41
  })();
@@ -18,7 +18,7 @@ export class FaustAudioWorkletNode<Poly extends boolean = false> extends (global
18
18
  #hasAccInput = false;
19
19
  #hasGyrInput = false;
20
20
 
21
- constructor(context: BaseAudioContext, name: string, factory: LooseFaustDspFactory, options: FaustAudioWorkletNodeOptions<Poly>["processorOptions"], nodeOptions: Partial<FaustAudioWorkletNodeOptions> = {}) {
21
+ constructor(context: BaseAudioContext, name: string, factory: LooseFaustDspFactory, options: Partial<FaustAudioWorkletNodeOptions<Poly>> = {}) {
22
22
 
23
23
  // Create JSON object
24
24
  const JSONObj: FaustDspMeta = JSON.parse(factory.json);
@@ -31,8 +31,8 @@ export class FaustAudioWorkletNode<Poly extends boolean = false> extends (global
31
31
  outputChannelCount: [JSONObj.outputs],
32
32
  channelCountMode: "explicit",
33
33
  channelInterpretation: "speakers",
34
- processorOptions: options,
35
- ...nodeOptions
34
+ processorOptions: options.processorOptions,
35
+ ...options
36
36
  });
37
37
 
38
38
  this.fJSONDsp = JSONObj;
@@ -76,20 +76,11 @@ export class FaustAudioWorkletNode<Poly extends boolean = false> extends (global
76
76
  async listenSensors() {
77
77
  if (this.hasAccInput) {
78
78
  const isAndroid: boolean = /Android/i.test(navigator.userAgent);
79
- let handleDeviceMotion = null;
80
- if (isAndroid) {
81
- handleDeviceMotion = ({ accelerationIncludingGravity }: DeviceMotionEvent) => {
82
- if (!accelerationIncludingGravity) return;
83
- const { x, y, z } = accelerationIncludingGravity;
84
- this.propagateAcc({ x, y, z }, true);
85
- }
86
- } else {
87
- handleDeviceMotion = ({ accelerationIncludingGravity }: DeviceMotionEvent) => {
88
- if (!accelerationIncludingGravity) return;
89
- const { x, y, z } = accelerationIncludingGravity;
90
- this.propagateAcc({ x, y, z });
91
- }
92
- }
79
+ const handleDeviceMotion = ({ accelerationIncludingGravity }: DeviceMotionEvent) => {
80
+ if (!accelerationIncludingGravity) return;
81
+ const { x, y, z } = accelerationIncludingGravity;
82
+ this.propagateAcc({ x, y, z }, isAndroid);
83
+ };
93
84
  if (window.DeviceMotionEvent) {
94
85
  if (typeof (window.DeviceMotionEvent as any).requestPermission === "function") { // for iOS 13+
95
86
  try {
@@ -156,6 +147,9 @@ export class FaustAudioWorkletNode<Poly extends boolean = false> extends (global
156
147
  getPlotHandler(): PlotHandler | null {
157
148
  return this.fPlotHandler;
158
149
  }
150
+ setupWamEventHandler() {
151
+ this.port.postMessage({ type: "setupWamEventHandler" });
152
+ }
159
153
 
160
154
  getNumInputs() {
161
155
  return this.fJSONDsp.inputs;
@@ -252,8 +246,8 @@ export class FaustMonoAudioWorkletNode extends FaustAudioWorkletNode<false> impl
252
246
  throw e;
253
247
  }
254
248
 
255
- constructor(context: BaseAudioContext, name: string, factory: LooseFaustDspFactory, sampleSize: number, nodeOptions: Partial<FaustAudioWorkletNodeOptions> = {}) {
256
- super(context, name, factory, { name, factory, sampleSize }, nodeOptions);
249
+ constructor(context: BaseAudioContext, options: Partial<FaustAudioWorkletNodeOptions<false>> & Pick<FaustAudioWorkletNodeOptions<false>, "processorOptions">) {
250
+ super(context, options.processorOptions.name, options.processorOptions.factory, options);
257
251
  }
258
252
  }
259
253
 
@@ -269,30 +263,16 @@ export class FaustPolyAudioWorkletNode extends FaustAudioWorkletNode<true> imple
269
263
  throw e;
270
264
  }
271
265
 
272
- constructor(context: BaseAudioContext,
273
- name: string,
274
- voiceFactory: LooseFaustDspFactory,
275
- mixerModule: WebAssembly.Module,
276
- voices: number,
277
- sampleSize: number,
278
- effectFactory?: LooseFaustDspFactory, nodeOptions: Partial<FaustAudioWorkletNodeOptions> = {}) {
266
+ constructor(context: BaseAudioContext, options: Partial<FaustAudioWorkletNodeOptions<true>> & Pick<FaustAudioWorkletNodeOptions<true>, "processorOptions">) {
279
267
 
280
268
  super(
281
269
  context,
282
- name,
283
- voiceFactory,
284
- {
285
- name,
286
- voiceFactory,
287
- mixerModule,
288
- voices,
289
- sampleSize,
290
- effectFactory
291
- },
292
- nodeOptions
270
+ options.processorOptions.name,
271
+ options.processorOptions.voiceFactory,
272
+ options
293
273
  );
294
274
 
295
- this.fJSONEffect = effectFactory ? JSON.parse(effectFactory.json) : null;
275
+ this.fJSONEffect = options.processorOptions.effectFactory ? JSON.parse(options.processorOptions.effectFactory.json) : null;
296
276
 
297
277
  if (this.fJSONEffect) {
298
278
  FaustBaseWebAudioDsp.parseUI(this.fJSONEffect.ui, this.fUICallback);
@@ -1,6 +1,7 @@
1
1
  import type FaustWasmInstantiator from "./FaustWasmInstantiator";
2
2
  import type { FaustBaseWebAudioDsp, FaustWebAudioDspVoice, FaustMonoWebAudioDsp, FaustPolyWebAudioDsp } from "./FaustWebAudioDsp";
3
- import type { AudioParamDescriptor, AudioWorkletGlobalScope, LooseFaustDspFactory, FaustDspMeta, FaustUIItem, AudioWorkletProcessor } from "./types";
3
+ import type { AudioParamDescriptor, AudioWorkletGlobalScope, LooseFaustDspFactory, FaustDspMeta, FaustUIItem } from "./types";
4
+ import type { AudioWorkletGlobalScope as WamAudioWorkletGlobalScope, WamParamMgrSDKBaseModuleScope } from "@webaudiomodules/sdk-parammgr";
4
5
 
5
6
  /**
6
7
  * Injected in the string to be compiled on AudioWorkletProcessor side
@@ -31,6 +32,9 @@ export interface FaustPolyAudioWorkletNodeOptions extends AudioWorkletNodeOption
31
32
  export interface FaustAudioWorkletProcessorOptions {
32
33
  name: string;
33
34
  sampleSize: number;
35
+ // for WAMs
36
+ moduleId?: string;
37
+ instanceId?: string;
34
38
  }
35
39
  export interface FaustMonoAudioWorkletProcessorOptions extends FaustAudioWorkletProcessorOptions {
36
40
  factory: LooseFaustDspFactory;
@@ -82,6 +86,8 @@ const getFaustAudioWorkletProcessor = <Poly extends boolean = false>(dependencie
82
86
 
83
87
  protected paramValuesCache: Record<string, number> = {};
84
88
 
89
+ protected wamInfo?: { moduleId: string; instanceId: string };
90
+
85
91
  constructor(options: FaustAudioWorkletNodeOptions<Poly>) {
86
92
  super(options);
87
93
 
@@ -92,6 +98,10 @@ const getFaustAudioWorkletProcessor = <Poly extends boolean = false>(dependencie
92
98
  parameterDescriptors.forEach((pd) => {
93
99
  this.paramValuesCache[pd.name] = pd.defaultValue || 0;
94
100
  })
101
+
102
+ const { moduleId, instanceId } = options.processorOptions;
103
+ if (!moduleId || !instanceId) return;
104
+ this.wamInfo = { moduleId, instanceId };
95
105
  }
96
106
 
97
107
  static get parameterDescriptors() {
@@ -107,6 +117,19 @@ const getFaustAudioWorkletProcessor = <Poly extends boolean = false>(dependencie
107
117
  return params;
108
118
  }
109
119
 
120
+ setupWamEventHandler() {
121
+ if (!this.wamInfo) return;
122
+ const { moduleId, instanceId } = this.wamInfo;
123
+ const { webAudioModules } = (globalThis as unknown as WamAudioWorkletGlobalScope);
124
+ const ModuleScope = webAudioModules.getModuleScope(moduleId) as WamParamMgrSDKBaseModuleScope;
125
+ const paramMgrProcessor = ModuleScope?.paramMgrProcessors?.[instanceId];
126
+ if (!paramMgrProcessor) return;
127
+ if (paramMgrProcessor.handleEvent) return;
128
+ paramMgrProcessor.handleEvent = (event) => {
129
+ if (event.type === "wam-midi") this.midiMessage(event.data.bytes);
130
+ };
131
+ }
132
+
110
133
  process(inputs: Float32Array[][], outputs: Float32Array[][], parameters: { [key: string]: Float32Array }) {
111
134
 
112
135
  // Update controls (possibly needed for sample accurate control)
@@ -162,6 +185,10 @@ const getFaustAudioWorkletProcessor = <Poly extends boolean = false>(dependencie
162
185
  }
163
186
  break;
164
187
  }
188
+ case "setupWamEventHandler": {
189
+ this.setupWamEventHandler();
190
+ break;
191
+ }
165
192
  case "start": {
166
193
  this.fDSPCode.start();
167
194
  break;
@@ -52,6 +52,7 @@ export interface IFaustMonoDspGenerator extends GeneratorSupportingSoundfiles {
52
52
  * @param sp - whether to compile a ScriptProcessorNode or an AudioWorkletNode
53
53
  * @param bufferSize - the buffer size in frames to be used in ScriptProcessorNode only, since AudioWorkletNode always uses 128 frames
54
54
  * @param processorName - AudioWorklet Processor name
55
+ * @param processorOptions - Additional AudioWorklet Processor options
55
56
  * @returns the compiled WebAudio node or 'null' if failure
56
57
  */
57
58
  createNode(
@@ -60,7 +61,8 @@ export interface IFaustMonoDspGenerator extends GeneratorSupportingSoundfiles {
60
61
  factory?: LooseFaustDspFactory,
61
62
  sp?: boolean,
62
63
  bufferSize?: number,
63
- processorName?: string
64
+ processorName?: string,
65
+ processorOptions?: Record<string, any>
64
66
  ): Promise<IFaustMonoWebAudioNode | null>;
65
67
 
66
68
  /**
@@ -72,6 +74,7 @@ export interface IFaustMonoDspGenerator extends GeneratorSupportingSoundfiles {
72
74
  * @param factory - default is the compiled factory
73
75
  * @param fftOptions - initial FFT options
74
76
  * @param processorName - AudioWorklet Processor name
77
+ * @param processorOptions - Additional AudioWorklet Processor options
75
78
  * @returns the compiled WebAudio node or 'null' if failure
76
79
  */
77
80
  createFFTNode(
@@ -80,7 +83,8 @@ export interface IFaustMonoDspGenerator extends GeneratorSupportingSoundfiles {
80
83
  name?: string,
81
84
  factory?: LooseFaustDspFactory,
82
85
  fftOptions?: Partial<FaustFFTOptionsData>,
83
- processorName?: string
86
+ processorName?: string,
87
+ processorOptions?: Record<string, any>
84
88
  ): Promise<FaustMonoAudioWorkletNode | null>;
85
89
 
86
90
  /**
@@ -148,6 +152,7 @@ export interface IFaustPolyDspGenerator extends GeneratorSupportingSoundfiles {
148
152
  * @param effectFactory - the Faust factory for the effect, either obtained with a compiler (createDSPFactory) or loaded from files (loadDSPFactory)
149
153
  * @param sp - whether to compile a ScriptProcessorNode or an AudioWorkletNode
150
154
  * @param bufferSize - the buffer size in frames to be used in ScriptProcessorNode only, since AudioWorkletNode always uses 128 frames
155
+ * @param processorOptions - Additional AudioWorklet Processor options
151
156
  * @returns the compiled WebAudio node or 'null' if failure
152
157
  */
153
158
  createNode(
@@ -159,7 +164,8 @@ export interface IFaustPolyDspGenerator extends GeneratorSupportingSoundfiles {
159
164
  effectFactory?: LooseFaustDspFactory | null,
160
165
  sp?: boolean,
161
166
  bufferSize?: number,
162
- processorName?: string
167
+ processorName?: string,
168
+ processorOptions?: Record<string, any>
163
169
  ): Promise<IFaustPolyWebAudioNode | null>;
164
170
 
165
171
  /**
@@ -244,7 +250,8 @@ export class FaustMonoDspGenerator implements IFaustMonoDspGenerator {
244
250
  factory = this.factory as LooseFaustDspFactory,
245
251
  sp = false as SP,
246
252
  bufferSize = 1024,
247
- processorName = factory?.shaKey || name
253
+ processorName = factory?.shaKey || name,
254
+ processorOptions: Record<string, any> = {}
248
255
  ): Promise<SP extends true ? FaustMonoScriptProcessorNode | null : FaustMonoAudioWorkletNode | null> {
249
256
  if (!factory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
250
257
 
@@ -307,7 +314,7 @@ const dependencies = {
307
314
  }
308
315
  }
309
316
  // Create the AWN
310
- const node = new FaustMonoAudioWorkletNode(context, processorName, factory, sampleSize);
317
+ const node = new FaustMonoAudioWorkletNode(context, { processorOptions: { name: processorName, factory, sampleSize, ...processorOptions } });
311
318
 
312
319
  return node as SP extends true ? FaustMonoScriptProcessorNode : FaustMonoAudioWorkletNode;
313
320
  }
@@ -319,7 +326,8 @@ const dependencies = {
319
326
  name = this.name,
320
327
  factory = this.factory as LooseFaustDspFactory,
321
328
  fftOptions: Partial<FaustFFTOptionsData> = {},
322
- processorName = factory?.shaKey ? `${factory.shaKey}_fft` : name
329
+ processorName = factory?.shaKey ? `${factory.shaKey}_fft` : name,
330
+ processorOptions: Record<string, any> = {}
323
331
  ): Promise<FaustMonoAudioWorkletNode | null> {
324
332
  if (!factory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
325
333
 
@@ -375,7 +383,7 @@ const dependencies = {
375
383
  }
376
384
  }
377
385
  // Create the AWN
378
- const node = new FaustMonoAudioWorkletNode(context, processorName, factory, sampleSize, { channelCount: Math.max(1, Math.ceil(meta.inputs / 3)), outputChannelCount: [Math.ceil(meta.outputs / 2)] });
386
+ const node = new FaustMonoAudioWorkletNode(context, { channelCount: Math.max(1, Math.ceil(meta.inputs / 3)), outputChannelCount: [Math.ceil(meta.outputs / 2)], processorOptions: { name: processorName, factory, sampleSize, ...processorOptions } });
379
387
  if (fftOptions.fftSize) {
380
388
  const param = node.parameters.get("fftSize");
381
389
  if (param) param.value = fftOptions.fftSize;
@@ -573,7 +581,8 @@ process = adaptorIns(dsp_code.process) : dsp_code.effect : adaptorOuts;
573
581
  effectFactory = this.effectFactory as LooseFaustDspFactory | null,
574
582
  sp = false as SP,
575
583
  bufferSize = 1024,
576
- processorName = ((voiceFactory?.shaKey || "") + (effectFactory?.shaKey || "")) || `${name}_poly`
584
+ processorName = ((voiceFactory?.shaKey || "") + (effectFactory?.shaKey || "")) || `${name}_poly`,
585
+ processorOptions = {}
577
586
  ): Promise<SP extends true ? FaustPolyScriptProcessorNode | null : FaustPolyAudioWorkletNode | null> {
578
587
  if (!voiceFactory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
579
588
 
@@ -642,7 +651,7 @@ const dependencies = {
642
651
  }
643
652
  }
644
653
  // Create the AWN
645
- const node = new FaustPolyAudioWorkletNode(context, processorName, voiceFactory, mixerModule, voices, sampleSize, effectFactory || undefined);
654
+ const node = new FaustPolyAudioWorkletNode(context, { processorOptions: { name: processorName, voiceFactory, mixerModule, voices, sampleSize, effectFactory: effectFactory || undefined, ...processorOptions } });
646
655
 
647
656
  return node as SP extends true ? FaustPolyScriptProcessorNode : FaustPolyAudioWorkletNode;
648
657
  }
@@ -2,6 +2,7 @@ import type { FaustMonoDspInstance } from "./FaustDspInstance";
2
2
  import type FaustWasmInstantiator from "./FaustWasmInstantiator";
3
3
  import type { FaustBaseWebAudioDsp, FaustMonoWebAudioDsp, PlotHandler } from "./FaustWebAudioDsp";
4
4
  import type { AudioParamDescriptor, AudioWorkletGlobalScope, LooseFaustDspFactory, FaustDspMeta, FaustUIItem, InterfaceFFT, TWindowFunction, Writeable, TypedArray, FFTUtils } from "./types";
5
+ import type { AudioWorkletGlobalScope as WamAudioWorkletGlobalScope, WamParamMgrSDKBaseModuleScope } from "@webaudiomodules/sdk-parammgr";
5
6
 
6
7
  export interface FaustFFTOptionsData {
7
8
  fftSize: number;
@@ -33,6 +34,9 @@ export interface FaustFFTAudioWorkletProcessorOptions {
33
34
  name: string;
34
35
  sampleSize: number;
35
36
  factory: LooseFaustDspFactory;
37
+ // for WAMs
38
+ moduleId?: string;
39
+ instanceId?: string;
36
40
  }
37
41
 
38
42
 
@@ -122,6 +126,8 @@ const getFaustFFTAudioWorkletProcessor = (dependencies: FaustFFTAudioWorkletProc
122
126
 
123
127
  protected paramValuesCache: Record<string, number> = {};
124
128
 
129
+ protected wamInfo?: { moduleId: string; instanceId: string };
130
+
125
131
  private dspInstance!: FaustMonoDspInstance;
126
132
  private sampleSize!: number;
127
133
 
@@ -187,6 +193,10 @@ const getFaustFFTAudioWorkletProcessor = (dependencies: FaustFFTAudioWorkletProc
187
193
 
188
194
  // Init the FFT constructor and the Faust FFT Processor
189
195
  this.initFFT();
196
+
197
+ const { moduleId, instanceId } = options.processorOptions;
198
+ if (!moduleId || !instanceId) return;
199
+ this.wamInfo = { moduleId, instanceId };
190
200
  }
191
201
 
192
202
  async initFFT(): Promise<true> {
@@ -232,6 +242,19 @@ const getFaustFFTAudioWorkletProcessor = (dependencies: FaustFFTAudioWorkletProc
232
242
  ];
233
243
  }
234
244
 
245
+ setupWamEventHandler() {
246
+ if (!this.wamInfo) return;
247
+ const { moduleId, instanceId } = this.wamInfo;
248
+ const { webAudioModules } = (globalThis as unknown as WamAudioWorkletGlobalScope);
249
+ const ModuleScope = webAudioModules.getModuleScope(moduleId) as WamParamMgrSDKBaseModuleScope;
250
+ const paramMgrProcessor = ModuleScope?.paramMgrProcessors?.[instanceId];
251
+ if (!paramMgrProcessor) return;
252
+ if (paramMgrProcessor.handleEvent) return;
253
+ paramMgrProcessor.handleEvent = (event) => {
254
+ if (event.type === "wam-midi") this.midiMessage(event.data.bytes);
255
+ };
256
+ }
257
+
235
258
  processFFT() {
236
259
  // Get the number of samples that need to proceed, from the input r/w pointers
237
260
  let samplesForFFT = mod(this.$inputWrite - this.$inputRead, this.fftBufferSize) || this.fftBufferSize;
@@ -400,6 +423,10 @@ const getFaustFFTAudioWorkletProcessor = (dependencies: FaustFFTAudioWorkletProc
400
423
  this.fDSPCode?.setPlotHandler(this.fPlotHandler);
401
424
  break;
402
425
  }
426
+ case "setupWamEventHandler": {
427
+ this.setupWamEventHandler();
428
+ break;
429
+ }
403
430
  case "start": {
404
431
  this.fDSPCode?.start();
405
432
  break;
@@ -40,20 +40,11 @@ export class FaustScriptProcessorNode<Poly extends boolean = false> extends (glo
40
40
  async listenSensors() {
41
41
  if (this.hasAccInput) {
42
42
  const isAndroid: boolean = /Android/i.test(navigator.userAgent);
43
- let handleDeviceMotion = null;
44
- if (isAndroid) {
45
- handleDeviceMotion = ({ accelerationIncludingGravity }: DeviceMotionEvent) => {
46
- if (!accelerationIncludingGravity) return;
47
- const { x, y, z } = accelerationIncludingGravity;
48
- this.propagateAcc({ x, y, z }, true);
49
- }
50
- } else {
51
- handleDeviceMotion = ({ accelerationIncludingGravity }: DeviceMotionEvent) => {
52
- if (!accelerationIncludingGravity) return;
53
- const { x, y, z } = accelerationIncludingGravity;
54
- this.propagateAcc({ x, y, z });
55
- }
56
- }
43
+ const handleDeviceMotion = ({ accelerationIncludingGravity }: DeviceMotionEvent) => {
44
+ if (!accelerationIncludingGravity) return;
45
+ const { x, y, z } = accelerationIncludingGravity;
46
+ this.propagateAcc({ x, y, z }, isAndroid);
47
+ };
57
48
  if (window.DeviceMotionEvent) {
58
49
  if (typeof (window.DeviceMotionEvent as any).requestPermission === "function") { // for iOS 13+
59
50
  try {
@@ -1,4 +1,4 @@
1
- declare const copyWebStandaloneAssets: (outputDir: string, dspName: string, poly: boolean) => void;
2
- declare const copyWebTemplateAssets: (outputDir: string, dspName: string, poly: boolean) => void;
1
+ declare const copyWebStandaloneAssets: (outputDir: string, dspName: string, poly?: boolean, effect?: boolean) => void;
2
+ declare const copyWebTemplateAssets: (outputDir: string, dspName: string, poly?: boolean, effect?: boolean) => void;
3
3
 
4
4
  export { copyWebStandaloneAssets, copyWebTemplateAssets };
@@ -1,7 +1,7 @@
1
1
  //@ts-check
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
- import { cpSync, cpSyncModify, rmSync } from "../fileutils.js";
4
+ import { cpSync, cpSyncModify } from "../fileutils.js";
5
5
  import { fileURLToPath } from "url";
6
6
 
7
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -11,27 +11,30 @@ const __filename = fileURLToPath(import.meta.url);
11
11
  * @param {string} outputDir - The output directory.
12
12
  * @param {string} dspName - The name of the DSP to be loaded.
13
13
  * @param {boolean} [poly] - Whether the DSP is polyphonic.
14
+ * @param {boolean} [effect] - Whether the DSP has an effect module.
14
15
  */
15
- const copyWebStandaloneAssets = (outputDir, dspName, poly = false) => {
16
+ const copyWebStandaloneAssets = (outputDir, dspName, poly = false, effect = false) => {
16
17
  console.log(`Writing assets files.`)
17
18
  const assetsPath = path.join(__dirname, "../assets/standalone");
18
19
  if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
19
20
 
21
+ const findAndReplace = ["FAUST_DSP_NAME", dspName];
22
+ if (poly) findAndReplace.push("FAUST_DSP_VOICES = 0", "FAUST_DSP_VOICES = 16");
23
+ if (poly && effect) findAndReplace.push("FAUST_DSP_HAS_EFFECT = false", "FAUST_DSP_HAS_EFFECT = true");
24
+
20
25
  // Copy some files
21
- const templateJSPath = (poly) ? path.join(__dirname, "../assets/standalone/index-poly.js") : path.join(__dirname, "../assets/standalone/index.js");
22
- cpSyncModify(templateJSPath, outputDir + `/${dspName}.js`, "FAUST_DSP", dspName);
26
+ const createNodeJSPath = path.join(__dirname, "../assets/standalone/create-node.js");
27
+ cpSyncModify(createNodeJSPath, outputDir + `/create-node.js`, ...findAndReplace);
28
+
29
+ const templateJSPath = path.join(__dirname, "../assets/standalone/index.js");
30
+ cpSyncModify(templateJSPath, outputDir + `/index.js`, ...findAndReplace);
23
31
 
24
32
  const templateHTMLPath = path.join(__dirname, "../assets/standalone/index.html");
25
- //cpSyncModify(templateHTMLPath, outputDir + `/${dspName}.html`, "FAUST_DSP", dspName);
26
- cpSyncModify(templateHTMLPath, outputDir + `/index.html`, "FAUST_DSP", dspName);
33
+ //cpSyncModify(templateHTMLPath, outputDir + `/${dspName}.html`, "FAUST_DSP_NAME", dspName);
34
+ cpSyncModify(templateHTMLPath, outputDir + `/index.html`, ...findAndReplace);
27
35
 
28
- if (poly) {
29
- const templateWorkerPath = path.join(__dirname, "../assets/standalone/service-worker-poly.js");
30
- cpSyncModify(templateWorkerPath, outputDir + `/service-worker.js`, "FAUST_DSP", dspName);
31
- } else {
32
- const templateWorkerPath = path.join(__dirname, "../assets/standalone/service-worker.js");
33
- cpSyncModify(templateWorkerPath, outputDir + `/service-worker.js`, "FAUST_DSP", dspName);
34
- }
36
+ const templateWorkerPath = path.join(__dirname, "../assets/standalone/service-worker.js");
37
+ cpSyncModify(templateWorkerPath, outputDir + `/service-worker.js`, ...findAndReplace);
35
38
 
36
39
  const templateIconPath = path.join(__dirname, "../assets/standalone/icon.png");
37
40
  cpSync(templateIconPath, outputDir + `/icon.png`);
@@ -43,25 +46,33 @@ const copyWebStandaloneAssets = (outputDir, dspName, poly = false) => {
43
46
  cpSync(faustuiPath, outputDir + "/faust-ui");
44
47
 
45
48
  const templateManifestPath = path.join(__dirname, "../assets/standalone/manifest.json");
46
- cpSyncModify(templateManifestPath, outputDir + `/manifest.json`, "FAUST_DSP", dspName);
49
+ cpSyncModify(templateManifestPath, outputDir + `/manifest.json`, ...findAndReplace);
47
50
  };
48
51
 
49
52
  /**
50
53
  * @param {string} outputDir - The output directory.
51
54
  * @param {string} dspName - The name of the DSP to be loaded.
52
55
  * @param {boolean} [poly] - Whether the DSP is polyphonic.
56
+ * @param {boolean} [effect] - Whether the DSP has an effect module.
53
57
  */
54
- const copyWebTemplateAssets = (outputDir, dspName, poly = false) => {
58
+ const copyWebTemplateAssets = (outputDir, dspName, poly = false, effect = false) => {
55
59
 
56
60
  // Create output directory if it doesn't exist
57
61
  if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
58
62
 
63
+ const findAndReplace = ["FAUST_DSP_NAME", dspName];
64
+ if (poly) findAndReplace.push("FAUST_DSP_VOICES = 0", "FAUST_DSP_VOICES = 16");
65
+ if (poly && effect) findAndReplace.push("FAUST_DSP_HAS_EFFECT = false", "FAUST_DSP_HAS_EFFECT = true");
66
+
59
67
  // Copy some files
60
- const templateJSPath = path.join(__dirname, "../assets/standalone/template.js");
61
- cpSync(templateJSPath, outputDir + `/${dspName}.js`);
68
+ const createNodeJSPath = path.join(__dirname, "../assets/standalone/create-node.js");
69
+ cpSyncModify(createNodeJSPath, outputDir + `/create-node.js`, ...findAndReplace);
70
+
71
+ const templateJSPath = path.join(__dirname, "../assets/standalone/index-template.js");
72
+ cpSyncModify(templateJSPath, outputDir + `/index.js`, ...findAndReplace);
62
73
 
63
- const templateHTMLPath = (poly) ? path.join(__dirname, "../assets/standalone/template-poly.html") : path.join(__dirname, "../assets/standalone/template.html");
64
- cpSyncModify(templateHTMLPath, outputDir + `/${dspName}.html`, "FAUST_DSP", dspName);
74
+ const templateHTMLPath = path.join(__dirname, "../assets/standalone/index-template.html");
75
+ cpSyncModify(templateHTMLPath, outputDir + `/index.html`, "index-template.js", "index.js", ...findAndReplace);
65
76
 
66
77
  const faustwasmPath = path.join(__dirname, "../assets/standalone/faustwasm");
67
78
  cpSync(faustwasmPath, outputDir + "/faustwasm");
@@ -34,11 +34,11 @@ const faust2wasmFiles = async (inputFile, outputDir, argv = [], poly = false) =>
34
34
 
35
35
  if (!argv.find(a => a === "-I")) argv.push("-I", "libraries/");
36
36
  argv.push("-ftz", "2");
37
- const dspModulePath = path.join(outputDir, `${dspName}.wasm`);
38
- const dspMetaPath = path.join(outputDir, `${dspName}.json`);
39
- const effectModulePath = path.join(outputDir, `${dspName}_effect.wasm`);
40
- const effectMetaPath = path.join(outputDir, `${dspName}_effect.json`);
41
- const mixerModulePath = path.join(outputDir, "mixerModule.wasm");
37
+ const dspModulePath = path.join(outputDir, `dsp-module.wasm`);
38
+ const dspMetaPath = path.join(outputDir, `dsp-meta.json`);
39
+ const effectModulePath = path.join(outputDir, `effect-module.wasm`);
40
+ const effectMetaPath = path.join(outputDir, `effect-meta.json`);
41
+ const mixerModulePath = path.join(outputDir, "mixer-module.wasm");
42
42
  /** @type {Uint8Array} */
43
43
  let dspModule;
44
44
  /** @type {import("./types").FaustDspMeta} */