Faustで作ったサウンドをp5.jsから使う方法のメモ
Faustに関する説明はこちらがわかりやすかったです。
https://matsuuratomoya.com/blog/2016-12-01/faust_introduction/
FaustをWasmビルドする
公式サンプルの gameaudio > bubble.dsp
を使います。
グラフィック的にbubbleというよりはdropなので、
ファイル名は drop.dsp
としました。
import("stdfaust.lib");
bubble(f0,trig) = os.osc(f) * (exp(-damp*time) : si.smooth(0.99))
with {
damp = 0.043*f0 + 0.0014*f0^(3/2);
f = f0*(1+sigma*time);
sigma = eta * damp;
eta = 0.075;
time = 0 : (select2(trig>trig'):+(1)) ~ _ : ba.samp2sec;
};
process = button("drop") : bubble(hslider("v:freq", 600, 150, 2000, 1)) <: dm.freeverb_demo;
ローカルでビルドするなら faust2wasm
でビルドできます。
macOSならHomebrewから brew install faust
でインストールできます。
ビルドコマンドはこちらです。
ビルドすると drop.wasm
drop.js
が出力されます。
faust2wasm drop.dsp
p5.jsのコード
Wasmを扱うためのJSのコードは、先ほど出力した drop.js
に含まれています。
以下のように ScriptProcessorNode
に設定できるパラメータを確認することができます。
drop.createDSP(audioContext, 1024)
.then(node => {
dropNode = node;
dropNode.connect(audioContext.destination);
console.log('params: ', dropNode.getParams());
});
例えば、鳴らす音の周波数を変更するなら /drop/freq/0x00
を設定します。
dropNode.setParamValue('/drop/freq/0x00', 440)
と呼び出すことで440Hzに設定されます。
こちらが出力結果のWasmをp5.jsから使うためのHTMLとJSです。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>p5.js faust example</title>
<script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.js"></script>
<script src="drop.js"></script>
<script src="sketch.js"></script>
</head>
<body>
</body>
</html>
class Drop {
constructor(pos) {
this.pos = pos;
this.rad = 0;
this.life = 255;
}
update() {
this.rad++;
this.life -= 3;
}
draw() {
push();
stroke(255, this.life);
noFill();
circle(this.pos.x, this.pos.y, this.rad * 2);
pop();
}
}
const audioContext = new AudioContext();
let dropNode = null;
let drops = [];
function setup() {
createCanvas(600, 600);
drop.createDSP(audioContext, 1024)
.then(node => {
dropNode = node;
dropNode.connect(audioContext.destination);
console.log('params: ', dropNode.getParams());
});
}
function draw() {
background(0);
for (const drop of drops) {
drop.update();
drop.draw();
}
drops = drops.filter(b => b.life > 0);
}
function mousePressed() {
if (!dropNode) {
return;
}
if (audioContext.state === 'suspended') {
audioContext.resume();
}
dropNode.setParamValue('/drop/drop', 1.0);
// dropNode.setParamValue('/drop/Freeverb/Wet', random());
// dropNode.setParamValue('/drop/Freeverb/0x00/Damp', random());
// dropNode.setParamValue('/drop/Freeverb/0x00/RoomSize', random());
// dropNode.setParamValue('/drop/Freeverb/0x00/Stereo_Spread', random());
dropNode.setParamValue('/drop/freq/0x00', random(440, 880));
drops.push(new Drop(createVector(mouseX, mouseY)));
}
function mouseReleased() {
if (!dropNode) {
return;
}
dropNode.setParamValue('/drop/drop', 0.0);
}