Repository navigation
Expand file tree
/
Copy pathkarplus-strong.js
More file actions
42 lines (34 loc) · 1.99 KB
/
Copy pathkarplus-strong.js
File metadata and controls
42 lines (34 loc) · 1.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// Karplus-Strong — plucked string from noise + delay feedback.
// Run: node examples/karplus-strong.js 220 4s
// Run: node examples/karplus-strong.js freq=440 -d 2s
// Keys: space pause · p pluck · ↑/↓ ±semitone · q quit
import { AudioContext, AudioWorkletNode } from 'web-audio-api'
import { init } from './graphs/karplus-strong.js'
import { args, num, sec, keys, status, clearLine, noteName, pausedTag, help } from './utils.js'
help({
description: 'synthesize a plucked string from noise and feedback',
usage: ['', '[frequency] [duration]', 'freq=A4 dur=2s'],
options: [
['freq=<hz|note>', 'string frequency (default: 220)'],
['decay=<s>', 'time for the pluck to fade 60 dB (default: 4)'],
['-d, --duration <time>', 'run time with optional s/m/h suffix (default: 30s)'],
],
controls: [['Space', 'pause/resume'], ['P', 'pluck again'], ['↑ / ↓', 'move one semitone and pluck'], ['Q / Esc', 'quit']],
})
let { pos, $ } = args()
let f = num(pos.find(t => /^\d/.test(t) && !/[smh]$/.test(t) || /^[A-G][#b]?\d$/i.test(t)) || $('freq', 220))
let decay = num($('decay', 4))
let dur = sec(pos.find(t => /\d[smh]$/.test(t)) || $('dur', '30'))
let ctx = new AudioContext()
await ctx.resume()
let demo = await init(ctx, { frequency: f, decay, duration: dur, AudioWorkletNodeClass: AudioWorkletNode })
let node = demo.data.node
let render = status()
let ui = setInterval(() => render(`Karplus-Strong · ${f.toFixed(1)}Hz ${noteName(f)} · space pause · p pluck · ↑↓ semi · q quit${pausedTag(ctx)}`), 80)
keys({
p: () => node.port.postMessage({ frequency: f }),
up: () => { f *= 2 ** (1/12); node.port.postMessage({ frequency: f }) },
down: () => { f *= 2 ** (-1/12); node.port.postMessage({ frequency: f }) },
}, () => { clearInterval(ui); clearLine(); ctx.close() }, ctx)
console.log(`Karplus-Strong pluck @ ${f}Hz ${noteName(f)} (${dur}s) space pause · p pluck · ↑↓ semi · q quit`)
setTimeout(() => { clearInterval(ui); clearLine(); ctx.close(); process.exit(0) }, dur * 1000)