Skip to content

Commit 81afcb5

Browse files
committed
Make faust.js work (thanks B.Matuszewski), add faust-params.js example.
1 parent b907f94 commit 81afcb5

2 files changed

Lines changed: 119 additions & 25 deletions

File tree

‎examples/faust-params.js‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Example: Faust DSP with parameters and runtime tweaks using node-web-audio-api.
2+
// Requires: npm install @grame/faustwasm
3+
// Usage: node faust-params.js
4+
5+
import { fileURLToPath } from 'node:url';
6+
import { AudioContext, AudioWorkletNode } from '../index.mjs';
7+
8+
// Expose AudioWorkletNode globally so faustwasm can patch it into worklet factories.
9+
if (typeof globalThis.AudioWorkletNode === 'undefined') {
10+
globalThis.AudioWorkletNode = AudioWorkletNode;
11+
}
12+
13+
const {
14+
instantiateFaustModuleFromFile,
15+
LibFaust,
16+
FaustCompiler,
17+
FaustMonoDspGenerator,
18+
} = await import('@grame/faustwasm/dist/esm/index.js');
19+
20+
const audioContext = new AudioContext({ latencyHint: 'interactive' });
21+
22+
// Stereo tone with controllable gain, pitch and filter cutoff.
23+
const dspCode = `
24+
import("stdfaust.lib");
25+
26+
gain = hslider("gain[dB]", -6, -24, 6, 0.1) : ba.db2linear;
27+
freq = hslider("freq[Hz]", 220, 50, 2000, 1);
28+
cutoff = hslider("cutoff[Hz]", 800, 200, 4000, 1);
29+
30+
process = vgroup("faust-params",
31+
os.osc(freq) * gain : fi.lowpass(2, cutoff)
32+
) <: _,_;
33+
`;
34+
35+
// Load the Faust toolchain from the wasm distribution.
36+
const faustModule = await instantiateFaustModuleFromFile(
37+
fileURLToPath(import.meta.resolve('@grame/faustwasm/libfaust-wasm/libfaust-wasm.js')),
38+
fileURLToPath(import.meta.resolve('@grame/faustwasm/libfaust-wasm/libfaust-wasm.data')),
39+
fileURLToPath(import.meta.resolve('@grame/faustwasm/libfaust-wasm/libfaust-wasm.wasm')),
40+
);
41+
42+
const libFaust = new LibFaust(faustModule);
43+
const compiler = new FaustCompiler(libFaust);
44+
const generator = new FaustMonoDspGenerator();
45+
46+
const compiled = await generator.compile(compiler, 'faust-params', dspCode, '-ftz 2');
47+
if (!compiled) {
48+
throw new Error('Faust compilation failed');
49+
}
50+
51+
const faustNode = await generator.createNode(audioContext);
52+
if (!faustNode) {
53+
throw new Error('Failed to create Faust node');
54+
}
55+
56+
console.log('Faust parameterized node created.', faustNode);
57+
58+
faustNode.connect(audioContext.destination);
59+
faustNode.start();
60+
61+
62+
// Helpers to inspect and tweak parameters. The parameter names come from the Faust UI paths.
63+
const params = faustNode.getParams?.() ?? [];
64+
console.log('Available parameters:', params);
65+
66+
const set = (name, value) => {
67+
faustNode.setParamValue(name, value);
68+
console.log(`Set ${name} -> ${value}`);
69+
};
70+
71+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
72+
73+
// Parameter automation demo.
74+
set('/faust-params/gain', -12);
75+
set('/faust-params/freq', 220);
76+
set('/faust-params/cutoff', 800);
77+
set('/faust-params/resonance', 0.9);
78+
79+
console.log('Sweeping parameters for 6 seconds...');
80+
await sleep(1500);
81+
set('/faust-params/freq', 440);
82+
set('/faust-params/cutoff', 1200);
83+
84+
await sleep(1500);
85+
set('/faust-params/gain', -3);
86+
set('/faust-params/resonance', 1.8);
87+
88+
await sleep(1500);
89+
set('/faust-params/freq', 660);
90+
set('/faust-params/cutoff', 1800);
91+
92+
await sleep(1500);
93+
console.log('Stopping...');
94+
95+
faustNode.stop();
96+
await audioContext.close();

