@grame/faustwasm 0.0.25
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/README.md +181 -0
- package/assets/standalone/faust-ui/index.css +414 -0
- package/assets/standalone/faust-ui/index.css.map +1 -0
- package/assets/standalone/faust-ui/index.d.ts +1 -0
- package/assets/standalone/faust-ui/index.js +3478 -0
- package/assets/standalone/faust-ui/index.js.map +1 -0
- package/assets/standalone/faustwasm/index.d.ts +1252 -0
- package/assets/standalone/faustwasm/index.js +3772 -0
- package/assets/standalone/faustwasm/index.js.map +7 -0
- package/assets/standalone/index.html +73 -0
- package/assets/standalone/index.js +198 -0
- package/assets/standalone/types.d.ts +9 -0
- package/dist/cjs/index.d.ts +1252 -0
- package/dist/cjs/index.js +3752 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs-bundle/index.d.ts +1259 -0
- package/dist/cjs-bundle/index.js +11507 -0
- package/dist/cjs-bundle/index.js.map +7 -0
- package/dist/esm/index.d.ts +1252 -0
- package/dist/esm/index.js +3772 -0
- package/dist/esm/index.js.map +7 -0
- package/dist/esm-bundle/index.d.ts +1259 -0
- package/dist/esm-bundle/index.js +11495 -0
- package/dist/esm-bundle/index.js.map +7 -0
- package/fileutils.js +40 -0
- package/libfaust-wasm/libfaust-wasm.d.cts +3 -0
- package/libfaust-wasm/libfaust-wasm.d.ts +3 -0
- package/libfaust-wasm/libfaust-wasm.data +0 -0
- package/libfaust-wasm/libfaust-wasm.data.d.ts +2 -0
- package/libfaust-wasm/libfaust-wasm.js +21 -0
- package/libfaust-wasm/libfaust-wasm.wasm +0 -0
- package/libfaust-wasm/libfaust-wasm.wasm.d.ts +2 -0
- package/library.md +91 -0
- package/package.json +43 -0
- package/postbuild-bundled.js +11 -0
- package/postbuild.js +63 -0
- package/prebuild-bundled.js +11 -0
- package/rsrc/overview.png +0 -0
- package/rsrc/overview.xlsx +0 -0
- package/scripts/faust2sndfile.js +43 -0
- package/scripts/faust2svg.js +17 -0
- package/scripts/faust2wasm.js +29 -0
- package/src/FaustAudioWorkletNode.ts +264 -0
- package/src/FaustAudioWorkletProcessor.ts +273 -0
- package/src/FaustCompiler.ts +252 -0
- package/src/FaustDspGenerator.ts +498 -0
- package/src/FaustDspInstance.ts +141 -0
- package/src/FaustFFTAudioWorkletProcessor.ts +554 -0
- package/src/FaustOfflineProcessor.ts +141 -0
- package/src/FaustScriptProcessorNode.ts +80 -0
- package/src/FaustSvgDiagrams.ts +37 -0
- package/src/FaustWasmInstantiator.ts +202 -0
- package/src/FaustWebAudioDsp.ts +1060 -0
- package/src/LibFaust.ts +57 -0
- package/src/WavDecoder.ts +207 -0
- package/src/WavEncoder.ts +204 -0
- package/src/copyWebStandaloneAssets.d.ts +3 -0
- package/src/copyWebStandaloneAssets.js +20 -0
- package/src/exports-bundle.ts +5 -0
- package/src/exports.ts +24 -0
- package/src/faust2svgFiles.d.ts +3 -0
- package/src/faust2svgFiles.js +43 -0
- package/src/faust2wasmFiles.d.ts +5 -0
- package/src/faust2wasmFiles.js +93 -0
- package/src/faust2wavFiles.d.ts +3 -0
- package/src/faust2wavFiles.js +58 -0
- package/src/index-bundle-iife.ts +7 -0
- package/src/index-bundle.ts +5 -0
- package/src/index.ts +5 -0
- package/src/instantiateFaustModule.ts +32 -0
- package/src/instantiateFaustModuleFromFile.ts +57 -0
- package/src/types.ts +234 -0
- package/test/clarinet.dsp +1 -0
- package/test/faustlive-wasm/faust-ui/index.css +414 -0
- package/test/faustlive-wasm/faust-ui/index.css.map +1 -0
- package/test/faustlive-wasm/faust-ui/index.d.ts +1 -0
- package/test/faustlive-wasm/faust-ui/index.js +3478 -0
- package/test/faustlive-wasm/faust-ui/index.js.map +1 -0
- package/test/faustlive-wasm/faustwasm/index.d.ts +1252 -0
- package/test/faustlive-wasm/faustwasm/index.js +3772 -0
- package/test/faustlive-wasm/faustwasm/index.js.map +7 -0
- package/test/faustlive-wasm/index.css +97 -0
- package/test/faustlive-wasm/index.html +130 -0
- package/test/faustlive-wasm/index.js +638 -0
- package/test/guitar.dsp +1 -0
- package/test/guitar1.dsp +2 -0
- package/test/libfaust-in-worklet/index.html +13 -0
- package/test/libfaust-in-worklet/index.js +40 -0
- package/test/mono.dsp +8 -0
- package/test/node/test.js +17 -0
- package/test/noise.dsp +3 -0
- package/test/organ.dsp +5 -0
- package/test/organ1.dsp +6 -0
- package/test/osc.dsp +4 -0
- package/test/osc2.dsp +5 -0
- package/test/osc3.dsp +4 -0
- package/test/p-dj.dsp +4 -0
- package/test/poly.dsp +13 -0
- package/test/rev.dsp +2 -0
- package/test/t1.dsp +1 -0
- package/test/web/fft.html +87 -0
- package/test/web/fft1.html +88 -0
- package/test/web/fft2.html +114 -0
- package/test/web/fftw.js +1907 -0
- package/test/web/index.html +12 -0
- package/test/web/index.js +235 -0
- package/test/web/kissfft.js +904 -0
- package/test/web/mono.html +46 -0
- package/test/web/poly-key.html +62 -0
- package/test/web/poly.html +59 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import type FaustWasmInstantiator from "./FaustWasmInstantiator";
|
|
2
|
+
import type { FaustBaseWebAudioDsp, FaustWebAudioDspVoice, FaustMonoWebAudioDsp, FaustPolyWebAudioDsp } from "./FaustWebAudioDsp";
|
|
3
|
+
import type { AudioParamDescriptor, AudioWorkletGlobalScope, LooseFaustDspFactory, FaustDspMeta, FaustUIItem, AudioWorkletProcessor } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Injected in the string to be compiled on AudioWorkletProcessor side
|
|
7
|
+
*/
|
|
8
|
+
export interface FaustData {
|
|
9
|
+
processorName: string;
|
|
10
|
+
dspName: string;
|
|
11
|
+
dspMeta: FaustDspMeta;
|
|
12
|
+
poly: boolean;
|
|
13
|
+
effectMeta?: FaustDspMeta;
|
|
14
|
+
};
|
|
15
|
+
export interface FaustAudioWorkletProcessorDependencies<Poly extends boolean = false> {
|
|
16
|
+
FaustBaseWebAudioDsp: typeof FaustBaseWebAudioDsp;
|
|
17
|
+
FaustMonoWebAudioDsp: Poly extends true ? undefined : typeof FaustMonoWebAudioDsp;
|
|
18
|
+
FaustPolyWebAudioDsp: Poly extends true ? typeof FaustPolyWebAudioDsp : undefined;
|
|
19
|
+
FaustWebAudioDspVoice: Poly extends true ? typeof FaustWebAudioDspVoice : undefined;
|
|
20
|
+
FaustWasmInstantiator: typeof FaustWasmInstantiator;
|
|
21
|
+
}
|
|
22
|
+
export interface FaustAudioWorkletNodeOptions<Poly extends boolean = false> extends AudioWorkletNodeOptions {
|
|
23
|
+
processorOptions: Poly extends true ? FaustPolyAudioWorkletProcessorOptions : FaustMonoAudioWorkletProcessorOptions;
|
|
24
|
+
}
|
|
25
|
+
export interface FaustMonoAudioWorkletNodeOptions extends AudioWorkletNodeOptions {
|
|
26
|
+
processorOptions: FaustMonoAudioWorkletProcessorOptions;
|
|
27
|
+
}
|
|
28
|
+
export interface FaustPolyAudioWorkletNodeOptions extends AudioWorkletNodeOptions {
|
|
29
|
+
processorOptions: FaustPolyAudioWorkletProcessorOptions;
|
|
30
|
+
}
|
|
31
|
+
export interface FaustAudioWorkletProcessorOptions {
|
|
32
|
+
name: string;
|
|
33
|
+
sampleSize: number;
|
|
34
|
+
}
|
|
35
|
+
export interface FaustMonoAudioWorkletProcessorOptions extends FaustAudioWorkletProcessorOptions {
|
|
36
|
+
factory: LooseFaustDspFactory;
|
|
37
|
+
}
|
|
38
|
+
export interface FaustPolyAudioWorkletProcessorOptions extends FaustAudioWorkletProcessorOptions {
|
|
39
|
+
voiceFactory: LooseFaustDspFactory;
|
|
40
|
+
mixerModule: WebAssembly.Module;
|
|
41
|
+
voices: number;
|
|
42
|
+
effectFactory?: LooseFaustDspFactory;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// Dynamic AudioWorkletProcessor code generator
|
|
47
|
+
const getFaustAudioWorkletProcessor = <Poly extends boolean = false>(dependencies: FaustAudioWorkletProcessorDependencies<Poly>, faustData: FaustData, register = true): typeof AudioWorkletProcessor => {
|
|
48
|
+
const { registerProcessor, AudioWorkletProcessor, sampleRate } = globalThis as unknown as AudioWorkletGlobalScope;
|
|
49
|
+
|
|
50
|
+
const {
|
|
51
|
+
FaustBaseWebAudioDsp,
|
|
52
|
+
FaustWasmInstantiator
|
|
53
|
+
} = dependencies;
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
processorName,
|
|
57
|
+
dspName,
|
|
58
|
+
dspMeta,
|
|
59
|
+
effectMeta,
|
|
60
|
+
poly
|
|
61
|
+
} = faustData;
|
|
62
|
+
|
|
63
|
+
// Analyse voice JSON to generate AudioParam parameters
|
|
64
|
+
const analysePolyParameters = (item: FaustUIItem): AudioParamDescriptor | null => {
|
|
65
|
+
const polyKeywords = ["/gate", "/freq", "/gain", "/key", "/vel", "/velocity"];
|
|
66
|
+
const isPolyReserved = "address" in item && !!polyKeywords.find(k => item.address.endsWith(k));
|
|
67
|
+
if (poly && isPolyReserved) return null;
|
|
68
|
+
if (item.type === "vslider" || item.type === "hslider" || item.type === "nentry") {
|
|
69
|
+
return { name: item.address, defaultValue: item.init || 0, minValue: item.min || 0, maxValue: item.max || 0 };
|
|
70
|
+
} else if (item.type === "button" || item.type === "checkbox") {
|
|
71
|
+
return { name: item.address, defaultValue: item.init || 0, minValue: 0, maxValue: 1 };
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Base class for Monophonic and Polyphonic AudioWorkletProcessor
|
|
78
|
+
*/
|
|
79
|
+
class FaustAudioWorkletProcessor<Poly extends boolean = false> extends AudioWorkletProcessor {
|
|
80
|
+
|
|
81
|
+
// Use ! syntax when the field is not defined in the constructor
|
|
82
|
+
protected fDSPCode!: Poly extends true ? FaustPolyWebAudioDsp : FaustMonoWebAudioDsp;
|
|
83
|
+
|
|
84
|
+
protected paramValuesCache: Record<string, number> = {};
|
|
85
|
+
|
|
86
|
+
constructor(options: FaustAudioWorkletNodeOptions<Poly>) {
|
|
87
|
+
super(options);
|
|
88
|
+
|
|
89
|
+
// Setup port message handling
|
|
90
|
+
this.port.onmessage = (e: MessageEvent) => this.handleMessageAux(e);
|
|
91
|
+
|
|
92
|
+
const { parameterDescriptors } = (this.constructor as typeof AudioWorkletProcessor);
|
|
93
|
+
parameterDescriptors.forEach((pd) => {
|
|
94
|
+
this.paramValuesCache[pd.name] = pd.defaultValue || 0;
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static get parameterDescriptors() {
|
|
99
|
+
const params = [] as AudioParamDescriptor[];
|
|
100
|
+
// Analyse voice JSON to generate AudioParam parameters
|
|
101
|
+
const callback = (item: FaustUIItem) => {
|
|
102
|
+
const param = analysePolyParameters(item);
|
|
103
|
+
if (param) params.push(param);
|
|
104
|
+
}
|
|
105
|
+
FaustBaseWebAudioDsp.parseUI(dspMeta.ui, callback);
|
|
106
|
+
// Analyse effect JSON to generate AudioParam parameters
|
|
107
|
+
if (effectMeta) FaustBaseWebAudioDsp.parseUI(effectMeta.ui, callback);
|
|
108
|
+
return params;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
process(inputs: Float32Array[][], outputs: Float32Array[][], parameters: { [key: string]: Float32Array }) {
|
|
112
|
+
|
|
113
|
+
// Update controls (possibly needed for sample accurate control)
|
|
114
|
+
for (const path in parameters) {
|
|
115
|
+
const [paramValue] = parameters[path];
|
|
116
|
+
if (paramValue !== this.paramValuesCache[path]) {
|
|
117
|
+
this.fDSPCode.setParamValue(path, paramValue);
|
|
118
|
+
this.paramValuesCache[path] = paramValue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return this.fDSPCode.compute(inputs[0], outputs[0]);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
protected handleMessageAux(e: MessageEvent) { // use arrow function for binding
|
|
126
|
+
const msg = e.data;
|
|
127
|
+
|
|
128
|
+
switch (msg.type) {
|
|
129
|
+
// Generic MIDI message
|
|
130
|
+
case "midi": this.midiMessage(msg.data); break;
|
|
131
|
+
// Typed MIDI message
|
|
132
|
+
case "ctrlChange": this.ctrlChange(msg.data[0], msg.data[1], msg.data[2]); break;
|
|
133
|
+
case "pitchWheel": this.pitchWheel(msg.data[0], msg.data[1]); break;
|
|
134
|
+
// Generic data message
|
|
135
|
+
case "param": this.setParamValue(msg.data.path, msg.data.value); break;
|
|
136
|
+
// Plot handler set on demand
|
|
137
|
+
case "setPlotHandler": {
|
|
138
|
+
if (msg.data) {
|
|
139
|
+
this.fDSPCode.setPlotHandler((output, index, events) => this.port.postMessage({ type: "plot", value: output, index, events }));
|
|
140
|
+
} else {
|
|
141
|
+
this.fDSPCode.setPlotHandler(null);
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
case "start": {
|
|
146
|
+
this.fDSPCode.start();
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case "stop": {
|
|
150
|
+
this.fDSPCode.stop();
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case "destroy": {
|
|
154
|
+
this.port.close();
|
|
155
|
+
this.fDSPCode.destroy();
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
default:
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
protected setParamValue(path: string, value: number) {
|
|
164
|
+
this.fDSPCode.setParamValue(path, value);
|
|
165
|
+
this.paramValuesCache[path] = value;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
protected midiMessage(data: number[] | Uint8Array) {
|
|
169
|
+
this.fDSPCode.midiMessage(data);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
protected ctrlChange(channel: number, ctrl: number, value: number) {
|
|
173
|
+
this.fDSPCode.ctrlChange(channel, ctrl, value);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected pitchWheel(channel: number, wheel: number) {
|
|
177
|
+
this.fDSPCode.pitchWheel(channel, wheel);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Monophonic AudioWorkletProcessor
|
|
183
|
+
*/
|
|
184
|
+
class FaustMonoAudioWorkletProcessor extends FaustAudioWorkletProcessor<false> {
|
|
185
|
+
|
|
186
|
+
constructor(options: FaustAudioWorkletNodeOptions) {
|
|
187
|
+
super(options);
|
|
188
|
+
const { FaustMonoWebAudioDsp } = dependencies as FaustAudioWorkletProcessorDependencies<false>;
|
|
189
|
+
const { factory, sampleSize } = options.processorOptions;
|
|
190
|
+
|
|
191
|
+
const instance = FaustWasmInstantiator.createSyncMonoDSPInstance(factory);
|
|
192
|
+
// Create Monophonic DSP
|
|
193
|
+
this.fDSPCode = new FaustMonoWebAudioDsp(instance, sampleRate, sampleSize, 128);
|
|
194
|
+
|
|
195
|
+
// Setup output handler
|
|
196
|
+
this.fDSPCode.setOutputParamHandler((path, value) => this.port.postMessage({ path, value, type: "param" }));
|
|
197
|
+
|
|
198
|
+
this.fDSPCode.start();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Polyphonic AudioWorkletProcessor
|
|
204
|
+
*/
|
|
205
|
+
class FaustPolyAudioWorkletProcessor extends FaustAudioWorkletProcessor<true> {
|
|
206
|
+
|
|
207
|
+
constructor(options: FaustPolyAudioWorkletNodeOptions) {
|
|
208
|
+
super(options);
|
|
209
|
+
const { FaustPolyWebAudioDsp } = dependencies as FaustAudioWorkletProcessorDependencies<true>;
|
|
210
|
+
|
|
211
|
+
const { voiceFactory, mixerModule, voices, effectFactory, sampleSize } = options.processorOptions;
|
|
212
|
+
|
|
213
|
+
const instance = FaustWasmInstantiator.createSyncPolyDSPInstance(voiceFactory, mixerModule, voices, effectFactory);
|
|
214
|
+
// Create Polyphonic DSP
|
|
215
|
+
this.fDSPCode = new FaustPolyWebAudioDsp(instance, sampleRate, sampleSize, 128);
|
|
216
|
+
|
|
217
|
+
// Setup port message handling
|
|
218
|
+
this.port.onmessage = (e: MessageEvent) => this.handleMessageAux(e);
|
|
219
|
+
|
|
220
|
+
// Setup output handler
|
|
221
|
+
this.fDSPCode.setOutputParamHandler((path, value) => this.port.postMessage({ path, value, type: "param" }));
|
|
222
|
+
|
|
223
|
+
this.fDSPCode.start();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
protected midiMessage(data: number[] | Uint8Array) {
|
|
227
|
+
const cmd = data[0] >> 4;
|
|
228
|
+
const channel = data[0] & 0xf;
|
|
229
|
+
const data1 = data[1];
|
|
230
|
+
const data2 = data[2];
|
|
231
|
+
if (cmd === 8 || (cmd === 9 && data2 === 0)) this.keyOff(channel, data1, data2);
|
|
232
|
+
else if (cmd === 9) this.keyOn(channel, data1, data2);
|
|
233
|
+
else super.midiMessage(data);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
protected handleMessageAux = (e: MessageEvent) => { // use arrow function for binding
|
|
237
|
+
const msg = e.data;
|
|
238
|
+
switch (msg.type) {
|
|
239
|
+
case "keyOn": this.keyOn(msg.data[0], msg.data[1], msg.data[2]); break;
|
|
240
|
+
case "keyOff": this.keyOff(msg.data[0], msg.data[1], msg.data[2]); break;
|
|
241
|
+
default:
|
|
242
|
+
super.handleMessageAux(e);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Public API
|
|
248
|
+
keyOn(channel: number, pitch: number, velocity: number) {
|
|
249
|
+
this.fDSPCode.keyOn(channel, pitch, velocity);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
keyOff(channel: number, pitch: number, velocity: number) {
|
|
253
|
+
this.fDSPCode.keyOff(channel, pitch, velocity);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
allNotesOff(hard: boolean) {
|
|
257
|
+
this.fDSPCode.allNotesOff(hard);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const Processor = poly ? FaustPolyAudioWorkletProcessor : FaustMonoAudioWorkletProcessor;
|
|
262
|
+
if (register) {
|
|
263
|
+
try {
|
|
264
|
+
registerProcessor(processorName || dspName || (poly ? "mydsp_poly" : "mydsp"), Processor);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.warn(error);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return poly ? FaustPolyAudioWorkletProcessor : FaustMonoAudioWorkletProcessor;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export default getFaustAudioWorkletProcessor;
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { Sha256 } from "@aws-crypto/sha256-js";
|
|
2
|
+
import type { ILibFaust } from "./LibFaust";
|
|
3
|
+
import type { FaustDspFactory, IntVector } from "./types";
|
|
4
|
+
|
|
5
|
+
export const ab2str = (buf: Uint8Array) => String.fromCharCode.apply(null, buf);
|
|
6
|
+
|
|
7
|
+
export const str2ab = (str: string) => {
|
|
8
|
+
const buf = new ArrayBuffer(str.length);
|
|
9
|
+
const bufView = new Uint8Array(buf);
|
|
10
|
+
for (let i = 0, strLen = str.length; i < strLen; i++) {
|
|
11
|
+
bufView[i] = str.charCodeAt(i);
|
|
12
|
+
}
|
|
13
|
+
return bufView;
|
|
14
|
+
};
|
|
15
|
+
const sha256 = async (str: string) => {
|
|
16
|
+
const sha256 = new Sha256();
|
|
17
|
+
sha256.update(str);
|
|
18
|
+
const hashArray = Array.from(await sha256.digest());
|
|
19
|
+
const hashHex = hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
|
|
20
|
+
return hashHex;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface IFaustCompiler {
|
|
24
|
+
/**
|
|
25
|
+
* Gives the Faust compiler version.
|
|
26
|
+
* @return a version string
|
|
27
|
+
*/
|
|
28
|
+
version(): string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Gives the last compilation error.
|
|
32
|
+
* @return an error string
|
|
33
|
+
*/
|
|
34
|
+
getErrorMessage(): string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create a wasm factory from Faust code i.e. wasm compiled code, to be used to create monophonic instances.
|
|
38
|
+
* This function is running asynchronously.
|
|
39
|
+
*
|
|
40
|
+
* @param name - an arbitrary name for the Faust factory
|
|
41
|
+
* @param code - Faust dsp code
|
|
42
|
+
* @param args - the compiler options
|
|
43
|
+
* @returns returns the wasm factory
|
|
44
|
+
*/
|
|
45
|
+
createMonoDSPFactory(name: string, code: string, args: string): Promise<FaustDspFactory | null>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create a wasm factory from Faust code i.e. wasm compiled code, to be used to create polyphonic instances.
|
|
49
|
+
* This function is running asynchronously.
|
|
50
|
+
*
|
|
51
|
+
* @param name - an arbitrary name for the Faust factory
|
|
52
|
+
* @param code - Faust dsp code
|
|
53
|
+
* @param args - the compiler options
|
|
54
|
+
* @returns returns the wasm factory
|
|
55
|
+
*/
|
|
56
|
+
createPolyDSPFactory(name: string, code: string, args: string): Promise<FaustDspFactory | null>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Delete a dsp factory.
|
|
60
|
+
*
|
|
61
|
+
* @param factory - the factory to be deleted
|
|
62
|
+
*/
|
|
63
|
+
deleteDSPFactory(factory: FaustDspFactory): void;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Expand Faust code i.e. linearize included libraries.
|
|
67
|
+
*
|
|
68
|
+
* @param code - Faust dsp code
|
|
69
|
+
* @param args - the compiler options
|
|
70
|
+
* @returns returns the expanded dsp code
|
|
71
|
+
*/
|
|
72
|
+
expandDSP(code: string, args: string): string | null;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generates auxiliary files from Faust code. The output depends on the compiler options.
|
|
76
|
+
*
|
|
77
|
+
* @param name - an arbitrary name for the Faust module
|
|
78
|
+
* @param code - Faust dsp code
|
|
79
|
+
* @param args - the compiler options
|
|
80
|
+
* @returns whether the generation actually succeded
|
|
81
|
+
*/
|
|
82
|
+
generateAuxFiles(name: string, code: string, args: string): boolean;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Delete all factories.
|
|
86
|
+
*/
|
|
87
|
+
deleteAllDSPFactories(): void;
|
|
88
|
+
|
|
89
|
+
fs(): typeof FS;
|
|
90
|
+
|
|
91
|
+
getAsyncInternalMixerModule(isDouble?: boolean): Promise<{ mixerBuffer: Uint8Array; mixerModule: WebAssembly.Module }>;
|
|
92
|
+
getSyncInternalMixerModule(isDouble?: boolean): { mixerBuffer: Uint8Array; mixerModule: WebAssembly.Module };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
class FaustCompiler implements IFaustCompiler {
|
|
96
|
+
private fLibFaust: ILibFaust;
|
|
97
|
+
private fErrorMessage: string;
|
|
98
|
+
private static gFactories: Map<string, FaustDspFactory> = new Map<string, FaustDspFactory>();
|
|
99
|
+
private mixer32Buffer!: Uint8Array;
|
|
100
|
+
private mixer64Buffer!: Uint8Array;
|
|
101
|
+
private mixer32Module!: WebAssembly.Module;
|
|
102
|
+
private mixer64Module!: WebAssembly.Module;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get a stringified DSP factories table
|
|
106
|
+
*/
|
|
107
|
+
static serializeDSPFactories() {
|
|
108
|
+
const table: Record<string, { code: string, json: any; poly: boolean }> = {};
|
|
109
|
+
this.gFactories.forEach((factory, shaKey) => {
|
|
110
|
+
const { code, json, poly } = factory;
|
|
111
|
+
table[shaKey] = { code: btoa(ab2str(code)), json: JSON.parse(json), poly };
|
|
112
|
+
});
|
|
113
|
+
return table;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get a stringified DSP factories table as string
|
|
117
|
+
*/
|
|
118
|
+
static stringifyDSPFactories() {
|
|
119
|
+
return JSON.stringify(this.serializeDSPFactories());
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Import a DSP factories table
|
|
123
|
+
*/
|
|
124
|
+
static deserializeDSPFactories(table: Record<string, { code: string, json: any; poly: boolean }>) {
|
|
125
|
+
const awaited: Promise<Map<string, FaustDspFactory>>[] = [];
|
|
126
|
+
for (const shaKey in table) {
|
|
127
|
+
const factory = table[shaKey];
|
|
128
|
+
const { code, json, poly } = factory;
|
|
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 })));
|
|
131
|
+
}
|
|
132
|
+
return Promise.all(awaited);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Import a stringified DSP factories table
|
|
136
|
+
*/
|
|
137
|
+
static importDSPFactories(tableStr: string) {
|
|
138
|
+
const table: Record<string, { code: string, json: any; poly: boolean }> = JSON.parse(tableStr);
|
|
139
|
+
return this.deserializeDSPFactories(table);
|
|
140
|
+
}
|
|
141
|
+
constructor(libFaust: ILibFaust) {
|
|
142
|
+
this.fLibFaust = libFaust;
|
|
143
|
+
this.fErrorMessage = "";
|
|
144
|
+
}
|
|
145
|
+
private intVec2intArray(vec: IntVector) {
|
|
146
|
+
const size = vec.size();
|
|
147
|
+
const ui8Code = new Uint8Array(size);
|
|
148
|
+
for (let i = 0; i < size; i++) {
|
|
149
|
+
ui8Code[i] = vec.get(i);
|
|
150
|
+
}
|
|
151
|
+
return ui8Code;
|
|
152
|
+
}
|
|
153
|
+
private async createDSPFactory(name: string, code: string, args: string, poly: boolean) {
|
|
154
|
+
// Cleanup the cache
|
|
155
|
+
if (FaustCompiler.gFactories.size > 10) {
|
|
156
|
+
FaustCompiler.gFactories.clear();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// If code is already compiled, return the cached factory
|
|
160
|
+
let shaKey = await sha256(name + code + args + (poly ? "poly" : "mono"));
|
|
161
|
+
if (FaustCompiler.gFactories.has(shaKey)) {
|
|
162
|
+
return FaustCompiler.gFactories.get(shaKey) || null;
|
|
163
|
+
} else {
|
|
164
|
+
try {
|
|
165
|
+
// Can possibly raise a C++ exception catched by the second catch()
|
|
166
|
+
const faustDspWasm = this.fLibFaust.createDSPFactory(name, code, args, !poly);
|
|
167
|
+
const ui8Code = this.intVec2intArray(faustDspWasm.data);
|
|
168
|
+
faustDspWasm.data.delete();
|
|
169
|
+
const module = await WebAssembly.compile(ui8Code);
|
|
170
|
+
const factory: FaustDspFactory = { shaKey, cfactory: faustDspWasm.cfactory, code: ui8Code, module, json: faustDspWasm.json, poly };
|
|
171
|
+
// Factory C++ side can be deallocated immediately
|
|
172
|
+
this.deleteDSPFactory(factory);
|
|
173
|
+
// Keep the compiled factory in the cache
|
|
174
|
+
FaustCompiler.gFactories.set(shaKey, factory);
|
|
175
|
+
return factory;
|
|
176
|
+
} catch (e) {
|
|
177
|
+
this.fErrorMessage = this.fLibFaust.getErrorAfterException();
|
|
178
|
+
// console.error(`=> exception raised while running createDSPFactory: ${this.fErrorMessage}`, e);
|
|
179
|
+
this.fLibFaust.cleanupAfterException();
|
|
180
|
+
throw this.fErrorMessage ? new Error(this.fErrorMessage) : e;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
version() {
|
|
185
|
+
return this.fLibFaust.version();
|
|
186
|
+
}
|
|
187
|
+
getErrorMessage() {
|
|
188
|
+
return this.fErrorMessage;
|
|
189
|
+
}
|
|
190
|
+
async createMonoDSPFactory(name: string, code: string, args: string) {
|
|
191
|
+
return this.createDSPFactory(name, code, args, false);
|
|
192
|
+
}
|
|
193
|
+
async createPolyDSPFactory(name: string, code: string, args: string) {
|
|
194
|
+
return this.createDSPFactory(name, code, args, true);
|
|
195
|
+
}
|
|
196
|
+
deleteDSPFactory(factory: FaustDspFactory) {
|
|
197
|
+
this.fLibFaust.deleteDSPFactory(factory.cfactory);
|
|
198
|
+
factory.cfactory = 0;
|
|
199
|
+
}
|
|
200
|
+
expandDSP(code: string, args: string) {
|
|
201
|
+
try {
|
|
202
|
+
return this.fLibFaust.expandDSP("FaustDSP", code, args);
|
|
203
|
+
} catch (e) {
|
|
204
|
+
this.fErrorMessage = this.fLibFaust.getErrorAfterException();
|
|
205
|
+
// console.error(`=> exception raised while running expandDSP: ${this.fErrorMessage}`);
|
|
206
|
+
this.fLibFaust.cleanupAfterException();
|
|
207
|
+
throw this.fErrorMessage ? new Error(this.fErrorMessage) : e;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
generateAuxFiles(name: string, code: string, args: string) {
|
|
211
|
+
try {
|
|
212
|
+
return this.fLibFaust.generateAuxFiles(name, code, args);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
this.fErrorMessage = this.fLibFaust.getErrorAfterException();
|
|
215
|
+
// console.error(`=> exception raised while running generateAuxFiles: ${this.fErrorMessage}`);
|
|
216
|
+
this.fLibFaust.cleanupAfterException();
|
|
217
|
+
throw this.fErrorMessage ? new Error(this.fErrorMessage) : e;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
deleteAllDSPFactories(): void {
|
|
221
|
+
this.fLibFaust.deleteAllDSPFactories();
|
|
222
|
+
}
|
|
223
|
+
fs() {
|
|
224
|
+
return this.fLibFaust.fs();
|
|
225
|
+
}
|
|
226
|
+
async getAsyncInternalMixerModule(isDouble = false) {
|
|
227
|
+
const bufferKey = isDouble ? "mixer64Buffer" : "mixer32Buffer";
|
|
228
|
+
const moduleKey = isDouble ? "mixer64Module" : "mixer32Module";
|
|
229
|
+
if (this[moduleKey]) return { mixerBuffer: this[bufferKey], mixerModule: this[moduleKey] };
|
|
230
|
+
const path = isDouble ? "/usr/rsrc/mixer64.wasm" : "/usr/rsrc/mixer32.wasm";
|
|
231
|
+
const mixerBuffer = this.fs().readFile(path, { encoding: "binary" });
|
|
232
|
+
this[bufferKey] = mixerBuffer;
|
|
233
|
+
// Compile mixer
|
|
234
|
+
const mixerModule = await WebAssembly.compile(mixerBuffer);
|
|
235
|
+
this[moduleKey] = mixerModule;
|
|
236
|
+
return { mixerBuffer, mixerModule };
|
|
237
|
+
}
|
|
238
|
+
getSyncInternalMixerModule(isDouble = false) {
|
|
239
|
+
const bufferKey = isDouble ? "mixer64Buffer" : "mixer32Buffer";
|
|
240
|
+
const moduleKey = isDouble ? "mixer64Module" : "mixer32Module";
|
|
241
|
+
if (this[moduleKey]) return { mixerBuffer: this[bufferKey], mixerModule: this[moduleKey] };
|
|
242
|
+
const path = isDouble ? "/usr/rsrc/mixer64.wasm" : "/usr/rsrc/mixer32.wasm";
|
|
243
|
+
const mixerBuffer = this.fs().readFile(path, { encoding: "binary" });
|
|
244
|
+
this[bufferKey] = mixerBuffer;
|
|
245
|
+
// Compile mixer
|
|
246
|
+
const mixerModule = new WebAssembly.Module(mixerBuffer);
|
|
247
|
+
this[moduleKey] = mixerModule;
|
|
248
|
+
return { mixerBuffer, mixerModule };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export default FaustCompiler;
|