@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,498 @@
|
|
|
1
|
+
import { FaustMonoAudioWorkletNode, FaustPolyAudioWorkletNode } from "./FaustAudioWorkletNode";
|
|
2
|
+
import getFaustAudioWorkletProcessor, { FaustData } from "./FaustAudioWorkletProcessor";
|
|
3
|
+
import getFaustFFTAudioWorkletProcessor, { FaustFFTData, FaustFFTOptionsData } from "./FaustFFTAudioWorkletProcessor";
|
|
4
|
+
import FaustDspInstance from "./FaustDspInstance";
|
|
5
|
+
import FaustWasmInstantiator from "./FaustWasmInstantiator";
|
|
6
|
+
import { FaustMonoOfflineProcessor, FaustPolyOfflineProcessor, IFaustMonoOfflineProcessor, IFaustPolyOfflineProcessor } from "./FaustOfflineProcessor";
|
|
7
|
+
import { FaustMonoScriptProcessorNode, FaustPolyScriptProcessorNode } from "./FaustScriptProcessorNode";
|
|
8
|
+
import { FaustBaseWebAudioDsp, FaustMonoWebAudioDsp, FaustPolyWebAudioDsp, FaustWebAudioDspVoice, IFaustMonoWebAudioNode, IFaustPolyWebAudioNode } from "./FaustWebAudioDsp";
|
|
9
|
+
import type { IFaustCompiler } from "./FaustCompiler";
|
|
10
|
+
import type { FaustDspFactory, FaustDspMeta, FFTUtils, LooseFaustDspFactory } from "./types";
|
|
11
|
+
|
|
12
|
+
export interface IFaustMonoDspGenerator {
|
|
13
|
+
/**
|
|
14
|
+
* Compile a monophonic DSP factory from given code.
|
|
15
|
+
*
|
|
16
|
+
* @param compiler - the Faust compiler
|
|
17
|
+
* @param name - the DSP name
|
|
18
|
+
* @param code - the DSP code
|
|
19
|
+
* @param args - the compilation parameters
|
|
20
|
+
* @returns the compiled factory or 'null' if failure
|
|
21
|
+
*/
|
|
22
|
+
compile(compiler: IFaustCompiler, name: string, code: string, args: string): Promise<{
|
|
23
|
+
factory: FaustDspFactory | null;
|
|
24
|
+
name?: string;
|
|
25
|
+
meta?: FaustDspMeta;
|
|
26
|
+
} | null>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a monophonic WebAudio node (either ScriptProcessorNode or AudioWorkletNode).
|
|
30
|
+
*
|
|
31
|
+
* @param context - the WebAudio context
|
|
32
|
+
* @param name - DSP name, can be used for processorName
|
|
33
|
+
* @param factory - default is the compiled factory
|
|
34
|
+
* @param sp - whether to compile a ScriptProcessorNode or an AudioWorkletNode
|
|
35
|
+
* @param bufferSize - the buffer size in frames to be used in ScriptProcessorNode only, since AudioWorkletNode always uses 128 frames
|
|
36
|
+
* @param processorName - AudioWorklet Processor name
|
|
37
|
+
* @returns the compiled WebAudio node or 'null' if failure
|
|
38
|
+
*/
|
|
39
|
+
createNode(
|
|
40
|
+
context: BaseAudioContext,
|
|
41
|
+
name?: string,
|
|
42
|
+
factory?: LooseFaustDspFactory,
|
|
43
|
+
sp?: boolean,
|
|
44
|
+
bufferSize?: number,
|
|
45
|
+
processorName?: string
|
|
46
|
+
): Promise<IFaustMonoWebAudioNode | null>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a monophonic WebAudio node (either ScriptProcessorNode or AudioWorkletNode).
|
|
50
|
+
*
|
|
51
|
+
* @param context - the WebAudio context
|
|
52
|
+
* @param fftUtils - should be an anonymous class with static methods, without any import from outside
|
|
53
|
+
* @param name - DSP name, can be used for processorName
|
|
54
|
+
* @param factory - default is the compiled factory
|
|
55
|
+
* @param fftOptions - initial FFT options
|
|
56
|
+
* @param processorName - AudioWorklet Processor name
|
|
57
|
+
* @returns the compiled WebAudio node or 'null' if failure
|
|
58
|
+
*/
|
|
59
|
+
createFFTNode(
|
|
60
|
+
context: BaseAudioContext,
|
|
61
|
+
fftUtils: typeof FFTUtils,
|
|
62
|
+
name?: string,
|
|
63
|
+
factory?: LooseFaustDspFactory,
|
|
64
|
+
fftOptions?: Partial<FaustFFTOptionsData>,
|
|
65
|
+
processorName?: string
|
|
66
|
+
): Promise<FaustMonoAudioWorkletNode | null>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Create a monophonic Offline processor.
|
|
70
|
+
*
|
|
71
|
+
* @param sampleRate - the sample rate in Hz
|
|
72
|
+
* @param bufferSize - the buffer size in frames
|
|
73
|
+
* @param factory - default is the compiled factory
|
|
74
|
+
* @returns the compiled processor or 'null' if failure
|
|
75
|
+
*/
|
|
76
|
+
createOfflineProcessor(sampleRate: number, bufferSize: number, factory?: LooseFaustDspFactory, meta?: FaustDspMeta): Promise<IFaustMonoOfflineProcessor | null>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface IFaustPolyDspGenerator {
|
|
80
|
+
/**
|
|
81
|
+
* Compile a monophonic DSP factory from given code.
|
|
82
|
+
*
|
|
83
|
+
* @param compiler - the Faust compiler
|
|
84
|
+
* @param name - the DSP name
|
|
85
|
+
* @param dspCode - the DSP code ('dsp_code' can possibly contain an integrated effect)
|
|
86
|
+
* @param args - the compilation parameters
|
|
87
|
+
* @param effectCode - optional effect DSP code
|
|
88
|
+
* @returns the compiled factory or 'null' if failure
|
|
89
|
+
*/
|
|
90
|
+
compile(compiler: IFaustCompiler, name: string, dspCode: string, args: string, effectCode?: string): Promise<{
|
|
91
|
+
voiceFactory: FaustDspFactory | null;
|
|
92
|
+
effectFactory?: FaustDspFactory | null;
|
|
93
|
+
} | null>;
|
|
94
|
+
/**
|
|
95
|
+
* Create a polyphonic WebAudio node (either ScriptProcessorNode or AudioWorkletNode).
|
|
96
|
+
*
|
|
97
|
+
* @param context the WebAudio context
|
|
98
|
+
* @param voices - the number of voices
|
|
99
|
+
* @param name - AudioWorklet Processor name
|
|
100
|
+
* @param voiceFactory - the Faust factory for voices, either obtained with a compiler (createDSPFactory) or loaded from files (loadDSPFactory)
|
|
101
|
+
* @param mixerModule - the wasm Mixer module (loaded from 'mixer32.wasm' or 'mixer64.wasm' files)
|
|
102
|
+
* @param effectFactory - the Faust factory for the effect, either obtained with a compiler (createDSPFactory) or loaded from files (loadDSPFactory)
|
|
103
|
+
* @param sp - whether to compile a ScriptProcessorNode or an AudioWorkletNode
|
|
104
|
+
* @param bufferSize - the buffer size in frames to be used in ScriptProcessorNode only, since AudioWorkletNode always uses 128 frames
|
|
105
|
+
* @returns the compiled WebAudio node or 'null' if failure
|
|
106
|
+
*/
|
|
107
|
+
createNode(
|
|
108
|
+
context: BaseAudioContext,
|
|
109
|
+
voices: number,
|
|
110
|
+
name?: string,
|
|
111
|
+
voiceFactory?: LooseFaustDspFactory,
|
|
112
|
+
mixerModule?: WebAssembly.Module,
|
|
113
|
+
effectFactory?: LooseFaustDspFactory | null,
|
|
114
|
+
sp?: boolean,
|
|
115
|
+
bufferSize?: number
|
|
116
|
+
): Promise<IFaustPolyWebAudioNode | null>;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Create a monophonic Offline processor.
|
|
120
|
+
*
|
|
121
|
+
* @param sampleRate - the sample rate in Hz
|
|
122
|
+
* @param bufferSize - the buffer size in frames
|
|
123
|
+
* @param voiceFactory - the Faust factory for voices, either obtained with a compiler (createDSPFactory) or loaded from files (loadDSPFactory)
|
|
124
|
+
* @param mixerModule - the wasm Mixer module (loaded from 'mixer32.wasm' or 'mixer64.wasm' files)
|
|
125
|
+
* @param effectFactory - the Faust factory for the effect, either obtained with a compiler (createDSPFactory) or loaded from files (loadDSPFactory)
|
|
126
|
+
* @returns the compiled processor or 'null' if failure
|
|
127
|
+
*/
|
|
128
|
+
createOfflineProcessor(
|
|
129
|
+
sampleRate: number,
|
|
130
|
+
bufferSize: number,
|
|
131
|
+
voices: number,
|
|
132
|
+
voiceFactory?: LooseFaustDspFactory,
|
|
133
|
+
mixerModule?: WebAssembly.Module,
|
|
134
|
+
effectFactory?: LooseFaustDspFactory | null
|
|
135
|
+
): Promise<IFaustPolyOfflineProcessor | null>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export class FaustMonoDspGenerator implements IFaustMonoDspGenerator {
|
|
139
|
+
// Set of all created WorkletProcessors, each of them has to be unique
|
|
140
|
+
private static gWorkletProcessors: Map<BaseAudioContext, Set<string>> = new Map();
|
|
141
|
+
|
|
142
|
+
name: string;
|
|
143
|
+
factory!: FaustDspFactory | null;
|
|
144
|
+
|
|
145
|
+
constructor() {
|
|
146
|
+
this.factory = null;
|
|
147
|
+
}
|
|
148
|
+
async compile(compiler: IFaustCompiler, name: string, code: string, args: string) {
|
|
149
|
+
this.factory = await compiler.createMonoDSPFactory(name, code, args);
|
|
150
|
+
if (!this.factory) return null;
|
|
151
|
+
this.name = name;
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async createNode<SP extends boolean = false>(
|
|
156
|
+
context: BaseAudioContext,
|
|
157
|
+
name = this.name,
|
|
158
|
+
factory = this.factory as LooseFaustDspFactory,
|
|
159
|
+
sp = false as SP,
|
|
160
|
+
bufferSize = 1024,
|
|
161
|
+
processorName = factory?.shaKey || name
|
|
162
|
+
): Promise<SP extends true ? FaustMonoScriptProcessorNode | null : FaustMonoAudioWorkletNode | null> {
|
|
163
|
+
if (!factory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
|
|
164
|
+
|
|
165
|
+
const meta = JSON.parse(factory.json);
|
|
166
|
+
const sampleSize = meta.compile_options.match("-double") ? 8 : 4;
|
|
167
|
+
if (sp) {
|
|
168
|
+
const instance = await FaustWasmInstantiator.createAsyncMonoDSPInstance(factory);
|
|
169
|
+
const monoDsp = new FaustMonoWebAudioDsp(instance, context.sampleRate, sampleSize, bufferSize);
|
|
170
|
+
const sp = context.createScriptProcessor(bufferSize, monoDsp.getNumInputs(), monoDsp.getNumOutputs()) as FaustMonoScriptProcessorNode;
|
|
171
|
+
Object.setPrototypeOf(sp, FaustMonoScriptProcessorNode.prototype);
|
|
172
|
+
sp.init(monoDsp);
|
|
173
|
+
return sp as SP extends true ? FaustMonoScriptProcessorNode : FaustMonoAudioWorkletNode;
|
|
174
|
+
} else {
|
|
175
|
+
// Dynamically create AudioWorkletProcessor if code not yet created
|
|
176
|
+
if (!FaustMonoDspGenerator.gWorkletProcessors.has(context)) FaustMonoDspGenerator.gWorkletProcessors.set(context, new Set());
|
|
177
|
+
if (!FaustMonoDspGenerator.gWorkletProcessors.get(context)?.has(processorName)) {
|
|
178
|
+
try {
|
|
179
|
+
const processorCode = `
|
|
180
|
+
// DSP name and JSON string for DSP are generated
|
|
181
|
+
const faustData = ${JSON.stringify({
|
|
182
|
+
processorName,
|
|
183
|
+
dspName: name,
|
|
184
|
+
dspMeta: meta,
|
|
185
|
+
poly: false
|
|
186
|
+
} as FaustData)};
|
|
187
|
+
// Implementation needed classes of functions
|
|
188
|
+
const ${FaustDspInstance.name}_default = ${FaustDspInstance.toString()}
|
|
189
|
+
const ${FaustBaseWebAudioDsp.name} = ${FaustBaseWebAudioDsp.toString()}
|
|
190
|
+
const ${FaustMonoWebAudioDsp.name} = ${FaustMonoWebAudioDsp.toString()}
|
|
191
|
+
const ${FaustWasmInstantiator.name} = ${FaustWasmInstantiator.toString()}
|
|
192
|
+
// Put them in dependencies
|
|
193
|
+
const dependencies = {
|
|
194
|
+
${FaustBaseWebAudioDsp.name},
|
|
195
|
+
${FaustMonoWebAudioDsp.name},
|
|
196
|
+
${FaustWasmInstantiator.name}
|
|
197
|
+
};
|
|
198
|
+
// Generate the actual AudioWorkletProcessor code
|
|
199
|
+
(${getFaustAudioWorkletProcessor.toString()})(dependencies, faustData);
|
|
200
|
+
`;
|
|
201
|
+
const url = URL.createObjectURL(new Blob([processorCode], { type: "text/javascript" }));
|
|
202
|
+
await context.audioWorklet.addModule(url);
|
|
203
|
+
// Keep the DSP name
|
|
204
|
+
FaustMonoDspGenerator.gWorkletProcessors.get(context)?.add(processorName);
|
|
205
|
+
} catch (e) {
|
|
206
|
+
// console.error(`=> exception raised while running createMonoNode: ${e}`);
|
|
207
|
+
// console.error(`=> check that your page is served using https.${e}`);
|
|
208
|
+
throw e;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Create the AWN
|
|
212
|
+
const node = new FaustMonoAudioWorkletNode(context, processorName, factory, sampleSize)
|
|
213
|
+
return node as SP extends true ? FaustMonoScriptProcessorNode : FaustMonoAudioWorkletNode;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async createFFTNode(
|
|
217
|
+
context: BaseAudioContext,
|
|
218
|
+
fftUtils: typeof FFTUtils,
|
|
219
|
+
name = this.name,
|
|
220
|
+
factory = this.factory as LooseFaustDspFactory,
|
|
221
|
+
fftOptions: Partial<FaustFFTOptionsData> = {},
|
|
222
|
+
processorName = factory?.shaKey ? `${factory.shaKey}_fft` : name
|
|
223
|
+
): Promise<FaustMonoAudioWorkletNode | null> {
|
|
224
|
+
if (!factory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
|
|
225
|
+
|
|
226
|
+
const meta: FaustDspMeta = JSON.parse(factory.json);
|
|
227
|
+
const sampleSize = meta.compile_options.match("-double") ? 8 : 4;
|
|
228
|
+
// Dynamically create AudioWorkletProcessor if code not yet created
|
|
229
|
+
if (!FaustMonoDspGenerator.gWorkletProcessors.has(context)) FaustMonoDspGenerator.gWorkletProcessors.set(context, new Set());
|
|
230
|
+
if (!FaustMonoDspGenerator.gWorkletProcessors.get(context)?.has(processorName)) {
|
|
231
|
+
try {
|
|
232
|
+
const processorCode = `
|
|
233
|
+
// DSP name and JSON string for DSP are generated
|
|
234
|
+
const faustData = ${JSON.stringify({
|
|
235
|
+
processorName,
|
|
236
|
+
dspName: name,
|
|
237
|
+
dspMeta: meta,
|
|
238
|
+
fftOptions
|
|
239
|
+
} as FaustFFTData)};
|
|
240
|
+
// Implementation needed classes of functions
|
|
241
|
+
const ${FaustDspInstance.name}_default = ${FaustDspInstance.toString()}
|
|
242
|
+
const ${FaustBaseWebAudioDsp.name} = ${FaustBaseWebAudioDsp.toString()}
|
|
243
|
+
const ${FaustMonoWebAudioDsp.name} = ${FaustMonoWebAudioDsp.toString()}
|
|
244
|
+
const ${FaustWasmInstantiator.name} = ${FaustWasmInstantiator.toString()}
|
|
245
|
+
const FFTUtils = ${fftUtils.toString()}
|
|
246
|
+
// Put them in dependencies
|
|
247
|
+
const dependencies = {
|
|
248
|
+
${FaustBaseWebAudioDsp.name},
|
|
249
|
+
${FaustMonoWebAudioDsp.name},
|
|
250
|
+
${FaustWasmInstantiator.name},
|
|
251
|
+
FFTUtils
|
|
252
|
+
};
|
|
253
|
+
// Generate the actual AudioWorkletProcessor code
|
|
254
|
+
(${getFaustFFTAudioWorkletProcessor.toString()})(dependencies, faustData);
|
|
255
|
+
`;
|
|
256
|
+
const url = URL.createObjectURL(new Blob([processorCode], { type: "text/javascript" }));
|
|
257
|
+
await context.audioWorklet.addModule(url);
|
|
258
|
+
// Keep the DSP name
|
|
259
|
+
FaustMonoDspGenerator.gWorkletProcessors.get(context)?.add(processorName);
|
|
260
|
+
} catch (e) {
|
|
261
|
+
// console.error(`=> exception raised while running createMonoNode: ${e}`);
|
|
262
|
+
// console.error(`=> check that your page is served using https.${e}`);
|
|
263
|
+
throw e;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Create the AWN
|
|
267
|
+
const node = new FaustMonoAudioWorkletNode(context, processorName, factory, sampleSize, { channelCount: Math.max(1, Math.ceil(meta.inputs / 3)), outputChannelCount: [Math.ceil(meta.outputs / 2)] });
|
|
268
|
+
if (fftOptions.fftSize) {
|
|
269
|
+
const param = node.parameters.get("fftSize");
|
|
270
|
+
if (param) param.value = fftOptions.fftSize;
|
|
271
|
+
}
|
|
272
|
+
if (fftOptions.fftOverlap) {
|
|
273
|
+
const param = node.parameters.get("fftOverlap");
|
|
274
|
+
if (param) param.value = fftOptions.fftOverlap;
|
|
275
|
+
}
|
|
276
|
+
if (typeof fftOptions.defaultWindowFunction === "number") {
|
|
277
|
+
const param = node.parameters.get("windowFunction");
|
|
278
|
+
if (param) param.value = fftOptions.defaultWindowFunction + 1;
|
|
279
|
+
}
|
|
280
|
+
if (typeof fftOptions.noIFFT === "boolean") {
|
|
281
|
+
const param = node.parameters.get("noIFFT");
|
|
282
|
+
if (param) param.value = +fftOptions.noIFFT;
|
|
283
|
+
}
|
|
284
|
+
return node;
|
|
285
|
+
}
|
|
286
|
+
async createAudioWorkletProcessor(
|
|
287
|
+
name = this.name,
|
|
288
|
+
factory = this.factory as LooseFaustDspFactory,
|
|
289
|
+
processorName = factory?.shaKey || name
|
|
290
|
+
) {
|
|
291
|
+
if (!factory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
|
|
292
|
+
|
|
293
|
+
const meta = JSON.parse(factory.json);
|
|
294
|
+
const dependencies = {
|
|
295
|
+
FaustBaseWebAudioDsp,
|
|
296
|
+
FaustMonoWebAudioDsp,
|
|
297
|
+
FaustWasmInstantiator,
|
|
298
|
+
FaustPolyWebAudioDsp: undefined,
|
|
299
|
+
FaustWebAudioDspVoice: undefined,
|
|
300
|
+
}
|
|
301
|
+
// const sampleSize = meta.compile_options.match("-double") ? 8 : 4;
|
|
302
|
+
// Dynamically create AudioWorkletProcessor if code not yet created
|
|
303
|
+
try {
|
|
304
|
+
// DSP name and JSON string for DSP are generated
|
|
305
|
+
const faustData = {
|
|
306
|
+
processorName,
|
|
307
|
+
dspName: name,
|
|
308
|
+
dspMeta: meta,
|
|
309
|
+
poly: false
|
|
310
|
+
} as FaustData;
|
|
311
|
+
// Generate the actual AudioWorkletProcessor code
|
|
312
|
+
const Processor = getFaustAudioWorkletProcessor(dependencies, faustData);
|
|
313
|
+
return Processor;
|
|
314
|
+
} catch (e) {
|
|
315
|
+
// console.error(`=> exception raised while running createMonoNode: ${e}`);
|
|
316
|
+
// console.error(`=> check that your page is served using https.${e}`);
|
|
317
|
+
throw e;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async createOfflineProcessor(
|
|
321
|
+
sampleRate: number,
|
|
322
|
+
bufferSize: number,
|
|
323
|
+
factory = this.factory as LooseFaustDspFactory,
|
|
324
|
+
) {
|
|
325
|
+
if (!factory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
|
|
326
|
+
|
|
327
|
+
const meta = JSON.parse(factory.json);
|
|
328
|
+
const instance = await FaustWasmInstantiator.createAsyncMonoDSPInstance(factory);
|
|
329
|
+
const sampleSize = meta.compile_options.match("-double") ? 8 : 4;
|
|
330
|
+
const monoDsp = new FaustMonoWebAudioDsp(instance, sampleRate, sampleSize, bufferSize);
|
|
331
|
+
return new FaustMonoOfflineProcessor(monoDsp, bufferSize);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export class FaustPolyDspGenerator implements IFaustPolyDspGenerator {
|
|
336
|
+
// Set of all created WorkletProcessors, each of them has to be unique
|
|
337
|
+
private static gWorkletProcessors: Map<BaseAudioContext, Set<string>> = new Map();
|
|
338
|
+
|
|
339
|
+
name: string;
|
|
340
|
+
voiceFactory!: FaustDspFactory | null;
|
|
341
|
+
effectFactory!: FaustDspFactory | null;
|
|
342
|
+
mixerBuffer!: Uint8Array;
|
|
343
|
+
mixerModule!: WebAssembly.Module;
|
|
344
|
+
|
|
345
|
+
constructor() {
|
|
346
|
+
this.voiceFactory = null;
|
|
347
|
+
this.effectFactory = null;
|
|
348
|
+
}
|
|
349
|
+
async compile(
|
|
350
|
+
compiler: IFaustCompiler,
|
|
351
|
+
name: string,
|
|
352
|
+
dspCode: string,
|
|
353
|
+
args: string,
|
|
354
|
+
effectCode = `
|
|
355
|
+
adapt(1,1) = _; adapt(2,2) = _,_; adapt(1,2) = _ <: _,_; adapt(2,1) = _,_ :> _;
|
|
356
|
+
adaptor(F,G) = adapt(outputs(F),inputs(G));
|
|
357
|
+
dsp_code = environment{${dspCode}};
|
|
358
|
+
process = adaptor(dsp_code.process, dsp_code.effect) : dsp_code.effect;`
|
|
359
|
+
) {
|
|
360
|
+
this.voiceFactory = await compiler.createPolyDSPFactory(name, dspCode, args);
|
|
361
|
+
if (!this.voiceFactory) return null;
|
|
362
|
+
// Compile effect, possibly failing since 'compilePolyNode2' can be called by called by 'compilePolyNode'
|
|
363
|
+
try {
|
|
364
|
+
this.effectFactory = await compiler.createPolyDSPFactory(name, effectCode, args);
|
|
365
|
+
} catch (e) {
|
|
366
|
+
console.warn(e);
|
|
367
|
+
}
|
|
368
|
+
this.name = name;
|
|
369
|
+
const voiceMeta = JSON.parse(this.voiceFactory.json);
|
|
370
|
+
const isDouble = voiceMeta.compile_options.match("-double");
|
|
371
|
+
const { mixerBuffer, mixerModule } = await compiler.getAsyncInternalMixerModule(!!isDouble);
|
|
372
|
+
this.mixerBuffer = mixerBuffer;
|
|
373
|
+
this.mixerModule = mixerModule;
|
|
374
|
+
return this;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async createNode<SP extends boolean = false>(
|
|
378
|
+
context: BaseAudioContext,
|
|
379
|
+
voices: number,
|
|
380
|
+
name = this.name,
|
|
381
|
+
voiceFactory = this.voiceFactory as LooseFaustDspFactory,
|
|
382
|
+
mixerModule = this.mixerModule,
|
|
383
|
+
effectFactory = this.effectFactory as LooseFaustDspFactory | null,
|
|
384
|
+
sp = false as SP,
|
|
385
|
+
bufferSize = 1024,
|
|
386
|
+
processorName = ((voiceFactory?.shaKey || "") + (effectFactory?.shaKey || "")) || `${name}_poly`
|
|
387
|
+
): Promise<SP extends true ? FaustPolyScriptProcessorNode | null : FaustPolyAudioWorkletNode | null> {
|
|
388
|
+
if (!voiceFactory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
|
|
389
|
+
|
|
390
|
+
const voiceMeta = JSON.parse(voiceFactory.json);
|
|
391
|
+
const effectMeta = effectFactory ? JSON.parse(effectFactory.json) : undefined;
|
|
392
|
+
const sampleSize = voiceMeta.compile_options.match("-double") ? 8 : 4;
|
|
393
|
+
if (sp) {
|
|
394
|
+
const instance = await FaustWasmInstantiator.createAsyncPolyDSPInstance(voiceFactory, mixerModule, voices, effectFactory || undefined);
|
|
395
|
+
const polyDsp = new FaustPolyWebAudioDsp(instance, context.sampleRate, sampleSize, bufferSize);
|
|
396
|
+
const sp = context.createScriptProcessor(bufferSize, polyDsp.getNumInputs(), polyDsp.getNumOutputs()) as FaustPolyScriptProcessorNode;
|
|
397
|
+
Object.setPrototypeOf(sp, FaustPolyScriptProcessorNode.prototype);
|
|
398
|
+
sp.init(polyDsp);
|
|
399
|
+
return sp as SP extends true ? FaustPolyScriptProcessorNode : FaustPolyAudioWorkletNode;
|
|
400
|
+
} else {
|
|
401
|
+
// Dynamically create AudioWorkletProcessor if code not yet created
|
|
402
|
+
if (!FaustPolyDspGenerator.gWorkletProcessors.has(context)) FaustPolyDspGenerator.gWorkletProcessors.set(context, new Set());
|
|
403
|
+
if (!FaustPolyDspGenerator.gWorkletProcessors.get(context)?.has(processorName)) {
|
|
404
|
+
try {
|
|
405
|
+
const processorCode = `
|
|
406
|
+
// DSP name and JSON string for DSP are generated
|
|
407
|
+
const faustData = ${JSON.stringify({
|
|
408
|
+
processorName,
|
|
409
|
+
dspName: name,
|
|
410
|
+
dspMeta: voiceMeta,
|
|
411
|
+
poly: true,
|
|
412
|
+
effectMeta
|
|
413
|
+
} as FaustData)};
|
|
414
|
+
// Implementation needed classes of functions
|
|
415
|
+
const ${FaustDspInstance.name}_default = ${FaustDspInstance.toString()}
|
|
416
|
+
const ${FaustBaseWebAudioDsp.name} = ${FaustBaseWebAudioDsp.toString()}
|
|
417
|
+
const ${FaustPolyWebAudioDsp.name} = ${FaustPolyWebAudioDsp.toString()}
|
|
418
|
+
const ${FaustWebAudioDspVoice.name} = ${FaustWebAudioDspVoice.toString()}
|
|
419
|
+
const ${FaustWasmInstantiator.name} = ${FaustWasmInstantiator.toString()}
|
|
420
|
+
// Put them in dependencies
|
|
421
|
+
const dependencies = {
|
|
422
|
+
${FaustBaseWebAudioDsp.name},
|
|
423
|
+
${FaustPolyWebAudioDsp.name},
|
|
424
|
+
${FaustWasmInstantiator.name}
|
|
425
|
+
};
|
|
426
|
+
// Generate the actual AudioWorkletProcessor code
|
|
427
|
+
(${getFaustAudioWorkletProcessor.toString()})(dependencies, faustData);
|
|
428
|
+
`;
|
|
429
|
+
const url = URL.createObjectURL(new Blob([processorCode], { type: "text/javascript" }));
|
|
430
|
+
await context.audioWorklet.addModule(url);
|
|
431
|
+
// Keep the DSP name
|
|
432
|
+
FaustPolyDspGenerator.gWorkletProcessors.get(context)?.add(processorName);
|
|
433
|
+
} catch (e) {
|
|
434
|
+
// console.error(`=> exception raised while running createPolyNode: ${e}`);
|
|
435
|
+
// console.error(`=> check that your page is served using https.${e}`);
|
|
436
|
+
throw e;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
// Create the AWN
|
|
440
|
+
const node = new FaustPolyAudioWorkletNode(context, processorName, voiceFactory, mixerModule, voices, sampleSize, effectFactory || undefined);
|
|
441
|
+
return node as SP extends true ? FaustPolyScriptProcessorNode : FaustPolyAudioWorkletNode;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
async createAudioWorkletProcessor(
|
|
445
|
+
name = this.name,
|
|
446
|
+
voiceFactory = this.voiceFactory as LooseFaustDspFactory,
|
|
447
|
+
effectFactory = this.effectFactory as LooseFaustDspFactory | null,
|
|
448
|
+
processorName = ((voiceFactory?.shaKey || "") + (effectFactory?.shaKey || "")) || `${name}_poly`
|
|
449
|
+
) {
|
|
450
|
+
if (!voiceFactory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
|
|
451
|
+
|
|
452
|
+
const voiceMeta = JSON.parse(voiceFactory.json);
|
|
453
|
+
const effectMeta = effectFactory ? JSON.parse(effectFactory.json) : undefined;
|
|
454
|
+
const sampleSize = voiceMeta.compile_options.match("-double") ? 8 : 4;
|
|
455
|
+
// Dynamically create AudioWorkletProcessor if code not yet created
|
|
456
|
+
try {
|
|
457
|
+
const dependencies = {
|
|
458
|
+
FaustBaseWebAudioDsp,
|
|
459
|
+
FaustMonoWebAudioDsp: undefined,
|
|
460
|
+
FaustWasmInstantiator,
|
|
461
|
+
FaustPolyWebAudioDsp,
|
|
462
|
+
FaustWebAudioDspVoice,
|
|
463
|
+
};
|
|
464
|
+
// DSP name and JSON string for DSP are generated
|
|
465
|
+
const faustData = {
|
|
466
|
+
processorName,
|
|
467
|
+
dspName: name,
|
|
468
|
+
dspMeta: voiceMeta,
|
|
469
|
+
poly: true,
|
|
470
|
+
effectMeta
|
|
471
|
+
} as FaustData;
|
|
472
|
+
// Generate the actual AudioWorkletProcessor code
|
|
473
|
+
const Processor = getFaustAudioWorkletProcessor<true>(dependencies, faustData);
|
|
474
|
+
return Processor;
|
|
475
|
+
} catch (e) {
|
|
476
|
+
// console.error(`=> exception raised while running createPolyNode: ${e}`);
|
|
477
|
+
// console.error(`=> check that your page is served using https.${e}`);
|
|
478
|
+
throw e;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async createOfflineProcessor(
|
|
482
|
+
sampleRate: number,
|
|
483
|
+
bufferSize: number,
|
|
484
|
+
voices: number,
|
|
485
|
+
voiceFactory = this.voiceFactory as LooseFaustDspFactory,
|
|
486
|
+
mixerModule = this.mixerModule,
|
|
487
|
+
effectFactory = this.effectFactory as LooseFaustDspFactory | null
|
|
488
|
+
) {
|
|
489
|
+
if (!voiceFactory) throw new Error("Code is not compiled, please define the factory or call `await this.compile()` first.");
|
|
490
|
+
|
|
491
|
+
const voiceMeta = JSON.parse(voiceFactory.json);
|
|
492
|
+
const effectMeta = effectFactory ? JSON.parse(effectFactory.json) : undefined;
|
|
493
|
+
const instance = await FaustWasmInstantiator.createAsyncPolyDSPInstance(voiceFactory, mixerModule, voices, effectFactory || undefined);
|
|
494
|
+
const sampleSize = voiceMeta.compile_options.match("-double") ? 8 : 4;
|
|
495
|
+
const polyDsp = new FaustPolyWebAudioDsp(instance, sampleRate, sampleSize, bufferSize);
|
|
496
|
+
return new FaustPolyOfflineProcessor(polyDsp, bufferSize);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Faust wasm instance interface.
|
|
3
|
+
*/
|
|
4
|
+
export interface IFaustDspInstance {
|
|
5
|
+
/**
|
|
6
|
+
* The dsp computation, to be called with successive input/output audio buffers.
|
|
7
|
+
*
|
|
8
|
+
* @param $dsp - the DSP pointer
|
|
9
|
+
* @param count - the audio buffer size in frames
|
|
10
|
+
* @param $inputs - the input audio buffer as in index in wasm memory
|
|
11
|
+
* @param $output - the output audio buffer as in index in wasm memory
|
|
12
|
+
*/
|
|
13
|
+
compute($dsp: number, count: number, $inputs: number, $output: number): void;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Give the number of inputs of a Faust wasm instance.
|
|
17
|
+
*
|
|
18
|
+
* @param $dsp - the DSP pointer
|
|
19
|
+
*/
|
|
20
|
+
getNumInputs($dsp: number): number;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Give the number of outputs of a Faust wasm instance.
|
|
24
|
+
*
|
|
25
|
+
* @param $dsp - the DSP pointer
|
|
26
|
+
*/
|
|
27
|
+
getNumOutputs($dsp: number): number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Give a parameter current value.
|
|
31
|
+
*
|
|
32
|
+
* @param $dsp - the DSP pointer
|
|
33
|
+
* @param index - the parameter index
|
|
34
|
+
* @preturn the parameter value
|
|
35
|
+
*/
|
|
36
|
+
getParamValue($dsp: number, index: number): number;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Give the Faust wasm instance sample rate.
|
|
40
|
+
*
|
|
41
|
+
* @param $dsp - the DSP pointer
|
|
42
|
+
* @preturn the sample rate
|
|
43
|
+
*/
|
|
44
|
+
getSampleRate($dsp: number): number;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Global init, calls the following methods:
|
|
48
|
+
* - static class 'classInit': static tables initialization
|
|
49
|
+
* - 'instanceInit': constants and instance state initialization
|
|
50
|
+
*
|
|
51
|
+
* @param $dsp - the DSP pointer
|
|
52
|
+
* @param sampleRate - the sampling rate in Hertz
|
|
53
|
+
*/
|
|
54
|
+
init($dsp: number, sampleRate: number): void;
|
|
55
|
+
|
|
56
|
+
/** Init instance state (delay lines...).
|
|
57
|
+
*
|
|
58
|
+
* @param $dsp - the DSP pointer
|
|
59
|
+
*/
|
|
60
|
+
instanceClear($dsp: number): void;
|
|
61
|
+
|
|
62
|
+
/** Init instance constant state.
|
|
63
|
+
*
|
|
64
|
+
* @param $dsp - the DSP pointer
|
|
65
|
+
* @param sampleRate - the sampling rate in Hertz
|
|
66
|
+
*/
|
|
67
|
+
instanceConstants($dsp: number, sampleRate: number): void;
|
|
68
|
+
|
|
69
|
+
/** Init instance state.
|
|
70
|
+
*
|
|
71
|
+
* @param $dsp - the DSP pointer
|
|
72
|
+
* @param sampleRate - the sampling rate in Hertz
|
|
73
|
+
*/
|
|
74
|
+
instanceInit($dsp: number, sampleRate: number): void;
|
|
75
|
+
|
|
76
|
+
/** Init default control parameters values.
|
|
77
|
+
*
|
|
78
|
+
* @param $dsp - the DSP pointer
|
|
79
|
+
*/
|
|
80
|
+
instanceResetUserInterface($dsp: number): void;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Set a parameter current value.
|
|
84
|
+
*
|
|
85
|
+
* @param $dsp - the DSP pointer
|
|
86
|
+
* @param index - the parameter index
|
|
87
|
+
* @param value - the parameter value
|
|
88
|
+
*/
|
|
89
|
+
setParamValue($dsp: number, index: number, value: number): void;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Mixer used in polyphonic mode.
|
|
94
|
+
*/
|
|
95
|
+
export interface IFaustMixerInstance {
|
|
96
|
+
clearOutput(bufferSize: number, chans: number, $outputs: number): void;
|
|
97
|
+
mixCheckVoice(bufferSize: number, chans: number, $inputs: number, $outputs: number): number;
|
|
98
|
+
fadeOut(bufferSize: number, chans: number, $outputs: number): void;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Monophonic instance.
|
|
103
|
+
*/
|
|
104
|
+
export interface FaustMonoDspInstance {
|
|
105
|
+
memory: WebAssembly.Memory;
|
|
106
|
+
api: IFaustDspInstance;
|
|
107
|
+
json: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Polyphonic instance.
|
|
112
|
+
*/
|
|
113
|
+
export interface FaustPolyDspInstance {
|
|
114
|
+
memory: WebAssembly.Memory;
|
|
115
|
+
voices: number;
|
|
116
|
+
voiceAPI: IFaustDspInstance;
|
|
117
|
+
effectAPI?: IFaustDspInstance;
|
|
118
|
+
mixerAPI: IFaustMixerInstance;
|
|
119
|
+
voiceJSON: string;
|
|
120
|
+
effectJSON?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
class FaustDspInstance implements IFaustDspInstance {
|
|
124
|
+
private readonly fExports: IFaustDspInstance;
|
|
125
|
+
|
|
126
|
+
constructor(exports: IFaustDspInstance) { this.fExports = exports; }
|
|
127
|
+
|
|
128
|
+
compute($dsp: number, count: number, $input: number, $output: number) { this.fExports.compute($dsp, count, $input, $output); }
|
|
129
|
+
getNumInputs($dsp: number) { return this.fExports.getNumInputs($dsp); }
|
|
130
|
+
getNumOutputs($dsp: number) { return this.fExports.getNumOutputs($dsp); }
|
|
131
|
+
getParamValue($dsp: number, index: number) { return this.fExports.getParamValue($dsp, index); }
|
|
132
|
+
getSampleRate($dsp: number) { return this.fExports.getSampleRate($dsp); }
|
|
133
|
+
init($dsp: number, sampleRate: number) { this.fExports.init($dsp, sampleRate); }
|
|
134
|
+
instanceClear($dsp: number) { this.fExports.instanceClear($dsp); }
|
|
135
|
+
instanceConstants($dsp: number, sampleRate: number) { this.fExports.instanceConstants($dsp, sampleRate); }
|
|
136
|
+
instanceInit($dsp: number, sampleRate: number) { this.fExports.instanceInit($dsp, sampleRate); }
|
|
137
|
+
instanceResetUserInterface($dsp: number) { this.fExports.instanceResetUserInterface($dsp); }
|
|
138
|
+
setParamValue($dsp: number, index: number, value: number) { this.fExports.setParamValue($dsp, index, value); }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default FaustDspInstance;
|