@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,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;
|