@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,638 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
/** @type {typeof AudioContext} */
|
|
3
|
+
const AudioContextConstructor = globalThis.AudioContext || globalThis.webkitAudioContext;
|
|
4
|
+
const audio_context = new AudioContextConstructor({ latencyHint: 0.00001 });
|
|
5
|
+
audio_context.destination.channelInterpretation = "discrete";
|
|
6
|
+
|
|
7
|
+
const isWasm = typeof WebAssembly !== "undefined";
|
|
8
|
+
const workletAvailable = typeof AudioWorklet !== "undefined";
|
|
9
|
+
|
|
10
|
+
/** @type {HTMLDivElement} */
|
|
11
|
+
const faustUIRoot = document.getElementById("faust-ui");
|
|
12
|
+
|
|
13
|
+
/** @type {import("./faustwasm").FaustCompiler} */
|
|
14
|
+
let faust_compiler = null;
|
|
15
|
+
/** @type {import("./faustwasm").FaustMonoDspGenerator} */
|
|
16
|
+
let faust_mono_factory = null;
|
|
17
|
+
/** @type {import("./faustwasm").FaustPolyDspGenerator} */
|
|
18
|
+
let faust_poly_factory = null;
|
|
19
|
+
let isPoly = false;
|
|
20
|
+
let buffer_size = 1024;
|
|
21
|
+
let audio_input = null;
|
|
22
|
+
let factory = null;
|
|
23
|
+
/** @type {import("./faustwasm").FaustAudioWorkletNode<any> | import("./faustwasm").FaustScriptProcessorNode<any>} */
|
|
24
|
+
let DSP = null;
|
|
25
|
+
/** @type {string} */
|
|
26
|
+
let dsp_code = null;
|
|
27
|
+
/** @type {typeof import("./faust-ui/index").FaustUI} */
|
|
28
|
+
let FaustUI;
|
|
29
|
+
/** @type {import("./faust-ui/index").FaustUI} */
|
|
30
|
+
let faustUI;
|
|
31
|
+
let poly_flag = "OFF";
|
|
32
|
+
let ftz_flag = "2";
|
|
33
|
+
let sample_format = "float";
|
|
34
|
+
let poly_nvoices = 16;
|
|
35
|
+
let rendering_mode = "ScriptProcessor";
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} path
|
|
38
|
+
* @param {number} value
|
|
39
|
+
*/
|
|
40
|
+
let output_handler = (path, value) => faustUI.paramChangeByDSP(path, value);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {HTMLSelectElement} bs_item
|
|
44
|
+
*/
|
|
45
|
+
const setBufferSize = (bs_item) => {
|
|
46
|
+
buffer_size = parseInt(bs_item.options[bs_item.selectedIndex].value);
|
|
47
|
+
if (buffer_size === 128 && rendering_mode === "ScriptProcessor") {
|
|
48
|
+
console.log("buffer_size cannot be set to 128 in ScriptProcessor mode !");
|
|
49
|
+
buffer_size = 256;
|
|
50
|
+
restoreMenu("selectedBuffer", buffer_size.toString());
|
|
51
|
+
}
|
|
52
|
+
compileDSP();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {HTMLSelectElement} poly_item
|
|
57
|
+
*/
|
|
58
|
+
const setPoly = (poly_item) => {
|
|
59
|
+
poly_flag = poly_item.options[poly_item.selectedIndex].value;
|
|
60
|
+
compileDSP();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {HTMLSelectElement} voices_item
|
|
65
|
+
*/
|
|
66
|
+
const setPolyVoices = (voices_item) => {
|
|
67
|
+
poly_nvoices = parseInt(voices_item.options[voices_item.selectedIndex].value);
|
|
68
|
+
compileDSP();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {HTMLSelectElement} rendering_item
|
|
73
|
+
*/
|
|
74
|
+
const setRenderingMode = (rendering_item) => {
|
|
75
|
+
rendering_mode = rendering_item.options[rendering_item.selectedIndex].value;
|
|
76
|
+
/** @type {HTMLSelectElement} */
|
|
77
|
+
const selectedBuffer = document.getElementById("selectedBuffer");
|
|
78
|
+
if (rendering_mode === "AudioWorklet") {
|
|
79
|
+
buffer_size = 128;
|
|
80
|
+
restoreMenu("selectedBuffer", buffer_size.toString());
|
|
81
|
+
selectedBuffer.disabled = true;
|
|
82
|
+
} else {
|
|
83
|
+
buffer_size = 1024;
|
|
84
|
+
restoreMenu("selectedBuffer", buffer_size.toString());
|
|
85
|
+
selectedBuffer.disabled = false;
|
|
86
|
+
}
|
|
87
|
+
compileDSP();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {HTMLSelectElement} ftz_item
|
|
92
|
+
*/
|
|
93
|
+
const setFTZ = (ftz_item) => {
|
|
94
|
+
ftz_flag = ftz_item.options[ftz_item.selectedIndex].value;
|
|
95
|
+
compileDSP();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {HTMLSelectElement} sample_item
|
|
100
|
+
*/
|
|
101
|
+
const setSampleFormat = (sample_item) => {
|
|
102
|
+
sample_format = sample_item.options[sample_item.selectedIndex].value;
|
|
103
|
+
compileDSP();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// MIDI input handling
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {number} channel
|
|
110
|
+
* @param {number} pitch
|
|
111
|
+
* @param {number} velocity
|
|
112
|
+
*/
|
|
113
|
+
const keyOn = (channel, pitch, velocity) => {
|
|
114
|
+
if (DSP && isPoly) {
|
|
115
|
+
DSP.keyOn(channel, pitch, velocity);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {number} channel
|
|
121
|
+
* @param {number} pitch
|
|
122
|
+
* @param {number} velocity
|
|
123
|
+
*/
|
|
124
|
+
const keyOff = (channel, pitch, velocity) => {
|
|
125
|
+
if (DSP && isPoly) {
|
|
126
|
+
DSP.keyOff(channel, pitch, velocity);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {number} channel
|
|
132
|
+
* @param {number} bend
|
|
133
|
+
*/
|
|
134
|
+
const pitchWheel = (channel, bend) => {
|
|
135
|
+
if (DSP) {
|
|
136
|
+
DSP.pitchWheel(channel, bend);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {number} channel
|
|
142
|
+
* @param {number} ctrl
|
|
143
|
+
* @param {number} value
|
|
144
|
+
*/
|
|
145
|
+
const ctrlChange = (channel, ctrl, value) => {
|
|
146
|
+
if (DSP) {
|
|
147
|
+
DSP.ctrlChange(channel, ctrl, value);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {MessageEvent} ev
|
|
153
|
+
*/
|
|
154
|
+
const midiMessageReceived = (ev) => {
|
|
155
|
+
var cmd = ev.data[0] >> 4;
|
|
156
|
+
var channel = ev.data[0] & 0xf;
|
|
157
|
+
var data1 = ev.data[1];
|
|
158
|
+
var data2 = ev.data[2];
|
|
159
|
+
|
|
160
|
+
if (channel === 9) {
|
|
161
|
+
return;
|
|
162
|
+
} else if (cmd === 8 || ((cmd === 9) && (data2 === 0))) {
|
|
163
|
+
keyOff(channel, data1, data2);
|
|
164
|
+
} else if (cmd === 9) {
|
|
165
|
+
keyOn(channel, data1, data2);
|
|
166
|
+
} else if (cmd === 11) {
|
|
167
|
+
ctrlChange(channel, data1, data2);
|
|
168
|
+
} else if (cmd === 14) {
|
|
169
|
+
pitchWheel(channel, (data2 * 128.0 + data1));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/*
|
|
173
|
+
// Direct message
|
|
174
|
+
if (DSP && isPoly) {
|
|
175
|
+
DSP.midiMessage(ev.data);
|
|
176
|
+
}
|
|
177
|
+
*/
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* @param {Error} error
|
|
181
|
+
*/
|
|
182
|
+
const onerrorcallback = (error) => {
|
|
183
|
+
console.log(error);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const onsuccesscallbackStandard = (access) => {
|
|
187
|
+
access.onstatechange = function (e) {
|
|
188
|
+
if (e.port.type === "input") {
|
|
189
|
+
if (e.port.state === "connected") {
|
|
190
|
+
console.log(e.port.name + " is connected");
|
|
191
|
+
e.port.onmidimessage = midiMessageReceived;
|
|
192
|
+
} else if (e.port.state === "disconnected") {
|
|
193
|
+
console.log(e.port.name + " is disconnected");
|
|
194
|
+
e.port.onmidimessage = null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (const input of access.inputs.values()) {
|
|
200
|
+
input.onmidimessage = midiMessageReceived;
|
|
201
|
+
console.log(input.name + " is connected");
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const activateMIDIInput = () => {
|
|
206
|
+
console.log("activateMIDIInput");
|
|
207
|
+
if (typeof (navigator.requestMIDIAccess) !== "undefined") {
|
|
208
|
+
navigator.requestMIDIAccess().then(onsuccesscallbackStandard, onerrorcallback);
|
|
209
|
+
} else {
|
|
210
|
+
alert("MIDI input cannot be activated, either your browser still does't have it, or you need to explicitly activate it.");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Audio input handling */
|
|
215
|
+
const activateAudioInput = () => {
|
|
216
|
+
console.log("activateAudioInput");
|
|
217
|
+
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
218
|
+
navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: false } })
|
|
219
|
+
.then(getDevice)
|
|
220
|
+
.catch((e) => {
|
|
221
|
+
alert('Error getting audio input');
|
|
222
|
+
console.log(e);
|
|
223
|
+
audio_input = null;
|
|
224
|
+
});
|
|
225
|
+
} else {
|
|
226
|
+
alert('Audio input API not available');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @param {MediaStream} device
|
|
232
|
+
*/
|
|
233
|
+
const getDevice = (device) => {
|
|
234
|
+
// Create an AudioNode from the stream.
|
|
235
|
+
audio_input = audio_context.createMediaStreamSource(device);
|
|
236
|
+
|
|
237
|
+
// Connect it to the destination.
|
|
238
|
+
audio_input.connect(DSP);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const setLocalStorage = (state) => {
|
|
242
|
+
console.log(state);
|
|
243
|
+
setStorageItemValue('FaustLibTester', 'FaustLocalStorage', ((state) ? "on" : "off"));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Save/Load functions using local storage
|
|
248
|
+
*
|
|
249
|
+
* @param {string} id
|
|
250
|
+
* @param {string} value
|
|
251
|
+
*/
|
|
252
|
+
const restoreMenu = (id, value) => {
|
|
253
|
+
/** @type {HTMLSelectElement} */
|
|
254
|
+
const menu = document.getElementById(id);
|
|
255
|
+
for (let i = 0; i < menu.length; i++) {
|
|
256
|
+
// Weak comparison here
|
|
257
|
+
if (menu.options[i].value == value) {
|
|
258
|
+
menu.selectedIndex = i;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* set item from local storage 'item_key' key
|
|
266
|
+
*
|
|
267
|
+
* @param {string} item_key
|
|
268
|
+
* @param {string} key
|
|
269
|
+
* @returns
|
|
270
|
+
*/
|
|
271
|
+
const getStorageItemValue = (item_key, key) => {
|
|
272
|
+
if (localStorage.getItem(item_key)) {
|
|
273
|
+
var item_value = JSON.parse(localStorage.getItem(item_key));
|
|
274
|
+
var item_index = item_value.findIndex((obj => obj[0] === key));
|
|
275
|
+
return (item_index >= 0) ? item_value[item_index][1] : null;
|
|
276
|
+
} else {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* get [key, value] in local storage item_key key
|
|
283
|
+
* @param {string} item_key
|
|
284
|
+
* @param {string} key
|
|
285
|
+
* @param {string} value
|
|
286
|
+
*/
|
|
287
|
+
const setStorageItemValue = (item_key, key, value) => {
|
|
288
|
+
var item_value;
|
|
289
|
+
if (localStorage.getItem(item_key)) {
|
|
290
|
+
item_value = JSON.parse(localStorage.getItem(item_key));
|
|
291
|
+
} else {
|
|
292
|
+
item_value = [];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Possibly update an existing 'key'
|
|
296
|
+
var item_index = item_value.findIndex((obj => obj[0] === key));
|
|
297
|
+
if (item_index >= 0) {
|
|
298
|
+
item_value[item_index][1] = value;
|
|
299
|
+
// Otherwise push a new [key, value]
|
|
300
|
+
} else {
|
|
301
|
+
item_value.push([key, value]);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
localStorage.setItem(item_key, JSON.stringify(item_value));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const saveDSPState = () => {
|
|
308
|
+
var params = DSP.getParams();
|
|
309
|
+
for (var i = 0; i < params.length; i++) {
|
|
310
|
+
setStorageItemValue('DSP', params[i], DSP.getParamValue(params[i]).toString());
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const loadDSPState = () => {
|
|
315
|
+
var params = DSP.getParams();
|
|
316
|
+
for (var i = 0; i < params.length; i++) {
|
|
317
|
+
var value = getStorageItemValue('DSP', params[i]);
|
|
318
|
+
if (value) {
|
|
319
|
+
// Restore DSP state
|
|
320
|
+
DSP.setParamValue(params[i], Number(value));
|
|
321
|
+
// Restore GUI state
|
|
322
|
+
output_handler(params[i], Number(value));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const savePageState = () => {
|
|
328
|
+
if (getStorageItemValue('FaustLibTester', 'FaustLocalStorage') === "on") {
|
|
329
|
+
setStorageItemValue('FaustLibTester', 'buffer_size', buffer_size.toString());
|
|
330
|
+
setStorageItemValue('FaustLibTester', 'poly_flag', poly_flag);
|
|
331
|
+
setStorageItemValue('FaustLibTester', 'ftz_flag', ftz_flag);
|
|
332
|
+
setStorageItemValue('FaustLibTester', 'sample_format', sample_format);
|
|
333
|
+
setStorageItemValue('FaustLibTester', 'poly_nvoices', poly_nvoices.toString());
|
|
334
|
+
setStorageItemValue('FaustLibTester', 'rendering_mode', rendering_mode);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const loadPageState = () => {
|
|
339
|
+
if (getStorageItemValue('FaustLibTester', 'FaustLocalStorage') === "on") {
|
|
340
|
+
buffer_size = (getStorageItemValue('FaustLibTester', 'buffer_size') ? getStorageItemValue('FaustLibTester', 'buffer_size') : 256);
|
|
341
|
+
poly_flag = (getStorageItemValue('FaustLibTester', 'poly_flag') ? getStorageItemValue('FaustLibTester', 'poly_flag') : "OFF");
|
|
342
|
+
poly_nvoices = (getStorageItemValue('FaustLibTester', 'poly_nvoices') ? getStorageItemValue('FaustLibTester', 'poly_nvoices') : 16);
|
|
343
|
+
ftz_flag = (getStorageItemValue('FaustLibTester', 'ftz_flag') ? getStorageItemValue('FaustLibTester', 'ftz_flag') : 2);
|
|
344
|
+
sample_format = (getStorageItemValue('FaustLibTester', 'sample_format') ? getStorageItemValue('FaustLibTester', 'sample_format') : "float");
|
|
345
|
+
rendering_mode = (getStorageItemValue('FaustLibTester', 'rendering_mode') ? getStorageItemValue('FaustLibTester', 'rendering_mode') : "ScriptProcessor");
|
|
346
|
+
|
|
347
|
+
// Restore menus
|
|
348
|
+
restoreMenu("selectedBuffer", buffer_size.toString());
|
|
349
|
+
restoreMenu("selectedPoly", poly_flag);
|
|
350
|
+
restoreMenu("polyVoices", poly_nvoices.toString());
|
|
351
|
+
restoreMenu("selectedFTZ", ftz_flag);
|
|
352
|
+
restoreMenu("selectedSampleFormat", sample_format);
|
|
353
|
+
restoreMenu("selectedRenderingMode", rendering_mode);
|
|
354
|
+
|
|
355
|
+
if (rendering_mode === "AudioWorklet") {
|
|
356
|
+
document.getElementById("selectedBuffer").disabled = true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @param {DragEvent | InputEvent} e
|
|
363
|
+
*/
|
|
364
|
+
const fileDragHover = (e) => {
|
|
365
|
+
e.stopPropagation();
|
|
366
|
+
e.preventDefault();
|
|
367
|
+
e.target.className = (e.type === "dragover" ? "hover" : "");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* @param {InputEvent} e
|
|
372
|
+
*/
|
|
373
|
+
const fileSelectHandler = (e) => {
|
|
374
|
+
fileDragHover(e);
|
|
375
|
+
/** @type {FileList} */
|
|
376
|
+
const files = e.target.files || e.dataTransfer.files;
|
|
377
|
+
uploadFile(files[0]);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @param {DragEvent} e
|
|
382
|
+
*/
|
|
383
|
+
const uploadOn = (e) => {
|
|
384
|
+
return new Promise((callback, reject) => {
|
|
385
|
+
if (!isWasm) {
|
|
386
|
+
alert("WebAssembly is not supported in this browser !");
|
|
387
|
+
reject("WebAssembly is not supported in this browser !");
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// CASE 1 : THE DROPPED OBJECT IS A URL TO SOME FAUST CODE
|
|
391
|
+
if (e.dataTransfer.getData('URL') && e.dataTransfer.getData('URL').split(':').shift() != "file") {
|
|
392
|
+
const url = e.dataTransfer.getData('URL');
|
|
393
|
+
let filename = url.toString().split('/').pop();
|
|
394
|
+
filename = filename.toString().split('.').shift();
|
|
395
|
+
const xmlhttp = new XMLHttpRequest();
|
|
396
|
+
|
|
397
|
+
xmlhttp.onreadystatechange = () => {
|
|
398
|
+
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
|
|
399
|
+
dsp_code = xmlhttp.responseText;
|
|
400
|
+
callback(dsp_code);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
try {
|
|
405
|
+
xmlhttp.open("GET", url, false);
|
|
406
|
+
// Avoid error "mal formé" on firefox
|
|
407
|
+
xmlhttp.overrideMimeType('text/html');
|
|
408
|
+
xmlhttp.send();
|
|
409
|
+
} catch (err) {
|
|
410
|
+
alert(err);
|
|
411
|
+
reject(err);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
} else if (e.dataTransfer.getData('URL').split(':').shift() != "file") {
|
|
415
|
+
dsp_code = e.dataTransfer.getData('text');
|
|
416
|
+
|
|
417
|
+
// CASE 2 : THE DROPPED OBJECT IS SOME FAUST CODE
|
|
418
|
+
if (dsp_code) {
|
|
419
|
+
callback(dsp_code);
|
|
420
|
+
}
|
|
421
|
+
// CASE 3 : THE DROPPED OBJECT IS A FILE CONTAINING SOME FAUST CODE
|
|
422
|
+
else {
|
|
423
|
+
/** @type {FileList} */
|
|
424
|
+
const files = e.target.files || e.dataTransfer.files;
|
|
425
|
+
const file = files[0];
|
|
426
|
+
|
|
427
|
+
if (location.host.indexOf("sitepointstatic") >= 0) return;
|
|
428
|
+
|
|
429
|
+
const request = new XMLHttpRequest();
|
|
430
|
+
if (request.upload) {
|
|
431
|
+
|
|
432
|
+
const reader = new FileReader();
|
|
433
|
+
const ext = file.name.toString().split('.').pop();
|
|
434
|
+
const filename = file.name.toString().split('.').shift();
|
|
435
|
+
let type;
|
|
436
|
+
|
|
437
|
+
if (ext === "dsp") {
|
|
438
|
+
type = "dsp";
|
|
439
|
+
reader.readAsText(file);
|
|
440
|
+
} else if (ext === "json") {
|
|
441
|
+
type = "json";
|
|
442
|
+
reader.readAsText(file);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
reader.onloadend = (e) => {
|
|
446
|
+
dsp_code = reader.result;
|
|
447
|
+
callback(dsp_code);
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// CASE 4 : ANY OTHER STRANGE THING
|
|
453
|
+
else {
|
|
454
|
+
window.alert("This object is not Faust code...");
|
|
455
|
+
reject(new Error("This object is not Faust code..."));
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const checkPolyphonicDSP = (json) => {
|
|
461
|
+
if (!(((json.indexOf("/freq") !== -1) || (json.indexOf("/key") !== -1))
|
|
462
|
+
&& (json.indexOf("/gate") !== -1)
|
|
463
|
+
&& ((json.indexOf("/gain") !== -1) || (json.indexOf("/vel") !== -1) || (json.indexOf("/velocity") !== -1)))) {
|
|
464
|
+
alert("Faust DSP code is not Polyphonic, it will probably not work correctly in this mode...")
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const deleteDSP = () => {
|
|
469
|
+
if (DSP) {
|
|
470
|
+
if (audio_input) {
|
|
471
|
+
audio_input.disconnect(DSP);
|
|
472
|
+
}
|
|
473
|
+
DSP.disconnect(audio_context.destination);
|
|
474
|
+
DSP.destroy();
|
|
475
|
+
faustUIRoot.innerHTML = "";
|
|
476
|
+
DSP = null;
|
|
477
|
+
faustUI = null;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const activateMonoDSP = (dsp) => {
|
|
482
|
+
if (!dsp) {
|
|
483
|
+
alert(faust_compiler.getErrorMessage());
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
DSP = dsp;
|
|
488
|
+
if (DSP.getNumInputs() > 0) {
|
|
489
|
+
activateAudioInput();
|
|
490
|
+
} else {
|
|
491
|
+
audio_input = null;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Setup UI
|
|
495
|
+
faustUI = new FaustUI({ ui: DSP.getUI(), root: faustUIRoot });
|
|
496
|
+
faustUI.paramChangeByUI = (path, value) => DSP.setParamValue(path, value);
|
|
497
|
+
DSP.setOutputParamHandler(output_handler);
|
|
498
|
+
console.log(DSP.getNumInputs());
|
|
499
|
+
console.log(DSP.getNumOutputs());
|
|
500
|
+
//DSP.metadata({ declare: function(key, value) { console.log("key = " + key + " value = " + value); }});
|
|
501
|
+
DSP.connect(audio_context.destination);
|
|
502
|
+
// DSP has to be explicitly started
|
|
503
|
+
DSP.start();
|
|
504
|
+
loadDSPState();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const activatePolyDSP = (dsp) => {
|
|
508
|
+
if (!dsp) {
|
|
509
|
+
alert(faust_compiler.getErrorMessage());
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
checkPolyphonicDSP(dsp.getJSON());
|
|
513
|
+
DSP = dsp;
|
|
514
|
+
|
|
515
|
+
if (DSP.getNumInputs() > 0) {
|
|
516
|
+
activateAudioInput();
|
|
517
|
+
} else {
|
|
518
|
+
audio_input = null;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Setup UI
|
|
522
|
+
faustUI = new FaustUI({ ui: DSP.getUI(), root: faustUIRoot });
|
|
523
|
+
faustUI.paramChangeByUI = (path, value) => DSP.setParamValue(path, value);
|
|
524
|
+
DSP.setOutputParamHandler(output_handler);
|
|
525
|
+
console.log(DSP.getNumInputs());
|
|
526
|
+
console.log(DSP.getNumOutputs());
|
|
527
|
+
//DSP.metadata({ declare: function(key, value) { console.log("key = " + key + " value = " + value); }});
|
|
528
|
+
DSP.connect(audio_context.destination);
|
|
529
|
+
// DSP has to be explicitly started
|
|
530
|
+
DSP.start();
|
|
531
|
+
loadDSPState();
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const compileDSP = async () => {
|
|
535
|
+
if (!dsp_code) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
deleteDSP();
|
|
540
|
+
|
|
541
|
+
// Prepare argv list
|
|
542
|
+
var argv = "-ftz " + ftz_flag.toString() + " -" + sample_format;
|
|
543
|
+
console.log(argv);
|
|
544
|
+
|
|
545
|
+
if (poly_flag === "ON") {
|
|
546
|
+
isPoly = true;
|
|
547
|
+
console.log("Poly DSP");
|
|
548
|
+
await faust_poly_factory.compile(faust_compiler, "FaustDSP", dsp_code, argv);
|
|
549
|
+
DSP = await faust_poly_factory.createNode(audio_context, poly_nvoices, undefined, undefined, undefined, undefined, (rendering_mode === "ScriptProcessor"), buffer_size);
|
|
550
|
+
activatePolyDSP(DSP);
|
|
551
|
+
} else {
|
|
552
|
+
isPoly = false;
|
|
553
|
+
console.log("Mono DSP");
|
|
554
|
+
await faust_mono_factory.compile(faust_compiler, "FaustDSP", dsp_code, argv);
|
|
555
|
+
DSP = await faust_mono_factory.createNode(audio_context, undefined, undefined, (rendering_mode === "ScriptProcessor"), buffer_size);
|
|
556
|
+
activateMonoDSP(DSP);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* @param {DragEvent} e
|
|
562
|
+
*/
|
|
563
|
+
const uploadFile = async (e) => {
|
|
564
|
+
fileDragHover(e);
|
|
565
|
+
await uploadOn(e);
|
|
566
|
+
compileDSP()
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const initPage = () => {
|
|
570
|
+
// Restore 'save' checkbox state
|
|
571
|
+
document.getElementById("localstorage").checked = (getStorageItemValue('FaustLibTester', 'FaustLocalStorage') === "on");
|
|
572
|
+
|
|
573
|
+
// Load page state
|
|
574
|
+
loadPageState();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const init = async () => {
|
|
578
|
+
const {
|
|
579
|
+
instantiateFaustModuleFromFile,
|
|
580
|
+
FaustCompiler,
|
|
581
|
+
FaustMonoDspGenerator,
|
|
582
|
+
FaustPolyDspGenerator,
|
|
583
|
+
LibFaust
|
|
584
|
+
} = await import("./faustwasm/index.js");
|
|
585
|
+
FaustUI = (await import("./faust-ui/index.js")).FaustUI;
|
|
586
|
+
// Init Faust compiler and node factory
|
|
587
|
+
const module = await instantiateFaustModuleFromFile(new URL("../../libfaust-wasm/libfaust-wasm.js", location.href).href);
|
|
588
|
+
// const module = await instantiateFaustModule();
|
|
589
|
+
const libFaust = new LibFaust(module);
|
|
590
|
+
faust_compiler = new FaustCompiler(libFaust);
|
|
591
|
+
faust_mono_factory = new FaustMonoDspGenerator();
|
|
592
|
+
faust_poly_factory = new FaustPolyDspGenerator();
|
|
593
|
+
|
|
594
|
+
// Check AudioWorklet support
|
|
595
|
+
if (!workletAvailable) {
|
|
596
|
+
document.getElementById("selectedRenderingMode").disabled = true;
|
|
597
|
+
alert("AudioWorklet is not supported, ScriptProcessor model only will be available");
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
activateMIDIInput();
|
|
601
|
+
|
|
602
|
+
const filedrag1 = document.getElementById("filedrag");
|
|
603
|
+
filedrag1.addEventListener("dragover", fileDragHover, false);
|
|
604
|
+
filedrag1.addEventListener("dragleave", fileDragHover, false);
|
|
605
|
+
filedrag1.addEventListener("drop", uploadFile, false);
|
|
606
|
+
filedrag1.textContent = "Drop a Faust .dsp file or URL here (compiled using libfaust version " + faust_compiler.version() + ")";
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Init page
|
|
610
|
+
initPage();
|
|
611
|
+
|
|
612
|
+
// Timer to save page and DSP state to local storage
|
|
613
|
+
setInterval(() => { savePageState(); if (DSP) { saveDSPState(); } }, 1000);
|
|
614
|
+
|
|
615
|
+
// Init Faust part
|
|
616
|
+
init();
|
|
617
|
+
|
|
618
|
+
// To activate audio on iOS
|
|
619
|
+
window.addEventListener('touchstart', function () {
|
|
620
|
+
|
|
621
|
+
// create empty buffer
|
|
622
|
+
var buffer = audio_context.createBuffer(1, 1, 22050);
|
|
623
|
+
var source = audio_context.createBufferSource();
|
|
624
|
+
source.buffer = buffer;
|
|
625
|
+
|
|
626
|
+
// connect to output (your speakers)
|
|
627
|
+
source.connect(audio_context.destination);
|
|
628
|
+
|
|
629
|
+
// play the file
|
|
630
|
+
source.start();
|
|
631
|
+
|
|
632
|
+
}, false);
|
|
633
|
+
|
|
634
|
+
// On desktop
|
|
635
|
+
window.addEventListener("mousedown", () => {
|
|
636
|
+
if (audio_context.state !== "suspended") return;
|
|
637
|
+
audio_context.resume().then(() => console.log("Audio resumed"))
|
|
638
|
+
});
|
package/test/guitar.dsp
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import("stdfaust.lib"); process = pm.guitar_ui_MIDI <: _,_;
|
package/test/guitar1.dsp
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<html lang="en">
|
|
2
|
+
<head>
|
|
3
|
+
<meta charset="UTF-8" />
|
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
5
|
+
<title>Faust Wasm Generator Demo</title>
|
|
6
|
+
</head>
|
|
7
|
+
|
|
8
|
+
<body>
|
|
9
|
+
<p id="log">...<br /></p>
|
|
10
|
+
|
|
11
|
+
<script src="../../dist/cjs-bundle/index.js"></script>
|
|
12
|
+
<script type="module" src="index.js"></script>
|
|
13
|
+
</body>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
const div = document.getElementById("log");
|
|
3
|
+
const log = (/** @type {string} */str) => div.innerHTML += str.replace("\n", "<br />") + "<br />";
|
|
4
|
+
const options = "-ftz 0";
|
|
5
|
+
|
|
6
|
+
(async () => {
|
|
7
|
+
const ctx = new AudioContext();
|
|
8
|
+
globalThis.ctx = ctx;
|
|
9
|
+
globalThis.faustModule = await faustwasm.instantiateFaustModule();
|
|
10
|
+
const post = async () => {
|
|
11
|
+
const {
|
|
12
|
+
instantiateFaustModule,
|
|
13
|
+
LibFaust,
|
|
14
|
+
getFaustAudioWorkletProcessor,
|
|
15
|
+
WavEncoder,
|
|
16
|
+
FaustWasmInstantiator,
|
|
17
|
+
FaustMonoDspGenerator,
|
|
18
|
+
FaustPolyDspGenerator,
|
|
19
|
+
FaustMonoWebAudioDsp,
|
|
20
|
+
FaustOfflineProcessor,
|
|
21
|
+
FaustCompiler,
|
|
22
|
+
FaustSvgDiagrams
|
|
23
|
+
} = faustwasm;
|
|
24
|
+
const faustModule = await instantiateFaustModule();
|
|
25
|
+
const code = `import("stdfaust.lib");
|
|
26
|
+
process = os.osc(440);`;
|
|
27
|
+
const gen = new FaustMonoDspGenerator();
|
|
28
|
+
const libFaust = new LibFaust(faustModule);
|
|
29
|
+
const compiler = new FaustCompiler(libFaust);
|
|
30
|
+
console.log(compiler.version());
|
|
31
|
+
await gen.compile(compiler, 'dsp', code, "-ftz 0");
|
|
32
|
+
|
|
33
|
+
console.log(await gen.createAudioWorkletProcessor());
|
|
34
|
+
const processor = await gen.createOfflineProcessor(48000, 128);
|
|
35
|
+
console.log(processor);
|
|
36
|
+
console.log(processor.render([], 48000));
|
|
37
|
+
};
|
|
38
|
+
await ctx.audioWorklet.addModule("../../dist/cjs-bundle/index.js");
|
|
39
|
+
await ctx.audioWorklet.addModule(URL.createObjectURL(new Blob([`(${post.toString()})()`], { type: "application/javascript" })));
|
|
40
|
+
})();
|
package/test/mono.dsp
ADDED