@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.
Files changed (111) hide show
  1. package/README.md +181 -0
  2. package/assets/standalone/faust-ui/index.css +414 -0
  3. package/assets/standalone/faust-ui/index.css.map +1 -0
  4. package/assets/standalone/faust-ui/index.d.ts +1 -0
  5. package/assets/standalone/faust-ui/index.js +3478 -0
  6. package/assets/standalone/faust-ui/index.js.map +1 -0
  7. package/assets/standalone/faustwasm/index.d.ts +1252 -0
  8. package/assets/standalone/faustwasm/index.js +3772 -0
  9. package/assets/standalone/faustwasm/index.js.map +7 -0
  10. package/assets/standalone/index.html +73 -0
  11. package/assets/standalone/index.js +198 -0
  12. package/assets/standalone/types.d.ts +9 -0
  13. package/dist/cjs/index.d.ts +1252 -0
  14. package/dist/cjs/index.js +3752 -0
  15. package/dist/cjs/index.js.map +7 -0
  16. package/dist/cjs-bundle/index.d.ts +1259 -0
  17. package/dist/cjs-bundle/index.js +11507 -0
  18. package/dist/cjs-bundle/index.js.map +7 -0
  19. package/dist/esm/index.d.ts +1252 -0
  20. package/dist/esm/index.js +3772 -0
  21. package/dist/esm/index.js.map +7 -0
  22. package/dist/esm-bundle/index.d.ts +1259 -0
  23. package/dist/esm-bundle/index.js +11495 -0
  24. package/dist/esm-bundle/index.js.map +7 -0
  25. package/fileutils.js +40 -0
  26. package/libfaust-wasm/libfaust-wasm.d.cts +3 -0
  27. package/libfaust-wasm/libfaust-wasm.d.ts +3 -0
  28. package/libfaust-wasm/libfaust-wasm.data +0 -0
  29. package/libfaust-wasm/libfaust-wasm.data.d.ts +2 -0
  30. package/libfaust-wasm/libfaust-wasm.js +21 -0
  31. package/libfaust-wasm/libfaust-wasm.wasm +0 -0
  32. package/libfaust-wasm/libfaust-wasm.wasm.d.ts +2 -0
  33. package/library.md +91 -0
  34. package/package.json +43 -0
  35. package/postbuild-bundled.js +11 -0
  36. package/postbuild.js +63 -0
  37. package/prebuild-bundled.js +11 -0
  38. package/rsrc/overview.png +0 -0
  39. package/rsrc/overview.xlsx +0 -0
  40. package/scripts/faust2sndfile.js +43 -0
  41. package/scripts/faust2svg.js +17 -0
  42. package/scripts/faust2wasm.js +29 -0
  43. package/src/FaustAudioWorkletNode.ts +264 -0
  44. package/src/FaustAudioWorkletProcessor.ts +273 -0
  45. package/src/FaustCompiler.ts +252 -0
  46. package/src/FaustDspGenerator.ts +498 -0
  47. package/src/FaustDspInstance.ts +141 -0
  48. package/src/FaustFFTAudioWorkletProcessor.ts +554 -0
  49. package/src/FaustOfflineProcessor.ts +141 -0
  50. package/src/FaustScriptProcessorNode.ts +80 -0
  51. package/src/FaustSvgDiagrams.ts +37 -0
  52. package/src/FaustWasmInstantiator.ts +202 -0
  53. package/src/FaustWebAudioDsp.ts +1060 -0
  54. package/src/LibFaust.ts +57 -0
  55. package/src/WavDecoder.ts +207 -0
  56. package/src/WavEncoder.ts +204 -0
  57. package/src/copyWebStandaloneAssets.d.ts +3 -0
  58. package/src/copyWebStandaloneAssets.js +20 -0
  59. package/src/exports-bundle.ts +5 -0
  60. package/src/exports.ts +24 -0
  61. package/src/faust2svgFiles.d.ts +3 -0
  62. package/src/faust2svgFiles.js +43 -0
  63. package/src/faust2wasmFiles.d.ts +5 -0
  64. package/src/faust2wasmFiles.js +93 -0
  65. package/src/faust2wavFiles.d.ts +3 -0
  66. package/src/faust2wavFiles.js +58 -0
  67. package/src/index-bundle-iife.ts +7 -0
  68. package/src/index-bundle.ts +5 -0
  69. package/src/index.ts +5 -0
  70. package/src/instantiateFaustModule.ts +32 -0
  71. package/src/instantiateFaustModuleFromFile.ts +57 -0
  72. package/src/types.ts +234 -0
  73. package/test/clarinet.dsp +1 -0
  74. package/test/faustlive-wasm/faust-ui/index.css +414 -0
  75. package/test/faustlive-wasm/faust-ui/index.css.map +1 -0
  76. package/test/faustlive-wasm/faust-ui/index.d.ts +1 -0
  77. package/test/faustlive-wasm/faust-ui/index.js +3478 -0
  78. package/test/faustlive-wasm/faust-ui/index.js.map +1 -0
  79. package/test/faustlive-wasm/faustwasm/index.d.ts +1252 -0
  80. package/test/faustlive-wasm/faustwasm/index.js +3772 -0
  81. package/test/faustlive-wasm/faustwasm/index.js.map +7 -0
  82. package/test/faustlive-wasm/index.css +97 -0
  83. package/test/faustlive-wasm/index.html +130 -0
  84. package/test/faustlive-wasm/index.js +638 -0
  85. package/test/guitar.dsp +1 -0
  86. package/test/guitar1.dsp +2 -0
  87. package/test/libfaust-in-worklet/index.html +13 -0
  88. package/test/libfaust-in-worklet/index.js +40 -0
  89. package/test/mono.dsp +8 -0
  90. package/test/node/test.js +17 -0
  91. package/test/noise.dsp +3 -0
  92. package/test/organ.dsp +5 -0
  93. package/test/organ1.dsp +6 -0
  94. package/test/osc.dsp +4 -0
  95. package/test/osc2.dsp +5 -0
  96. package/test/osc3.dsp +4 -0
  97. package/test/p-dj.dsp +4 -0
  98. package/test/poly.dsp +13 -0
  99. package/test/rev.dsp +2 -0
  100. package/test/t1.dsp +1 -0
  101. package/test/web/fft.html +87 -0
  102. package/test/web/fft1.html +88 -0
  103. package/test/web/fft2.html +114 -0
  104. package/test/web/fftw.js +1907 -0
  105. package/test/web/index.html +12 -0
  106. package/test/web/index.js +235 -0
  107. package/test/web/kissfft.js +904 -0
  108. package/test/web/mono.html +46 -0
  109. package/test/web/poly-key.html +62 -0
  110. package/test/web/poly.html +59 -0
  111. 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;