@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.
- package/assets/standalone/faustwasm/index.d.ts +150 -101
- package/assets/standalone/faustwasm/index.js +939 -1230
- package/assets/standalone/faustwasm/index.js.map +4 -4
- package/dist/cjs/index.d.ts +150 -101
- package/dist/cjs/index.js +941 -1212
- package/dist/cjs/index.js.map +4 -4
- package/dist/cjs-bundle/index.d.ts +150 -101
- package/dist/cjs-bundle/index.js +1044 -1317
- package/dist/cjs-bundle/index.js.map +4 -4
- package/dist/esm/index.d.ts +150 -101
- package/dist/esm/index.js +939 -1230
- package/dist/esm/index.js.map +4 -4
- package/dist/esm-bundle/index.d.ts +150 -101
- package/dist/esm-bundle/index.js +1044 -1321
- package/dist/esm-bundle/index.js.map +4 -4
- package/libfaust-wasm/libfaust-wasm.js +1 -1
- package/libfaust-wasm/libfaust-wasm.wasm +0 -0
- package/package.json +15 -15
- package/src/FaustAudioWorkletProcessor.ts +3 -14
- package/src/FaustCmajor.ts +6 -6
- package/src/FaustCompiler.ts +2 -2
- package/src/FaustDspGenerator.ts +156 -78
- package/src/FaustDspInstance.ts +2 -2
- package/src/FaustFFTAudioWorkletProcessor.ts +3 -1
- package/src/FaustWebAudioDsp.ts +224 -449
- package/src/SoundfileReader.ts +117 -0
- package/src/exports.ts +1 -0
- package/src/faust2wasmFiles.js +1 -1
- package/src/types.ts +15 -7
- package/test/faustlive-wasm/faustwasm/index.d.ts +150 -101
- package/test/faustlive-wasm/faustwasm/index.js +939 -1230
- package/test/faustlive-wasm/faustwasm/index.js.map +4 -4
- package/test/soundfile.dsp +15 -0
- package/test/soundfile1.dsp +23 -0
- 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
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
41
|
+
"@aws-crypto/sha256-js": "^5.2.0",
|
|
45
42
|
"@shren/faust-ui": "^1.1.9",
|
|
46
|
-
"@types/node": "^
|
|
47
|
-
"@types/webmidi": "^2.0.
|
|
48
|
-
"dts-bundle-generator": "^
|
|
49
|
-
"esbuild": "^0.
|
|
50
|
-
"typescript": "^
|
|
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);
|
package/src/FaustCmajor.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
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
|
|
16
|
+
private fCompiler: FaustCompiler;
|
|
17
17
|
|
|
18
|
-
constructor(
|
|
19
|
-
this.
|
|
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.
|
|
24
|
-
const success = this.
|
|
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
|
}
|
package/src/FaustCompiler.ts
CHANGED
|
@@ -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
|
package/src/FaustDspGenerator.ts
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
243
|
-
FaustMonoWebAudioDsp
|
|
244
|
-
FaustWasmInstantiator
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
|
298
|
-
FaustMonoWebAudioDsp
|
|
299
|
-
FaustWasmInstantiator
|
|
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
|
506
|
-
|
|
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
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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
|
|
535
|
-
FaustPolyWebAudioDsp
|
|
536
|
-
FaustWasmInstantiator
|
|
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
|
|
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
|
|
package/src/FaustDspInstance.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface IFaustDspInstance {
|
|
|
31
31
|
*
|
|
32
32
|
* @param $dsp - the DSP pointer
|
|
33
33
|
* @param index - the parameter index
|
|
34
|
-
* @
|
|
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
|
-
* @
|
|
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" }));
|