@grame/faustwasm 0.1.7 → 0.2.1

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 (35) hide show
  1. package/assets/standalone/faustwasm/index.d.ts +150 -101
  2. package/assets/standalone/faustwasm/index.js +939 -1230
  3. package/assets/standalone/faustwasm/index.js.map +4 -4
  4. package/dist/cjs/index.d.ts +150 -101
  5. package/dist/cjs/index.js +941 -1212
  6. package/dist/cjs/index.js.map +4 -4
  7. package/dist/cjs-bundle/index.d.ts +150 -101
  8. package/dist/cjs-bundle/index.js +1044 -1317
  9. package/dist/cjs-bundle/index.js.map +4 -4
  10. package/dist/esm/index.d.ts +150 -101
  11. package/dist/esm/index.js +939 -1230
  12. package/dist/esm/index.js.map +4 -4
  13. package/dist/esm-bundle/index.d.ts +150 -101
  14. package/dist/esm-bundle/index.js +1044 -1321
  15. package/dist/esm-bundle/index.js.map +4 -4
  16. package/libfaust-wasm/libfaust-wasm.js +1 -1
  17. package/libfaust-wasm/libfaust-wasm.wasm +0 -0
  18. package/package.json +15 -15
  19. package/src/FaustAudioWorkletProcessor.ts +3 -14
  20. package/src/FaustCmajor.ts +6 -6
  21. package/src/FaustCompiler.ts +2 -2
  22. package/src/FaustDspGenerator.ts +156 -78
  23. package/src/FaustDspInstance.ts +2 -2
  24. package/src/FaustFFTAudioWorkletProcessor.ts +3 -1
  25. package/src/FaustWebAudioDsp.ts +224 -449
  26. package/src/SoundfileReader.ts +117 -0
  27. package/src/exports.ts +1 -0
  28. package/src/faust2wasmFiles.js +1 -1
  29. package/src/types.ts +15 -7
  30. package/test/faustlive-wasm/faustwasm/index.d.ts +150 -101
  31. package/test/faustlive-wasm/faustwasm/index.js +939 -1230
  32. package/test/faustlive-wasm/faustwasm/index.js.map +4 -4
  33. package/test/soundfile.dsp +15 -0
  34. package/test/soundfile1.dsp +23 -0
  35. package/test/web/soundfile.html +69 -0
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grame/faustwasm",
3
- "version": "0.1.7",
3
+ "version": "0.2.1",
4
4
  "description": "WebAssembly version of Faust Compiler",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/esm/index.d.ts",
@@ -9,9 +9,9 @@
9
9
  "scripts": {
10
10
  "build": "npm run build-cjs && npm run build-cjs-bundle && npm run build-esm && npm run build-esm-bundle && node postbuild-bundled.js & npm run build-types & npm run build-types-bundle",
11
11
  "build-cjs": "esbuild src/index.ts --target=es2019 --bundle --sourcemap --outdir=dist/cjs --format=iife --external:fs --external:url",
12
- "build-cjs-bundle": "node prebuild-bundled.js && esbuild src/index-bundle-iife.ts --target=es2019 --bundle --sourcemap --loader:.wasm=binary --loader:.data=binary --outfile=dist/cjs-bundle/index.js --format=iife --external:fs --external:url --external:path && node postbuild-bundled.js",
12
+ "build-cjs-bundle": "node prebuild-bundled.js && esbuild src/index-bundle-iife.ts --target=es2019 --bundle --sourcemap --loader:.wasm=binary --loader:.data=binary --outfile=dist/cjs-bundle/index.js --format=iife --external:fs --external:url --external:path --external:ws && node postbuild-bundled.js",
13
13
  "build-esm": "esbuild src/index.ts --target=es2019 --bundle --sourcemap --outdir=dist/esm --format=esm --external:fs --external:url",
14
- "build-esm-bundle": "node prebuild-bundled.js && esbuild src/index-bundle.ts --target=es2019 --bundle --sourcemap --loader:.wasm=binary --loader:.data=binary --outfile=dist/esm-bundle/index.js --format=esm --external:fs --external:url --external:path && node postbuild-bundled.js",
14
+ "build-esm-bundle": "node prebuild-bundled.js && esbuild src/index-bundle.ts --target=es2019 --bundle --sourcemap --loader:.wasm=binary --loader:.data=binary --outfile=dist/esm-bundle/index.js --format=esm --external:fs --external:url --external:path --external:ws && node postbuild-bundled.js",
15
15
  "build-types": "dts-bundle-generator -o dist/cjs/index.d.ts src/index.ts --external-imports",
16
16
  "build-types-bundle": "dts-bundle-generator -o dist/cjs-bundle/index.d.ts src/index-bundle.ts --external-imports",
17
17
  "postbuild": "node postbuild.js"
@@ -27,9 +27,9 @@
27
27
  "signal processing"
28
28
  ],
