@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,141 @@
1
+ import { ComputeHandler, FaustBaseWebAudioDsp, FaustMonoWebAudioDsp, FaustPolyWebAudioDsp, IFaustBaseWebAudioDsp, IFaustMonoWebAudioDsp, IFaustPolyWebAudioDsp, MetadataHandler, OutputParamHandler, PlotHandler } from "./FaustWebAudioDsp";
2
+ import { AudioParamDescriptor, FaustUIItem } from "./types";
3
+
4
+ /**
5
+ * For offline rendering.
6
+ */
7
+ export interface IFaustOfflineProcessor extends IFaustBaseWebAudioDsp {
8
+ render(inputs?: Float32Array[], length?: number, onUpdate?: (sample: number) => any): Float32Array[];
9
+ }
10
+
11
+ export interface IFaustMonoOfflineProcessor extends IFaustOfflineProcessor, IFaustMonoWebAudioDsp {}
12
+ export interface IFaustPolyOfflineProcessor extends IFaustOfflineProcessor, IFaustPolyWebAudioDsp {}
13
+
14
+ export class FaustOfflineProcessor<Poly extends boolean = false> {
15
+ protected fDSPCode!: Poly extends true ? FaustPolyWebAudioDsp : FaustMonoWebAudioDsp;
16
+
17
+ protected fBufferSize: number;
18
+ protected fInputs: Float32Array[];
19
+ protected fOutputs: Float32Array[];
20
+
21
+ constructor(instance: Poly extends true ? FaustPolyWebAudioDsp : FaustMonoWebAudioDsp, bufferSize: number) {
22
+ this.fDSPCode = instance;
23
+ this.fBufferSize = bufferSize;
24
+ this.fInputs = new Array(this.fDSPCode.getNumInputs()).fill(null).map(() => new Float32Array(bufferSize));
25
+ this.fOutputs = new Array(this.fDSPCode.getNumOutputs()).fill(null).map(() => new Float32Array(bufferSize));
26
+ }
27
+
28
+ // Public API
29
+
30
+ getParameterDescriptors() {
31
+ const params = [] as AudioParamDescriptor[];
32
+ // Analyse voice JSON to generate AudioParam parameters
33
+ const callback = (item: FaustUIItem) => {
34
+ let param: AudioParamDescriptor | null = null;
35
+ const polyKeywords = ["/gate", "/freq", "/gain", "/key", "/vel", "/velocity"];
36
+ const isPolyReserved = "address" in item && !!polyKeywords.find(k => item.address.endsWith(k));
37
+ if (this.fDSPCode instanceof FaustMonoWebAudioDsp || !isPolyReserved) {
38
+ if (item.type === "vslider" || item.type === "hslider" || item.type === "nentry") {
39
+ param = { name: item.address, defaultValue: item.init || 0, minValue: item.min || 0, maxValue: item.max || 0 };
40
+ } else if (item.type === "button" || item.type === "checkbox") {
41
+ param = { name: item.address, defaultValue: item.init || 0, minValue: 0, maxValue: 1 };
42
+ }
43
+ }
44
+ if (param) params.push(param);
45
+ }
46
+ FaustBaseWebAudioDsp.parseUI(this.fDSPCode.getUI(), callback);
47
+ return params;
48
+ }
49
+ compute(input: Float32Array[], output: Float32Array[]) { return this.fDSPCode.compute(input, output); }
50
+
51
+ setOutputParamHandler(handler: OutputParamHandler) { this.fDSPCode.setOutputParamHandler(handler); }
52
+ getOutputParamHandler() { return this.fDSPCode.getOutputParamHandler(); }
53
+
54
+ setComputeHandler(handler: ComputeHandler) { this.fDSPCode.setComputeHandler(handler); }
55
+ getComputeHandler() { return this.fDSPCode.getComputeHandler(); }
56
+
57
+ setPlotHandler(handler: PlotHandler) { this.fDSPCode.setPlotHandler(handler); }
58
+ getPlotHandler() { return this.fDSPCode.getPlotHandler(); }
59
+
60
+ getNumInputs() { return this.fDSPCode.getNumInputs(); }
61
+ getNumOutputs() { return this.fDSPCode.getNumOutputs(); }
62
+
63
+ metadata(handler: MetadataHandler) { }
64
+
65
+ midiMessage(data: number[] | Uint8Array) { this.fDSPCode.midiMessage(data); }
66
+
67
+ ctrlChange(chan: number, ctrl: number, value: number) { this.fDSPCode.ctrlChange(chan, ctrl, value); }
68
+ pitchWheel(chan: number, value: number) { this.fDSPCode.pitchWheel(chan, value); }
69
+
70
+ setParamValue(path: string, value: number) { this.fDSPCode.setParamValue(path, value); }
71
+ getParamValue(path: string) { return this.fDSPCode.getParamValue(path); }
72
+ getParams() { return this.fDSPCode.getParams(); }
73
+
74
+ getMeta() { return this.fDSPCode.getMeta(); }
75
+ getJSON() { return this.fDSPCode.getJSON(); }
76
+ getDescriptors() { return this.fDSPCode.getDescriptors(); }
77
+ getUI() { return this.fDSPCode.getUI(); }
78
+
79
+ start() { this.fDSPCode.start(); }
80
+ stop() { this.fDSPCode.stop(); }
81
+
82
+ destroy() { this.fDSPCode.destroy(); }
83
+
84
+ /**
85
+ * Render frames in an array.
86
+ *
87
+ * @param inputs - input signal
88
+ * @param length - the number of frames to render (default: bufferSize)
89
+ * @param onUpdate - a callback after each buffer calculated, with an argument "current sample"
90
+ * @return an array of Float32Array with the rendered frames
91
+ */
92
+ render(inputs: Float32Array[] = [], length = this.fBufferSize, onUpdate?: (sample: number) => any): Float32Array[] {
93
+ let l = 0;
94
+ const outputs = new Array(this.fDSPCode.getNumOutputs()).fill(null).map(() => new Float32Array(length));
95
+ // The node has to be started before rendering
96
+ this.fDSPCode.start();
97
+ while (l < length) {
98
+ const sliceLength = Math.min(length - l, this.fBufferSize);
99
+ for (let i = 0; i < this.fDSPCode.getNumInputs(); i++) {
100
+ let input: Float32Array;
101
+ if (inputs[i]) {
102
+ if (inputs[i].length <= l) {
103
+ input = new Float32Array(sliceLength);
104
+ } else if (inputs[i].length > l + sliceLength) {
105
+ input = inputs[i].subarray(l, l + sliceLength);
106
+ } else {
107
+ input = inputs[i].subarray(l, inputs[i].length);
108
+ }
109
+ } else {
110
+ input = new Float32Array(sliceLength);
111
+ }
112
+ this.fInputs[i] = input;
113
+ }
114
+ this.fDSPCode.compute(this.fInputs, this.fOutputs);
115
+ for (let i = 0; i < this.fDSPCode.getNumOutputs(); i++) {
116
+ const output = this.fOutputs[i];
117
+ if (sliceLength < this.fBufferSize) {
118
+ outputs[i].set(output.subarray(0, sliceLength), l);
119
+ } else {
120
+ outputs[i].set(output, l);
121
+ }
122
+ }
123
+ l += this.fBufferSize;
124
+ onUpdate?.(l);
125
+ }
126
+ // The node can be stopped after rendering
127
+ this.fDSPCode.stop();
128
+ return outputs;
129
+ }
130
+ }
131
+
132
+ export class FaustMonoOfflineProcessor extends FaustOfflineProcessor<false> implements IFaustMonoWebAudioDsp {
133
+ }
134
+
135
+ export class FaustPolyOfflineProcessor extends FaustOfflineProcessor<true> implements IFaustPolyWebAudioDsp {
136
+ keyOn(channel: number, pitch: number, velocity: number) { this.fDSPCode.keyOn(channel, pitch, velocity); }
137
+ keyOff(channel: number, pitch: number, velocity: number) { this.fDSPCode.keyOff(channel, pitch, velocity); }
138
+ allNotesOff(hard: boolean) { this.fDSPCode.allNotesOff(hard); }
139
+ }
140
+
141
+ export default FaustOfflineProcessor;
@@ -0,0 +1,80 @@
1
+ import type { ComputeHandler, FaustMonoWebAudioDsp, FaustPolyWebAudioDsp, IFaustMonoWebAudioDsp, IFaustPolyWebAudioDsp, MetadataHandler, OutputParamHandler, PlotHandler } from "./FaustWebAudioDsp";
2
+
3
+ /**
4
+ * Base class for Monophonic and Polyphonic ScriptProcessorNode
5
+ */
6
+ export class FaustScriptProcessorNode<Poly extends boolean = false> extends (globalThis.ScriptProcessorNode || null) {
7
+ protected fDSPCode!: Poly extends true ? FaustPolyWebAudioDsp : FaustMonoWebAudioDsp;
8
+
9
+ // Needed for ScriptProcessorNode
10
+ protected fInputs!: Float32Array[];
11
+ protected fOutputs!: Float32Array[];
12
+
13
+ init(instance: Poly extends true ? FaustPolyWebAudioDsp : FaustMonoWebAudioDsp) {
14
+ this.fDSPCode = instance;
15
+
16
+ this.fInputs = new Array(this.fDSPCode.getNumInputs());
17
+ this.fOutputs = new Array(this.fDSPCode.getNumOutputs());
18
+
19
+ this.onaudioprocess = (e) => {
20
+
21
+ // Read inputs
22
+ for (let chan = 0; chan < this.fDSPCode.getNumInputs(); chan++) {
23
+ this.fInputs[chan] = e.inputBuffer.getChannelData(chan);
24
+ }
25
+
26
+ // Read outputs
27
+ for (let chan = 0; chan < this.fDSPCode.getNumOutputs(); chan++) {
28
+ this.fOutputs[chan] = e.outputBuffer.getChannelData(chan);
29
+ }
30
+
31
+ return this.fDSPCode.compute(this.fInputs, this.fOutputs);
32
+ }
33
+
34
+ this.start();
35
+ }
36
+ // Public API
37
+ compute(input: Float32Array[], output: Float32Array[]) { return this.fDSPCode.compute(input, output); }
38
+
39
+ setOutputParamHandler(handler: OutputParamHandler) { this.fDSPCode.setOutputParamHandler(handler); }
40
+ getOutputParamHandler() { return this.fDSPCode.getOutputParamHandler(); }
41
+
42
+ setComputeHandler(handler: ComputeHandler) { this.fDSPCode.setComputeHandler(handler); }
43
+ getComputeHandler() { return this.fDSPCode.getComputeHandler(); }
44
+
45
+ setPlotHandler(handler: PlotHandler) { this.fDSPCode.setPlotHandler(handler); }
46
+ getPlotHandler() { return this.fDSPCode.getPlotHandler(); }
47
+
48
+ getNumInputs() { return this.fDSPCode.getNumInputs(); }
49
+ getNumOutputs() { return this.fDSPCode.getNumOutputs(); }
50
+
51
+ metadata(handler: MetadataHandler) { }
52
+
53
+ midiMessage(data: number[] | Uint8Array) { this.fDSPCode.midiMessage(data); }
54
+
55
+ ctrlChange(chan: number, ctrl: number, value: number) { this.fDSPCode.ctrlChange(chan, ctrl, value); }
56
+ pitchWheel(chan: number, value: number) { this.fDSPCode.pitchWheel(chan, value); }
57
+
58
+ setParamValue(path: string, value: number) { this.fDSPCode.setParamValue(path, value); }
59
+ getParamValue(path: string) { return this.fDSPCode.getParamValue(path); }
60
+ getParams() { return this.fDSPCode.getParams(); }
61
+
62
+ getMeta() { return this.fDSPCode.getMeta(); }
63
+ getJSON() { return this.fDSPCode.getJSON(); }
64
+ getDescriptors() { return this.fDSPCode.getDescriptors(); }
65
+ getUI() { return this.fDSPCode.getUI(); }
66
+
67
+ start() { this.fDSPCode.start(); }
68
+ stop() { this.fDSPCode.stop(); }
69
+
70
+ destroy() { this.fDSPCode.destroy(); }
71
+ }
72
+
73
+ export class FaustMonoScriptProcessorNode extends FaustScriptProcessorNode<false> implements IFaustMonoWebAudioDsp {
74
+ }
75
+
76
+ export class FaustPolyScriptProcessorNode extends FaustScriptProcessorNode<true> implements IFaustPolyWebAudioDsp {
77
+ keyOn(channel: number, pitch: number, velocity: number) { this.fDSPCode.keyOn(channel, pitch, velocity); }
78
+ keyOff(channel: number, pitch: number, velocity: number) { this.fDSPCode.keyOff(channel, pitch, velocity); }
79
+ allNotesOff(hard: boolean) { this.fDSPCode.allNotesOff(hard); }
80
+ }
@@ -0,0 +1,37 @@
1
+ import type FaustCompiler from "./FaustCompiler";
2
+
3
+ interface IFaustSvgDiagrams {
4
+ /**
5
+ * Generates auxiliary files from Faust code. The output depends on the compiler options.
6
+ *
7
+ * @param name - the DSP's name
8
+ * @param code - Faust code
9
+ * @param args - compilation args
10
+ * @returns the svg diagrams as a filename - svg string map
11
+ */
12
+ from(name: string, code: string, args: string): Record<string, string>;
13
+ }
14
+
15
+ class FaustSvgDiagrams implements IFaustSvgDiagrams {
16
+ private compiler: FaustCompiler;
17
+
18
+ constructor(compiler: FaustCompiler) {
19
+ this.compiler = compiler;
20
+ }
21
+
22
+ from(name: string, code: string, args: string) {
23
+ const fs = this.compiler.fs();
24
+ try {
25
+ const files: string[] = fs.readdir(`/${name}-svg/`);
26
+ files.filter(file => file !== "." && file !== "..").forEach(file => fs.unlink(`/${name}-svg/${file}`));
27
+ } catch {}
28
+ const success = this.compiler.generateAuxFiles(name, code, `-lang wasm -svg ${args}`);
29
+ if (!success) throw new Error(this.compiler.getErrorMessage());
30
+ const svgs: Record<string, string> = {};
31
+ const files: string[] = fs.readdir(`/${name}-svg/`);
32
+ files.filter(file => file !== "." && file !== "..").forEach(file => svgs[file] = fs.readFile(`/${name}-svg/${file}`, { encoding: "utf8" }) as string);
33
+ return svgs;
34
+ }
35
+ }
36
+
37
+ export default FaustSvgDiagrams;
@@ -0,0 +1,202 @@
1
+ import FaustDspInstance, { FaustMonoDspInstance, FaustPolyDspInstance, IFaustDspInstance, IFaustMixerInstance } from "./FaustDspInstance";
2
+ import type { FaustDspFactory, FaustDspMeta, LooseFaustDspFactory } from "./types";
3
+
4
+ class FaustWasmInstantiator {
5
+ private static createWasmImport(memory?: WebAssembly.Memory) {
6
+ return {
7
+ env: {
8
+ memory: memory || new WebAssembly.Memory({ initial: 100 }),
9
+ memoryBase: 0,
10
+ tableBase: 0,
11
+ // Integer version
12
+ _abs: Math.abs,
13
+ // Float version
14
+ _acosf: Math.acos, _asinf: Math.asin, _atanf: Math.atan, _atan2f: Math.atan2,
15
+ _ceilf: Math.ceil, _cosf: Math.cos, _expf: Math.exp, _floorf: Math.floor,
16
+ _fmodf: (x: number, y: number) => x % y,
17
+ _logf: Math.log, _log10f: Math.log10, _max_f: Math.max, _min_f: Math.min,
18
+ _remainderf: (x: number, y: number) => x - Math.round(x / y) * y,
19
+ _powf: Math.pow, _roundf: Math.fround, _sinf: Math.sin, _sqrtf: Math.sqrt, _tanf: Math.tan,
20
+ _acoshf: Math.acosh, _asinhf: Math.asinh, _atanhf: Math.atanh,
21
+ _coshf: Math.cosh, _sinhf: Math.sinh, _tanhf: Math.tanh,
22
+ _isnanf: Number.isNaN, _isinff: (x: number) => !isFinite(x),
23
+ _copysignf: (x: number, y: number) => (Math.sign(x) === Math.sign(y) ? x : -x),
24
+
25
+ // Double version
26
+ _acos: Math.acos, _asin: Math.asin, _atan: Math.atan, _atan2: Math.atan2,
27
+ _ceil: Math.ceil, _cos: Math.cos, _exp: Math.exp, _floor: Math.floor,
28
+ _fmod: (x: number, y: number) => x % y,
29
+ _log: Math.log, _log10: Math.log10, _max_: Math.max, _min_: Math.min,
30
+ _remainder: (x: number, y: number) => x - Math.round(x / y) * y,
31
+ _pow: Math.pow, _round: Math.fround, _sin: Math.sin, _sqrt: Math.sqrt, _tan: Math.tan,
32
+ _acosh: Math.acosh, _asinh: Math.asinh, _atanh: Math.atanh,
33
+ _cosh: Math.cosh, _sinh: Math.sinh, _tanh: Math.tanh,
34
+ _isnan: Number.isNaN, _isinf: (x: number) => !isFinite(x),
35
+ _copysign: (x: number, y: number) => (Math.sign(x) === Math.sign(y) ? x : -x),
36
+
37
+ table: new WebAssembly.Table({ initial: 0, element: "anyfunc" })
38
+ }
39
+ };
40
+ }
41
+ private static createWasmMemory(voicesIn: number, sampleSize: number, dspMeta: FaustDspMeta, effectMeta: FaustDspMeta, bufferSize: number) {
42
+ // Hack : at least 4 voices (to avoid weird wasm memory bug?)
43
+ const voices = Math.max(4, voicesIn);
44
+ // Memory allocator
45
+ const ptrSize = sampleSize; // Done on wast/wasm backend side
46
+ const pow2limit = (x: number) => {
47
+ let n = 65536; // Minimum = 64 kB
48
+ while (n < x) { n *= 2; }
49
+ return n;
50
+ };
51
+ const effectSize = effectMeta ? effectMeta.size : 0;
52
+ let memorySize = pow2limit(
53
+ effectSize
54
+ + dspMeta.size * voices
55
+ + (dspMeta.inputs + dspMeta.outputs * 2)
56
+ * (ptrSize + bufferSize * sampleSize)
57
+ ) / 65536;
58
+ memorySize = Math.max(2, memorySize); // At least 2
59
+ return new WebAssembly.Memory({ initial: memorySize, maximum: memorySize });
60
+ };
61
+ private static createMonoDSPInstanceAux(instance: WebAssembly.Instance, json: string) {
62
+ const functions = instance.exports as IFaustDspInstance & WebAssembly.Exports;
63
+ const api = new FaustDspInstance(functions);
64
+ const memory: any = instance.exports.memory;
65
+ return { memory, api, json } as FaustMonoDspInstance;
66
+ }
67
+ private static createMemoryAux(voices: number, voiceFactory: LooseFaustDspFactory, effectFactory?: LooseFaustDspFactory) {
68
+ // Parse JSON to get 'size' and 'inputs/outputs' infos
69
+ const voiceMeta: FaustDspMeta = JSON.parse(voiceFactory.json);
70
+ const effectMeta: FaustDspMeta = (effectFactory && effectFactory.json) ? JSON.parse(effectFactory.json) : null;
71
+ const sampleSize = voiceMeta.compile_options.match("-double") ? 8 : 4;
72
+ // Memory will be shared by voice, mixer and (possibly) effect instances
73
+ return this.createWasmMemory(voices, sampleSize, voiceMeta, effectMeta, 8192);
74
+ }
75
+ private static createMixerAux(mixerModule: WebAssembly.Module, memory: WebAssembly.Memory) {
76
+ // Create mixer instance
77
+ const mixerImport = {
78
+ imports: { print: console.log },
79
+ memory: { memory }
80
+ };
81
+ const mixerInstance = new WebAssembly.Instance(mixerModule, mixerImport);
82
+ const mixerFunctions = mixerInstance.exports as IFaustMixerInstance & WebAssembly.Exports;
83
+ return mixerFunctions;
84
+ }
85
+ // Public API
86
+ static async loadDSPFactory(wasmPath: string, jsonPath: string) {
87
+ const wasmFile = await fetch(wasmPath);
88
+ if (!wasmFile.ok) {
89
+ throw new Error(`=> exception raised while running loadDSPFactory, file not found: ${wasmPath}`);
90
+ }
91
+ try {
92
+ const wasmBuffer = await wasmFile.arrayBuffer();
93
+ const module = await WebAssembly.compile(wasmBuffer);
94
+ const jsonFile = await fetch(jsonPath);
95
+ const json = await jsonFile.text();
96
+ const meta: FaustDspMeta = JSON.parse(json);
97
+ const cOptions = meta.compile_options;
98
+ const poly = cOptions.indexOf('wasm-e') !== -1;
99
+ return { cfactory: 0, code: new Uint8Array(wasmBuffer), module, json, poly } as FaustDspFactory;
100
+ } catch (e) {
101
+ // console.error(`=> exception raised while running loadDSPFactory: ${e}`);
102
+ throw e;
103
+ }
104
+ }
105
+
106
+ static async loadDSPMixer(mixerPath: string, fs?: typeof FS) {
107
+ try {
108
+ let mixerBuffer = null;
109
+ if (fs) {
110
+ mixerBuffer = fs.readFile(mixerPath, { encoding: "binary" });
111
+ } else {
112
+ const mixerFile = await fetch(mixerPath);
113
+ mixerBuffer = await mixerFile.arrayBuffer();
114
+ }
115
+ // Compile mixer
116
+ return WebAssembly.compile(mixerBuffer);
117
+ } catch (e) {
118
+ // console.error(`=> exception raised while running loadMixer: ${e}`);
119
+ throw e;
120
+ }
121
+ }
122
+
123
+ static async createAsyncMonoDSPInstance(factory: LooseFaustDspFactory) {
124
+ const instance = await WebAssembly.instantiate(factory.module, this.createWasmImport());
125
+ return this.createMonoDSPInstanceAux(instance, factory.json);
126
+ }
127
+
128
+ static createSyncMonoDSPInstance(factory: LooseFaustDspFactory) {
129
+ const instance = new WebAssembly.Instance(factory.module, this.createWasmImport());
130
+ return this.createMonoDSPInstanceAux(instance, factory.json);
131
+ }
132
+
133
+ static async createAsyncPolyDSPInstance(voiceFactory: LooseFaustDspFactory, mixerModule: WebAssembly.Module, voices: number, effectFactory?: LooseFaustDspFactory): Promise<FaustPolyDspInstance> {
134
+ const memory = this.createMemoryAux(voices, voiceFactory, effectFactory);
135
+ // Create voice
136
+ const voiceInstance = await WebAssembly.instantiate(voiceFactory.module, this.createWasmImport(memory));
137
+ const voiceFunctions = voiceInstance.exports as IFaustDspInstance & WebAssembly.Exports;
138
+ const voiceAPI = new FaustDspInstance(voiceFunctions);
139
+ // Create mixer
140
+ const mixerAPI = this.createMixerAux(mixerModule, memory);
141
+
142
+ // Possibly create effect instance
143
+ if (effectFactory) {
144
+ const effectInstance = await WebAssembly.instantiate(effectFactory.module, this.createWasmImport(memory));
145
+ const effectFunctions = effectInstance.exports as IFaustDspInstance & WebAssembly.Exports;
146
+ const effectAPI = new FaustDspInstance(effectFunctions);
147
+ return {
148
+ memory,
149
+ voices,
150
+ voiceAPI,
151
+ effectAPI,
152
+ mixerAPI,
153
+ voiceJSON: voiceFactory.json,
154
+ effectJSON: effectFactory.json
155
+ };
156
+ } else {
157
+ return {
158
+ memory,
159
+ voices,
160
+ voiceAPI,
161
+ mixerAPI,
162
+ voiceJSON: voiceFactory.json
163
+ };
164
+ }
165
+ }
166
+
167
+ static createSyncPolyDSPInstance(voiceFactory: LooseFaustDspFactory, mixerModule: WebAssembly.Module, voices: number, effectFactory?: LooseFaustDspFactory): FaustPolyDspInstance {
168
+ const memory = this.createMemoryAux(voices, voiceFactory, effectFactory);
169
+ // Create voice
170
+ const voiceInstance = new WebAssembly.Instance(voiceFactory.module, this.createWasmImport(memory));
171
+ const voiceFunctions = voiceInstance.exports as IFaustDspInstance & WebAssembly.Exports;
172
+ const voiceAPI = new FaustDspInstance(voiceFunctions);
173
+ // Create mixer
174
+ const mixerAPI = this.createMixerAux(mixerModule, memory);
175
+
176
+ // Possibly create effect instance
177
+ if (effectFactory) {
178
+ const effectInstance = new WebAssembly.Instance(effectFactory.module, this.createWasmImport(memory));
179
+ const effectFunctions = effectInstance.exports as IFaustDspInstance & WebAssembly.Exports;
180
+ const effectAPI = new FaustDspInstance(effectFunctions);
181
+ return {
182
+ memory,
183
+ voices,
184
+ voiceAPI,
185
+ effectAPI,
186
+ mixerAPI,
187
+ voiceJSON: voiceFactory.json,
188
+ effectJSON: effectFactory.json
189
+ };
190
+ } else {
191
+ return {
192
+ memory,
193
+ voices,
194
+ voiceAPI,
195
+ mixerAPI,
196
+ voiceJSON: voiceFactory.json
197
+ };
198
+ }
199
+ }
200
+ }
201
+
202
+ export default FaustWasmInstantiator;