-
Notifications
You must be signed in to change notification settings - Fork 71
Expand file tree
/
Copy pathbeating.js
More file actions
51 lines (41 loc) · 2.15 KB
/
Copy pathbeating.js
File metadata and controls
51 lines (41 loc) · 2.15 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
43
44
45
46
47
48
49
50
51
// Beating — two close frequencies create amplitude modulation.
// Run: node examples/beating.js 440 3 5s
// Run: node examples/beating.js freq=440 diff=3 -d 5s
// Keys: ←/→ ±0.5 Hz beat · ↑/↓ ±semitone carrier · q quit
import { AudioContext } from 'web-audio-api'
import { init } from './graphs/beating.js'
import { args, num, sec, keys, status, clearLine, pausedTag, help } from './utils.js'
help({
description: 'hear beating between two nearby frequencies',
usage: ['', '[carrier] [difference] [duration]', 'freq=440 diff=3 dur=5s'],
options: [
['freq=<hz|note>', 'lower carrier frequency (default: 440)'],
['diff=<hz>', 'difference/beat frequency (default: 3)'],
['-d, --duration <time>', 'run time with optional s/m/h suffix (default: 30s)'],
],
controls: [['Space', 'pause/resume'], ['← / →', 'change difference by 0.5 Hz'], ['↑ / ↓', 'move carrier one semitone'], ['Q / Esc', 'quit']],
})
let { pos, $ } = args()
let nums = pos.filter(t => /^\d/.test(t) && !/[smh]$/.test(t) || /^[A-G][#b]?\d$/i.test(t))
let f = num(nums[0] || $('freq', 440))
let diff = +(nums[1] || $('diff', 3))
let dur = sec(pos.find(t => /\d[smh]$/.test(t)) || $('dur', '30'))
let ctx = new AudioContext()
await ctx.resume()
let demo = init(ctx, { frequency: f, difference: diff, duration: dur, gain: 0.3 })
let [osc1, osc2] = demo.sources
let retune = () => {
let t = ctx.currentTime
osc1.frequency.setTargetAtTime(f, t, 0.02)
osc2.frequency.setTargetAtTime(f + diff, t, 0.02)
}
let render = status()
let ui = setInterval(() => render(`${f.toFixed(2)}Hz + ${(f + diff).toFixed(2)}Hz · beat ${diff.toFixed(2)}Hz · space pause · ←→ beat · ↑↓ carrier · q quit${pausedTag(ctx)}`), 80)
keys({
left: () => { diff = Math.max(0.1, diff - 0.5); retune() },
right: () => { diff += 0.5; retune() },
up: () => { f *= 2 ** (1/12); retune() },
down: () => { f *= 2 ** (-1/12); retune() },
}, () => { clearInterval(ui); clearLine(); ctx.close() }, ctx)
console.log(`${f}Hz + ${f + diff}Hz → ${diff}Hz beating (${dur}s) ←→ beat · ↑↓ carrier · q quit`)
setTimeout(() => { clearInterval(ui); clearLine(); ctx.close(); process.exit(0) }, dur * 1000)