29
29
  "bin": {
30
- "faust2sndfile-ts": "scripts/faust2sndfile.js",
31
- "faust2svg-ts": "scripts/faust2svg.js",
32
- "faust2wasm-ts": "scripts/faust2wasm.js"
30
+ "faust2sndfile-ts": "scripts/faust2sndfile.js",
31
+ "faust2svg-ts": "scripts/faust2svg.js",
32
+ "faust2wasm-ts": "scripts/faust2wasm.js"
33
33
  },
34
34
  "author": "Grame-CNCM",
35
35
  "license": "LGPL-3.0",
@@ -37,16 +37,16 @@
37
37
  "url": "https://github.com/grame-cncm/faustwasm/issues"
38
38
  },
39
39
  "homepage": "https://github.com/grame-cncm/faustwasm#readme",
40
- "dependencies": {
41
- "@types/emscripten": "^1.39.5"
42
- },
43
40
  "devDependencies": {
44
- "@aws-crypto/sha256-js": "^2.0.1",
41
+ "@aws-crypto/sha256-js": "^5.2.0",
45
42
  "@shren/faust-ui": "^1.1.9",
46
- "@types/node": "^16.11.7",
47
- "@types/webmidi": "^2.0.6",
48
- "dts-bundle-generator": "^6.13.0",
49
- "esbuild": "^0.14.27",
50
- "typescript": "^4.4.4"
43
+ "@types/node": "^20.12.7",
44
+ "@types/webmidi": "^2.0.10",
45
+ "dts-bundle-generator": "^9.5.1",
46
+ "esbuild": "^0.20.2",
47
+ "typescript": "^5.4.5"
48
+ },
49
+ "dependencies": {
50
+ "@types/emscripten": "^1.39.10"
51
51
  }
52
52
  }
