37 lines
1.0 KiB
JavaScript
37 lines
1.0 KiB
JavaScript
const SEMITONE = 2 ** (1 / 12)
|
|
|
|
function incrementSemitones(root) {
|
|
return (n) => root * SEMITONE ** n
|
|
}
|
|
|
|
function playNotes(root, notes, { seconds, wave } = {}) {
|
|
const ctx = new AudioContext()
|
|
const oscillator = ctx.createOscillator()
|
|
const freqs = notes.map(incrementSemitones(root))
|
|
|
|
oscillator.connect(ctx.createGain().connect(ctx.destination))
|
|
oscillator.type = wave ?? 'sine'
|
|
|
|
const duration = seconds ?? 0.3
|
|
|
|
for (const [i, freq] of freqs.entries()) {
|
|
oscillator.frequency.setValueAtTime(freq, ctx.currentTime + i * duration)
|
|
}
|
|
|
|
oscillator.start()
|
|
oscillator.stop(ctx.currentTime + freqs.length * duration)
|
|
|
|
return new Promise((res) => oscillator.addEventListener('ended', res))
|
|
}
|
|
|
|
const A = 440
|
|
const C = incrementSemitones(A)(-9) // ~= 261.63
|
|
|
|
const major = [0, 2, 4, 5, 7, 9, 11, 12]
|
|
const minor = [0, 2, 3, 5, 7, 8, 10, 12]
|
|
|
|
void (async function main() {
|
|
await playNotes(C, major, { seconds: 0.3, wave: 'sine' })
|
|
await playNotes(A, minor, { seconds: 0.1, wave: 'triangle' })
|
|
})()
|