/**
* @param {(AudioContext|AudioNode)} target output destination for audio
* @param {number} [tempo=120] Tempo of metronome in bpm
*/
class Metronome {
constructor(target, tempo = 120) {
this.context = target.context || target
this.target = target.destination || target
this.period = 60000 / tempo
this.active = false
this.buffer = this.context.createBuffer(1, 1024, this.context.sampleRate)
this.buffer.getChannelData(0).forEach((_, i, samples) => {
samples[i] = (1 - (i + 1) / samples.length) * (Math.random() * 2 - 1)
})
}
get tempo () {
return this.period * 60000
}
set tempo (bpm) {
this.period = 60000 / bpm
}
/** Makes a ticking sound
* @param {number} [playbackRate=1] Factor to scale the playback rate by.
*/
tick (playbackRate = 1) {
const tick = this.context.createBufferSource()
tick.buffer = this.buffer
tick.playbackRate.value = playbackRate
tick.connect(this.target)
tick.start()
}
/** Starts the metronome. */
start () {
this.active = true
this.keepTicking()
}
/** Makes ticking noises while the metronome is active
* @private
*/
keepTicking () {
if (this.active) {
this.tick()
window.setTimeout(() => this.keepTicking(), this.period)
}
}
/** Stops the metronome */
stop () {
this.active = false
}
}