Note events in the MOD player
This is part 9 of the JavaScript MOD Player series. If you haven't read the first part, here it is: Generating sound in modern Web Audio API
In the previous articles in this series, I have explored how to load and play back Amiga ProTracker MOD music in the browser, how to use timing events (watch and watchRows) to synchronize demo effects with music, and how to expose playback controls through the ModPlayer API.
Today, I'm introducing a new feature that was requested by Antoine Santo of CODEF fame: the ability for consumers of the playback module to listen to individual note events as they’re played by the player, for example to drive a sequencer visualization, or note-triggered demo effects.
What’s New
The watchNotes method lets you subscribe to each note as it begins, with metadata including:
channel: 1..4 (the four Amiga audio channels)sample: 1..32 (which instrument/sample is being played)volume: 1..64 (the sound level)note: –63..45 (note value, where0representsC-1)
With this you can react *in real time* to every note event in the song, without having to decode rows yourself or poll for state.
Here’s the updated ModPlayer class signature:
/// Subscribes to all individual notes starting
/// The callback for watchNotes must have the following signature:
/// callbackFunc( { channel: 1..4, sample: 1..32, volume: 1..64, note: -63..45 } )
watchNotes(callback) { }When a note starts, the callback is invoked with a plain object describing the note event.
Why This Matters
Previously, you could use:
watch(position, row, callback)to watch for particular row eventswatchRows(callback)to watch for every row played
...but these only fire at the *row level* in the pattern data, and they don’t tell you *which notes* are being played on each channel, nor do they expose sample, volume, or relative pitch.
With watchNotes, you now get fine-grained musical events, allowing a whole new class of musical interactivity in the browser, to drive animated visualizers that respond to melody and rhythm.
---
Example Usage
Here's a minimal example:
player.watchNotes(({ channel, sample, volume, note }) => {
console.log(`Channel ${channel}: note=${note}, sample=${sample}, vol=${volume}`);
animateVisualizer(channel, volume);
});This invokes animateVisualizer whenever a note starts, passing musical data to your animation logic.
🚀 Let me know...
If you build something with watchNotes, get in touch. I’d love to showcase your work!
You can try this solution at atornblad.github.io/js-mod-player. The latest version of the code is always available in the GitHub repository.
Articles in this series:
- Generating sound in modern Web Audio API
- Loading MOD files in the browser
- Playing a full song, almost
- Implementing looping and the first effects
- Adding pitch-related effects
- Syncopation and a human touch
- Using music as a timing source for demos
- Making the MOD player available
- Note events in the MOD player (this part)