@@ -190,13 +190,7 @@ const getFaustAudioWorkletProcessor = <Poly extends boolean = false>(dependencie
190
190
  const instance = FaustWasmInstantiator.createSyncMonoDSPInstance(factory);
191
191
 
192
192
  // Create Monophonic DSP
193
- this.fDSPCode = new FaustMonoWebAudioDsp(instance, sampleRate, sampleSize, 128);
194
-
195
- // Check for soundfile support
196
- if (this.fDSPCode.hasSoundfiles()) {
197
- console.error("FaustAudioWorkletProcessor: Soundfile support is not implemented yet, switch to ScriptProcessorNode for now");
198
- return;
199
- }
193
+ this.fDSPCode = new FaustMonoWebAudioDsp(instance, sampleRate, sampleSize, 128, factory.soundfiles);
200
194
 
201
195
  // Setup output handler
202
196
  this.fDSPCode.setOutputParamHandler((path, value) => this.port.postMessage({ path, value, type: "param" }));
@@ -218,14 +212,9 @@ const getFaustAudioWorkletProcessor = <Poly extends boolean = false>(dependencie
218
212
 
219
213
  const instance = FaustWasmInstantiator.createSyncPolyDSPInstance(voiceFactory, mixerModule, voices, effectFactory);
220
214
 
215
+ const soundfiles = { ...effectFactory?.soundfiles, ...voiceFactory.soundfiles };
221
216
  // Create Polyphonic DSP
222
- this.fDSPCode = new FaustPolyWebAudioDsp(instance, sampleRate, sampleSize, 128);
223
-
224
- // Check for soundfile support
225
- if (this.fDSPCode.hasSoundfiles()) {
226
- console.error("FaustAudioWorkletProcessor: Soundfile support is not implemented yet, switch to ScriptProcessorNode for now");
227
- return;
228
- }
217
+ this.fDSPCode = new FaustPolyWebAudioDsp(instance, sampleRate, sampleSize, 128, soundfiles);
229
218
 
230
219
  // Setup port message handling
231
220
  this.port.onmessage = (e: MessageEvent) => this.handleMessageAux(e);
@@ -1,4 +1,4 @@
1
- import type LibFaust from "./LibFaust";
1
+ import type FaustCompiler from "./FaustCompiler";
2
2
 
3
3
  interface IFaustCmajor {
4
4
  /**
@@ -13,15 +13,15 @@ interface IFaustCmajor {
13
13
  }
14
14
 
15
15
  class FaustCmajor implements IFaustCmajor {
16
- private fLibFaust: LibFaust;
16
+ private fCompiler: FaustCompiler;
17
17
 
18
- constructor(libfaust: LibFaust) {
19
- this.fLibFaust = libfaust;
18
+ constructor(compiler: FaustCompiler) {
19
+ this.fCompiler = compiler;
20
20
  }
21
21
 
22
22
  compile(name: string, code: string, args: string) {
23
- const fs = this.fLibFaust.fs();
24
- const success = this.fLibFaust.generateAuxFiles(name, code, `-lang cmajor-hybrid -cn ${name} -o ${name}.cmajor`);
23
+ const fs = this.fCompiler.fs();
24
+ const success = this.fCompiler.generateAuxFiles(name, code, `-lang cmajor-hybrid -cn ${name} -o ${name}.cmajor`);
25
25
  return (success) ? fs.readFile(`${name}.cmajor`, { encoding: "utf8" }) as string : "";
26
26
  }
27
27
  }
@@ -127,7 +127,7 @@ class FaustCompiler implements IFaustCompiler {
127
127
  const factory = table[shaKey];
128
128
  const { code, json, poly } = factory;
129
129
  const ab = str2ab(atob(code))
130
- awaited.push(WebAssembly.compile(ab).then(module => this.gFactories.set(shaKey, { shaKey, cfactory: 0, code: ab, module, json: JSON.stringify(json), poly })));
130
+ awaited.push(WebAssembly.compile(ab).then(module => this.gFactories.set(shaKey, { shaKey, cfactory: 0, code: ab, module, json: JSON.stringify(json), poly, soundfiles: {} })));
131
131
  }
132
132
  return Promise.all(awaited);
133
133
  }
@@ -167,7 +167,7 @@ class FaustCompiler implements IFaustCompiler {
167
167
  const ui8Code = this.intVec2intArray(faustDspWasm.data);
168
168
  faustDspWasm.data.delete();
169
169
  const module = await WebAssembly.compile(ui8Code);
170
- const factory: FaustDspFactory = { shaKey, cfactory: faustDspWasm.cfactory, code: ui8Code, module, json: faustDspWasm.json, poly };
170
+ const factory: FaustDspFactory = { shaKey, cfactory: faustDspWasm.cfactory, code: ui8Code, module, json: faustDspWasm.json, poly, soundfiles: {} };
171
171
  // Factory C++ side can be deallocated immediately
172
172
  this.deleteDSPFactory(factory);
173
173
  // Keep the compiled factory in the cache
@@ -5,11 +5,26 @@ import { FaustDspInstance } from "./FaustDspInstance";
5
5
  import FaustWasmInstantiator from "./FaustWasmInstantiator";
6
6
  import { FaustMonoOfflineProcessor, FaustPolyOfflineProcessor, IFaustMonoOfflineProcessor, IFaustPolyOfflineProcessor } from "./FaustOfflineProcessor";
7
7
  import { FaustMonoScriptProcessorNode, FaustPolyScriptProcessorNode } from "./FaustScriptProcessorNode";
8
- import { FaustBaseWebAudioDsp, FaustMonoWebAudioDsp, FaustPolyWebAudioDsp, FaustWebAudioDspVoice, IFaustMonoWebAudioNode, IFaustPolyWebAudioNode } from "./FaustWebAudioDsp";
8
+ import { FaustBaseWebAudioDsp, FaustMonoWebAudioDsp, FaustPolyWebAudioDsp, FaustWebAudioDspVoice, IFaustMonoWebAudioNode, IFaustPolyWebAudioNode, Soundfile, WasmAllocator } from "./FaustWebAudioDsp";
9
9
  import type { IFaustCompiler } from "./FaustCompiler";
10
- import type { FaustDspFactory, FaustUIDescriptor, FaustDspMeta, FFTUtils, LooseFaustDspFactory } from "./types";
10
+ import type { FaustDspFactory, FaustUIDescriptor, FaustDspMeta, FFTUtils, LooseFaustDspFactory, AudioData } from "./types";
11
+ import SoundfileReader from "./SoundfileReader";
11
12
 
12
- export interface IFaustMonoDspGenerator {
13
+ export interface GeneratorSupportingSoundfiles {
14
+ /**
15
+ * Attach a map of id - audio data, call after `compile()` before `createNode()`
16
+ *
17
+ * @param soundfileMap a map of id - `AudioData` as an object where `AudioData` contains channel data as `audioBuffer: Float32Array[]` and `sampleRate: number`
18
+ */
19
+ addSoundfiles(soundfileMap: Record<string, AudioData>): void;
20
+
21
+ /**
22
+ * Get a list of soundfiles needed, call after `compile()`
23
+ */
24
+ getSoundfileList(): string[];
25
+ }
26
+
27
+ export interface IFaustMonoDspGenerator extends GeneratorSupportingSoundfiles {
13
28
  /**
14
29
  * Compile a monophonic DSP factory from given code.
15
30
  *
@@ -25,6 +40,7 @@ export interface IFaustMonoDspGenerator {
25
40
  meta?: FaustDspMeta;
26
41
  } | null>;
27
42
 
43
+
28
44
  /**
29
45
  * Create a monophonic WebAudio node (either ScriptProcessorNode or AudioWorkletNode).
30
46
  *
@@ -73,7 +89,11 @@ export interface IFaustMonoDspGenerator {
73
89
  * @param factory - default is the compiled factory
74
90
  * @returns the compiled processor or 'null' if failure
75
91
  */
76
- createOfflineProcessor(sampleRate: number, bufferSize: number, factory?: LooseFaustDspFactory, meta?: FaustDspMeta): Promise<IFaustMonoOfflineProcessor | null>;
92
+ createOfflineProcessor(
93
+ sampleRate: number,
94
+ bufferSize: number,
95
+ factory?: LooseFaustDspFactory
96
+ ): Promise<IFaustMonoOfflineProcessor | null>;
77
97
 
78
98
  /**
79
99
  * Get DSP JSON description with its UI and metadata as object.
@@ -97,7 +117,7 @@ export interface IFaustMonoDspGenerator {
97
117
  getUI(): FaustUIDescriptor;
98
118
  }
99
119
 
100
- export interface IFaustPolyDspGenerator {
120
+ export interface IFaustPolyDspGenerator extends GeneratorSupportingSoundfiles {
101
121
  /**
102
122
  * Compile a monophonic DSP factory from given code.
103
123
  *
@@ -134,7 +154,8 @@ export interface IFaustPolyDspGenerator {
134
154
  mixerModule?: WebAssembly.Module,
135
155
  effectFactory?: LooseFaustDspFactory | null,
136
156
  sp?: boolean,
137
- bufferSize?: number
157
+ bufferSize?: number,
158
+ processorName?: string
138
159
  ): Promise<IFaustPolyWebAudioNode | null>;
139
160
 
140
161
  /**
@@ -153,7 +174,8 @@ export interface IFaustPolyDspGenerator {
153
174
  voices: number,
154
175
  voiceFactory?: LooseFaustDspFactory,
155
176
  mixerModule?: WebAssembly.Module,
156
- effectFactory?: LooseFaustDspFactory | null
177
+ effectFactory?: LooseFaustDspFactory | null,
178
+ processorName?: string
157
179
  ): Promise<IFaustPolyOfflineProcessor | null>;
158
180
 
159
181
  /**
@@ -198,6 +220,19 @@ export class FaustMonoDspGenerator implements IFaustMonoDspGenerator {
198
220
  }
199
221
  }
200
222
 
223
+ addSoundfiles(soundfileMap: Record<string, AudioData>) {
224
+ if (!this.factory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
225
+ for (const id in soundfileMap) {
226
+ this.factory.soundfiles[id] = soundfileMap[id];
227
+ }
228
+ }
229
+ getSoundfileList() {
230
+ if (!this.factory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
231
+ const meta = JSON.parse(this.factory.json);
232
+ const map = SoundfileReader.findSoundfilesFromMeta(meta);
233
+ return Object.keys(map);
234
+ }
235
+
201
236
  async createNode<SP extends boolean = false>(
202
237
  context: BaseAudioContext,
203
238
  name = this.name,
@@ -210,11 +245,10 @@ export class FaustMonoDspGenerator implements IFaustMonoDspGenerator {
210
245
 
211
246
  const meta = JSON.parse(factory.json);
212
247
  const sampleSize = meta.compile_options.match("-double") ? 8 : 4;
248
+ factory.soundfiles = await SoundfileReader.loadSoundfiles(meta, factory.soundfiles || {}, context);
213
249
  if (sp) {
214
250
  const instance = await FaustWasmInstantiator.createAsyncMonoDSPInstance(factory);
215
- const monoDsp = new FaustMonoWebAudioDsp(instance, context.sampleRate, sampleSize, bufferSize);
216
- // Initialize the DSP instance, possibly loading soundfiles
217
- await monoDsp.init(context);
251
+ const monoDsp = new FaustMonoWebAudioDsp(instance, context.sampleRate, sampleSize, bufferSize, factory.soundfiles);
218
252
  const sp = context.createScriptProcessor(bufferSize, monoDsp.getNumInputs(), monoDsp.getNumOutputs()) as FaustMonoScriptProcessorNode;
219
253
  Object.setPrototypeOf(sp, FaustMonoScriptProcessorNode.prototype);
220
254
  sp.init(monoDsp);
@@ -233,15 +267,23 @@ const faustData = ${JSON.stringify({
233
267
  poly: false
234
268
  } as FaustData)};
235
269
  // Implementation needed classes of functions
236
- const ${FaustDspInstance.name} = ${FaustDspInstance.toString()}
237
- const ${FaustBaseWebAudioDsp.name} = ${FaustBaseWebAudioDsp.toString()}
238
- const ${FaustMonoWebAudioDsp.name} = ${FaustMonoWebAudioDsp.toString()}
239
- const ${FaustWasmInstantiator.name} = ${FaustWasmInstantiator.toString()}
270
+ var ${FaustDspInstance.name} = ${FaustDspInstance.toString()}
271
+ var FaustDspInstance = ${FaustDspInstance.name};
272
+ var ${FaustBaseWebAudioDsp.name} = ${FaustBaseWebAudioDsp.toString()}
273
+ var FaustBaseWebAudioDsp = ${FaustBaseWebAudioDsp.name};
274
+ var ${FaustMonoWebAudioDsp.name} = ${FaustMonoWebAudioDsp.toString()}
275
+ var FaustMonoWebAudioDsp = ${FaustMonoWebAudioDsp.name};
276
+ var ${FaustWasmInstantiator.name} = ${FaustWasmInstantiator.toString()}
277
+ var FaustWasmInstantiator = ${FaustWasmInstantiator.name};
278
+ var ${Soundfile.name} = ${Soundfile.toString()}
279
+ var Soundfile = ${Soundfile.name};
280
+ var ${WasmAllocator.name} = ${WasmAllocator.toString()}
281
+ var WasmAllocator = ${WasmAllocator.name};
240
282
  // Put them in dependencies
241
283
  const dependencies = {
242
- FaustBaseWebAudioDsp: ${FaustBaseWebAudioDsp.name},
243
- FaustMonoWebAudioDsp: ${FaustMonoWebAudioDsp.name},
244
- FaustWasmInstantiator: ${FaustWasmInstantiator.name}
284
+ FaustBaseWebAudioDsp,
285
+ FaustMonoWebAudioDsp,
286
+ FaustWasmInstantiator
245
287
  };
246
288
  // Generate the actual AudioWorkletProcessor code
247
289
  (${getFaustAudioWorkletProcessor.toString()})(dependencies, faustData);
@@ -274,6 +316,7 @@ const dependencies = {
274
316
 
275
317
  const meta: FaustDspMeta = JSON.parse(factory.json);
276
318
  const sampleSize = meta.compile_options.match("-double") ? 8 : 4;
319
+ factory.soundfiles = await SoundfileReader.loadSoundfiles(meta, factory.soundfiles || {}, context);
277
320
  // Dynamically create AudioWorkletProcessor if code not yet created
278
321
  if (!FaustMonoDspGenerator.gWorkletProcessors.has(context)) FaustMonoDspGenerator.gWorkletProcessors.set(context, new Set());
279
322
  if (!FaustMonoDspGenerator.gWorkletProcessors.get(context)?.has(processorName)) {
@@ -287,16 +330,24 @@ const faustData = ${JSON.stringify({
287
330
  fftOptions
288
331
  } as FaustFFTData)};
289
332
  // Implementation needed classes of functions
290
- const ${FaustDspInstance.name} = ${FaustDspInstance.toString()}
291
- const ${FaustBaseWebAudioDsp.name} = ${FaustBaseWebAudioDsp.toString()}
292
- const ${FaustMonoWebAudioDsp.name} = ${FaustMonoWebAudioDsp.toString()}
293
- const ${FaustWasmInstantiator.name} = ${FaustWasmInstantiator.toString()}
294
- const FFTUtils = ${fftUtils.toString()}
333
+ var ${FaustDspInstance.name} = ${FaustDspInstance.toString()}
334
+ var FaustDspInstance = ${FaustDspInstance.name};
335
+ var ${FaustBaseWebAudioDsp.name} = ${FaustBaseWebAudioDsp.toString()}
336
+ var FaustBaseWebAudioDsp = ${FaustBaseWebAudioDsp.name};
337
+ var ${FaustMonoWebAudioDsp.name} = ${FaustMonoWebAudioDsp.toString()}
338
+ var FaustMonoWebAudioDsp = ${FaustMonoWebAudioDsp.name};
339
+ var ${FaustWasmInstantiator.name} = ${FaustWasmInstantiator.toString()}
340
+ var FaustWasmInstantiator = ${FaustWasmInstantiator.name};
341
+ var ${Soundfile.name} = ${Soundfile.toString()}
342
+ var Soundfile = ${Soundfile.name};
343
+ var ${WasmAllocator.name} = ${WasmAllocator.toString()}
344
+ var WasmAllocator = ${WasmAllocator.name};
345
+ var FFTUtils = ${fftUtils.toString()}
295
346
  // Put them in dependencies
296
347
  const dependencies = {
297
- FaustBaseWebAudioDsp: ${FaustBaseWebAudioDsp.name},
298
- FaustMonoWebAudioDsp: ${FaustMonoWebAudioDsp.name},
299
- FaustWasmInstantiator: ${FaustWasmInstantiator.name},
348
+ FaustBaseWebAudioDsp,
349
+ FaustMonoWebAudioDsp,
350
+ FaustWasmInstantiator,
300
351
  FFTUtils
301
352
  };
302
353
  // Generate the actual AudioWorkletProcessor code
@@ -371,16 +422,14 @@ const dependencies = {
371
422
  async createOfflineProcessor(
372
423
  sampleRate: number,
373
424
  bufferSize: number,
374
- factory = this.factory as LooseFaustDspFactory,
425
+ factory = this.factory as LooseFaustDspFactory
375
426
  ) {
376
427
  if (!factory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
377
428
 
378
429
  const meta = JSON.parse(factory.json);
379
430
  const instance = await FaustWasmInstantiator.createAsyncMonoDSPInstance(factory);
380
431
  const sampleSize = meta.compile_options.match("-double") ? 8 : 4;
381
- const monoDsp = new FaustMonoWebAudioDsp(instance, sampleRate, sampleSize, bufferSize);
382
- // Initialize the DSP instance, no soundfiles loading for now (TODO ?)
383
- await monoDsp.init(null);
432
+ const monoDsp = new FaustMonoWebAudioDsp(instance, sampleRate, sampleSize, bufferSize, factory.soundfiles);
384
433
  return new FaustMonoOfflineProcessor(monoDsp, bufferSize);
385
434
  }
386
435
 
@@ -421,43 +470,45 @@ export class FaustPolyDspGenerator implements IFaustPolyDspGenerator {
421
470
  // the voice and effect are adapted, possibly clearing buffers
422
471
  if (this.effectFactory) {
423
472
  const effectJSON = JSON.parse(this.effectFactory.json);
424
- const dspCode = `
425
- // Voice output is forced to 2, when DSP is stereo or effect has 2 ins or 2 outs,
426
- // so that the effect can process the 2 channels of the voice
427
- adaptOut(1,1,1) = _;
428
- adaptOut(1,1,2) = _ <: _,0; // The left channel only is kept
429
- adaptOut(1,2,1) = _ <: _,_;
430
- adaptOut(1,2,2) = _ <: _,_;
431
- adaptOut(2,1,1) = _,_;
432
- adaptOut(2,1,2) = _,_;
433
- adaptOut(2,2,1) = _,_;
434
- adaptOut(2,2,2) = _,_;
435
- adaptor(F) = adaptOut(outputs(F),${effectJSON.inputs},${effectJSON.outputs});
436
- dsp_code = environment{
437
- ${dspCodeAux}
438
- };
439
- process = dsp_code.process : adaptor(dsp_code.process);`
440
- const effectCode = `
441
- // Inputs
442
- adaptIn(1,1,1) = _;
443
- adaptIn(1,1,2) = _,_ :> _;
444
- adaptIn(1,2,1) = _,_;
445
- adaptIn(1,2,2) = _,_;
446
- adaptIn(2,1,1) = _,_ :> _;
447
- adaptIn(2,1,2) = _,_ :> _;
448
- adaptIn(2,2,1) = _,_;
449
- adaptIn(2,2,2) = _,_;
450
- // Outputs
451
- adaptOut(1,1) = _ <: _,0; // The left channel only is kept
452
- adaptOut(1,2) = _,_;
453
- adaptOut(2,1) = _ <: _,0; // The left channel only is kept
454
- adaptOut(2,2) = _,_;
455
- adaptorIns(F) = adaptIn(outputs(F),${effectJSON.inputs},${effectJSON.outputs});
456
- adaptorOuts = adaptOut(${effectJSON.inputs},${effectJSON.outputs});
457
- dsp_code = environment{
458
- ${dspCodeAux}
459
- };
460
- process = adaptorIns(dsp_code.process) : dsp_code.effect : adaptorOuts;`
473
+ const dspCode = `\
474
+ // Voice output is forced to 2, when DSP is stereo or effect has 2 ins or 2 outs,
475
+ // so that the effect can process the 2 channels of the voice
476
+ adaptOut(1,1,1) = _;
477
+ adaptOut(1,1,2) = _ <: _,0; // The left channel only is kept
478
+ adaptOut(1,2,1) = _ <: _,_;
479
+ adaptOut(1,2,2) = _ <: _,_;
480
+ adaptOut(2,1,1) = _,_;
481
+ adaptOut(2,1,2) = _,_;
482
+ adaptOut(2,2,1) = _,_;
483
+ adaptOut(2,2,2) = _,_;
484
+ adaptor(F) = adaptOut(outputs(F),${effectJSON.inputs},${effectJSON.outputs});
485
+ dsp_code = environment{
486
+ ${dspCodeAux}
487
+ };
488
+ process = dsp_code.process : adaptor(dsp_code.process);
489
+ `;
490
+ const effectCode = `\
491
+ // Inputs
492
+ adaptIn(1,1,1) = _;
493
+ adaptIn(1,1,2) = _,_ :> _;
494
+ adaptIn(1,2,1) = _,_;
495
+ adaptIn(1,2,2) = _,_;
496
+ adaptIn(2,1,1) = _,_ :> _;
497
+ adaptIn(2,1,2) = _,_ :> _;
498
+ adaptIn(2,2,1) = _,_;
499
+ adaptIn(2,2,2) = _,_;
500
+ // Outputs
501
+ adaptOut(1,1) = _ <: _,0; // The left channel only is kept
502
+ adaptOut(1,2) = _,_;
503
+ adaptOut(2,1) = _ <: _,0; // The left channel only is kept
504
+ adaptOut(2,2) = _,_;
505
+ adaptorIns(F) = adaptIn(outputs(F),${effectJSON.inputs},${effectJSON.outputs});
506
+ adaptorOuts = adaptOut(${effectJSON.inputs},${effectJSON.outputs});
507
+ dsp_code = environment{
508
+ ${dspCodeAux}
509
+ };
510
+ process = adaptorIns(dsp_code.process) : dsp_code.effect : adaptorOuts;
511
+ `;
461
512
  this.voiceFactory = await compiler.createPolyDSPFactory(name, dspCode, args);
462
513
  try {
463
514
  // Effect is processing same buffers for inputs and outputs, so has to use -inpl option
@@ -484,6 +535,22 @@ export class FaustPolyDspGenerator implements IFaustPolyDspGenerator {
484
535
  }
485
536
  }
486
537
 
538
+ addSoundfiles(soundfileMap: Record<string, AudioData>) {
539
+ if (!this.voiceFactory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
540
+ for (const id in soundfileMap) {
541
+ this.voiceFactory.soundfiles[id] = soundfileMap[id];
542
+ }
543
+ }
544
+ getSoundfileList() {
545
+ if (!this.voiceFactory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
546
+ const meta = JSON.parse(this.voiceFactory.json);
547
+ const map = SoundfileReader.findSoundfilesFromMeta(meta);
548
+ if (!this.effectFactory) return Object.keys(map);
549
+ const effectMeta = JSON.parse(this.effectFactory.json);
550
+ const effectMap = SoundfileReader.findSoundfilesFromMeta(effectMeta);
551
+ return Object.keys({ ...effectMap, ...map });
552
+ }
553
+
487
554
  async createNode<SP extends boolean = false>(
488
555
  context: BaseAudioContext,
489
556
  voices: number,
@@ -500,11 +567,12 @@ export class FaustPolyDspGenerator implements IFaustPolyDspGenerator {
500
567
  const voiceMeta = JSON.parse(voiceFactory.json);
501
568
  const effectMeta = effectFactory ? JSON.parse(effectFactory.json) : undefined;
502
569
  const sampleSize = voiceMeta.compile_options.match("-double") ? 8 : 4;
570
+ voiceFactory.soundfiles = await SoundfileReader.loadSoundfiles(voiceMeta, voiceFactory.soundfiles || {}, context);
571
+ if (effectFactory) effectFactory.soundfiles = await SoundfileReader.loadSoundfiles(effectMeta, effectFactory.soundfiles || {}, context);
503
572
  if (sp) {
504
573
  const instance = await FaustWasmInstantiator.createAsyncPolyDSPInstance(voiceFactory, mixerModule, voices, effectFactory || undefined);
505
- const polyDsp = new FaustPolyWebAudioDsp(instance, context.sampleRate, sampleSize, bufferSize);
506
- // Initialize the DSP instance, possibly loading soundfiles
507
- await polyDsp.init(context);
574
+ const soundfiles = { ...effectFactory?.soundfiles, ...voiceFactory.soundfiles };
575
+ const polyDsp = new FaustPolyWebAudioDsp(instance, context.sampleRate, sampleSize, bufferSize, soundfiles);
508
576
  const sp = context.createScriptProcessor(bufferSize, polyDsp.getNumInputs(), polyDsp.getNumOutputs()) as FaustPolyScriptProcessorNode;
509
577
  Object.setPrototypeOf(sp, FaustPolyScriptProcessorNode.prototype);
510
578
  sp.init(polyDsp);
@@ -524,16 +592,25 @@ const faustData = ${JSON.stringify({
524
592
  effectMeta
525
593
  } as FaustData)};
526
594
  // Implementation needed classes of functions
527
- const ${FaustDspInstance.name} = ${FaustDspInstance.toString()}
528
- const ${FaustBaseWebAudioDsp.name} = ${FaustBaseWebAudioDsp.toString()}
529
- const ${FaustPolyWebAudioDsp.name} = ${FaustPolyWebAudioDsp.toString()}
530
- const ${FaustWebAudioDspVoice.name} = ${FaustWebAudioDspVoice.toString()}
531
- const ${FaustWasmInstantiator.name} = ${FaustWasmInstantiator.toString()}
595
+ var ${FaustDspInstance.name} = ${FaustDspInstance.toString()}
596
+ var FaustDspInstance = ${FaustDspInstance.name};
597
+ var ${FaustBaseWebAudioDsp.name} = ${FaustBaseWebAudioDsp.toString()}
598
+ var FaustBaseWebAudioDsp = ${FaustBaseWebAudioDsp.name};
599
+ var ${FaustPolyWebAudioDsp.name} = ${FaustPolyWebAudioDsp.toString()}
600
+ var FaustPolyWebAudioDsp = ${FaustPolyWebAudioDsp.name};
601
+ var ${FaustWebAudioDspVoice.name} = ${FaustWebAudioDspVoice.toString()}
602
+ var FaustWebAudioDspVoice = ${FaustWebAudioDspVoice.name};
603
+ var ${FaustWasmInstantiator.name} = ${FaustWasmInstantiator.toString()}
604
+ var FaustWasmInstantiator = ${FaustWasmInstantiator.name};
605
+ var ${Soundfile.name} = ${Soundfile.toString()}
606
+ var Soundfile = ${Soundfile.name};
607
+ var ${WasmAllocator.name} = ${WasmAllocator.toString()}
608
+ var WasmAllocator = ${WasmAllocator.name};
532
609
  // Put them in dependencies
533
610
  const dependencies = {
534
- FaustBaseWebAudioDsp: ${FaustBaseWebAudioDsp.name},
535
- FaustPolyWebAudioDsp: ${FaustPolyWebAudioDsp.name},
536
- FaustWasmInstantiator: ${FaustWasmInstantiator.name}
611
+ FaustBaseWebAudioDsp,
612
+ FaustPolyWebAudioDsp,
613
+ FaustWasmInstantiator
537
614
  };
538
615
  // Generate the actual AudioWorkletProcessor code
539
616
  (${getFaustAudioWorkletProcessor.toString()})(dependencies, faustData);
@@ -606,7 +683,8 @@ const dependencies = {
606
683
  const effectMeta = effectFactory ? JSON.parse(effectFactory.json) : undefined;
607
684
  const instance = await FaustWasmInstantiator.createAsyncPolyDSPInstance(voiceFactory, mixerModule, voices, effectFactory || undefined);
608
685
  const sampleSize = voiceMeta.compile_options.match("-double") ? 8 : 4;
609
- const polyDsp = new FaustPolyWebAudioDsp(instance, sampleRate, sampleSize, bufferSize);
686
+ const soundfiles = { ...effectFactory?.soundfiles, ...voiceFactory.soundfiles };
687
+ const polyDsp = new FaustPolyWebAudioDsp(instance, sampleRate, sampleSize, bufferSize, soundfiles);
610
688
  return new FaustPolyOfflineProcessor(polyDsp, bufferSize);
611
689
  }
612
690
 
@@ -31,7 +31,7 @@ export interface IFaustDspInstance {
31
31
  *
32
32
  * @param $dsp - the DSP pointer
33
33
  * @param index - the parameter index
34
- * @preturn the parameter value
34
+ * @return the parameter value
35
35
  */
36
36
  getParamValue($dsp: number, index: number): number;
37
37
 
@@ -39,7 +39,7 @@ export interface IFaustDspInstance {
39
39
  * Give the Faust wasm instance sample rate.
40
40
  *
41
41
  * @param $dsp - the DSP pointer
42
- * @preturn the sample rate
42
+ * @return the sample rate
43
43
  */
44
44
  getSampleRate($dsp: number): number;
45
45
 
@@ -162,6 +162,7 @@ const getFaustFFTAudioWorkletProcessor = (dependencies: FaustFFTAudioWorkletProc
162
162
  private fPlotHandler: PlotHandler | null = null;
163
163
  private fCachedEvents: { type: string; data: any }[] = [];
164
164
  private fBufferNum = 0;
165
+ private soundfiles: LooseFaustDspFactory["soundfiles"] = {};
165
166
  get fftProcessorBufferSize() {
166
167
  return this.fftSize / 2 + 1;
167
168
  }
@@ -182,6 +183,7 @@ const getFaustFFTAudioWorkletProcessor = (dependencies: FaustFFTAudioWorkletProc
182
183
 
183
184
  this.dspInstance = FaustWasmInstantiator.createSyncMonoDSPInstance(factory);
184
185
  this.sampleSize = sampleSize;
186
+ this.soundfiles = factory.soundfiles;
185
187
 
186
188
  // Init the FFT constructor and the Faust FFT Processor
187
189
  this.initFFT();
@@ -509,7 +511,7 @@ const getFaustFFTAudioWorkletProcessor = (dependencies: FaustFFTAudioWorkletProc
509
511
  this.fDSPCode?.destroy();
510
512
 
511
513
  // Create Monophonic DSP
512
- this.fDSPCode = new FaustMonoWebAudioDsp(this.dspInstance, sampleRate, this.sampleSize, this.fftProcessorBufferSize);
514
+ this.fDSPCode = new FaustMonoWebAudioDsp(this.dspInstance, sampleRate, this.sampleSize, this.fftProcessorBufferSize, this.soundfiles);
513
515
 
514
516
  // Setup output handler
515
517
  this.fDSPCode.setOutputParamHandler((path, value) => this.port.postMessage({ path, value, type: "param" }));