‎examples/faust.js‎

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,58 @@
1+
2+
3+
// Example: run a simple Faust oscillator graph using node-web-audio-api.
14
// Requires: npm install @grame/faustwasm
2-
import path from 'node:path';
5+
// Usage: node faust.js
6+
37
import { fileURLToPath } from 'node:url';
48
import { AudioContext, AudioWorkletNode } from '../index.mjs';
5-
import {
6-
instantiateFaustModuleFromFile,
7-
LibFaust,
8-
FaustCompiler,
9-
FaustMonoDspGenerator,
10-
} from '@grame/faustwasm/dist/esm/index.js';
119

12-
// Ensure the AudioWorkletNode constructor is available globally for faustwasm.
10+
// Expose AudioWorkletNode globally so faustwasm can patch it into worklet factories.
1311
if (typeof globalThis.AudioWorkletNode === 'undefined') {
1412
globalThis.AudioWorkletNode = AudioWorkletNode;
1513
}
1614

17-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
18-
const faustWasmDir = path.resolve(__dirname, '../node_modules/@grame/faustwasm/libfaust-wasm');
15+
const {
16+
instantiateFaustModuleFromFile,
17+
LibFaust,
18+
FaustCompiler,
19+
FaustMonoDspGenerator,
20+
} = await import('@grame/faustwasm/dist/esm/index.js');
21+
1922
const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
2023
const audioContext = new AudioContext({ latencyHint });
2124

25+
// Minimal Faust DSP: two sine oscillators mixed to stereo.
2226
const dspCode = `
2327
import("stdfaust.lib");
24-
process = os.osc(440);
28+
process = os.osc(440)*0.3, os.osc(800)*0.3;
2529
`;
2630

31+
// Load the Faust toolchain from the wasm distribution.
2732
const faustModule = await instantiateFaustModuleFromFile(
28-
path.join(faustWasmDir, 'libfaust-wasm.js'),
29-
path.join(faustWasmDir, 'libfaust-wasm.data'),
30-
path.join(faustWasmDir, 'libfaust-wasm.wasm'),
33+
fileURLToPath(import.meta.resolve('@grame/faustwasm/libfaust-wasm/libfaust-wasm.js')),
34+
fileURLToPath(import.meta.resolve('@grame/faustwasm/libfaust-wasm/libfaust-wasm.data')),
35+
fileURLToPath(import.meta.resolve('@grame/faustwasm/libfaust-wasm/libfaust-wasm.wasm')),
3136
);
3237

3338
const libFaust = new LibFaust(faustModule);
3439
const compiler = new FaustCompiler(libFaust);
3540
const generator = new FaustMonoDspGenerator();
3641

37-
const compiled = await generator.compile(compiler, 'faust-osc', dspCode, '-I libraries/');
42+
const compiled = await generator.compile(compiler, 'faust-osc', dspCode, '-ftz 2');
3843
if (!compiled) {
3944
throw new Error('Faust compilation failed');
4045
}
4146

42-
const faustNode = await generator.createNode(audioContext, 'faust-osc', generator.factory, false);
47+
const faustNode = await generator.createNode(audioContext);
4348
if (!faustNode) {
4449
throw new Error('Failed to create Faust node');
4550
}
4651

4752
console.log('Faust node created successfully.', faustNode);
4853

49-
//if (typeof faustNode.connect === 'function') {
50-
faustNode.connect(audioContext.destination);
51-
//}
52-
53-
//if (typeof faustNode.start === 'function') {
54-
faustNode.start();
55-
//}
56-
57-
await audioContext.resume();
54+
faustNode.connect(audioContext.destination);
55+
faustNode.start();
5856

5957
console.log('Faust oscillator playing for 4 seconds...');
6058
await new Promise(resolve => setTimeout(resolve, 4000));

0 commit comments

Comments
 (0)