Timing
- Metronome / sync to beat vs. note duration
The next step is to look at rhythm/tempo. Or how to play our notes over time. If we think of music in terms of axes, pitch is on the y axis, with time stretching out ahead on the x axis. This gives us the building block of rhythm, which is a foundation of melody.
Timing models
Standard JavaScript timing
JS has setInterval
& setTimeout
.
To repeat a note we could do the following:
The problem with JavaScript's standard setInterval
and setTimeout
functions
is that they are not guaranteed to run exactly on schedule. Depending what else
is happening on the page, they may be delayed while the browser catches up. This
is fine for most use-cases, where millisecond delays in e.g. showing a
notification would be unnoticeable, but with music these lags can be very
obvious.
Web Audio API timing
Instead, the Web Audio API has the concept of scheduling. When you create an
AudioContext
, it begins counting from 0. We can check it's current time as
follows:
context.currentTime
uses seconds not millisecondsUnlike most timing-related features in JavaScript, the audio clock uses seconds, not milliseconds, with all scheduling happening relative to
currentTime
.
We can't control this internal clock, but we can schedule events relative to it:
AudioBufferSourceNode
s can only be played onceIt's worthing noting that in the above code we create three separate
sourceNode
s, each using the sameaudioBuffer
. Once started, anAudioBufferSourceNode
cannot be played again. The expensive part of working with samples is loading and decoding them, so the idea is to keep aroundAudioBuffer
s, while freely creating and discardingAudioBufferSourceNode
s. They are intended to be "fire and forget", and are automatically garbage collected when they finish playing.
Metronome
Most music-making apps have some form of metronome, something that emits a steady pulse given a BPM (beats per minute). Instruments and effects sync to this pulse, ensuring that everything plays back in time.
Version 1
We can create a basic metronome that emits a sound every beat using an
OscillatorNode
and taking advantage of its
onended
event. Passing in a callback, we can then schedule anything we want relative to
the beat.
For more details on how we derive the note length from the BPM, refer to the Music Primer chapter, which breaks this down with illustrations.
Version 2
Currently our metronome only enables us to schedule notes to occur exactly on the beat. To increase our options we need to increase the resolution of the events the metronome emits.
If we think of time as the x axis of a grid, resolution means how we subdivide that grid.
As we saw in the Music chapter, in traditional music notation we might call these divisions a "half note" or "quarter note", (or "minim" or "crotchet"), while in a pattern sequencer they are usually represented as fractions. Both refer to the same thing: how to subdivide a bar.
Common subdivisions are 1/2, 1/4, 1/8, 1/16, 1/32, 1/64 and 1/128 (plus some triplet divisions which we'll ignore for now).
If we presume a time signature of 4/4 (4 beats per bar), these resolutions break down as follows:
Rather than passing in a callback to be triggered on the beat, we can instead extend our metronome to emit events at each subdivision and listen for those.
Version 3
Async: Callbacks, Promises, Event Emitters, Observables
Sync | Async | |
---|---|---|
Single | Variable | Promise |
Collection | Array | Observable |
Observable (spec, RxJS)
Note length
If our metronome is ticking away at 60 bpm, we know that each beat lasts 1 second. A bar of 4 beats will therefore last 4 seconds. Knowing this, we just need to divide the bar length by the resolution to give us our note length.
We can encapsulate this knowledge into a resolution()
function that given a
bpm and resolution, returns the length of our note:
Our metronome()
function tells us the current bpm at each tick...
## Learning
TODO: metronome
is part of TODO package, see API docs.
While the musical results are less than inspiring, we now have a way to generate two key aspects of music: notes/pitch and rhythm/time, which combined can give us melody. With that in mind, we're ready to move on to a more interesting example.