trackswitch

Piano Roll

This page shows a multichannel MIDI transcription of a four-part chorale. Trackswitch displays the MIDI notes in a piano roll. This first player is channel unaware: it has no notion of individual channels, so every note is drawn in the same plain colour. A second player links each MIDI channel to an audio track, so its piano roll colors each channel and follows the audible track selection, and shows the same transcription as notes that move toward a piano keyboard.

Audio data: ChoraleWind, Drese, Jesu geh voran.

How it works

Piano roll views color the note events separately per channel by default — colorPerChannel defaults to true, so this needs no configuration even for a single combined recording:

{
  "type": "pianoRoll",
  "mediaID": "notes",
  "height": 260
}

Additionally, specific tracks can be assigned to each MIDI channel to show or hide MIDI note events depending on the audible track selection:

{
  "type": "pianoRoll",
  "mediaID": "notes",
  "height": 260,
  "channelToTrackIDMap": {
    "0": "soprano",
    "1": "alto",
    "2": "tenor",
    "3": "bass"
  }
}

Each value names a media entry of type audio. Two channels may name the same track, which is what a transcription of a two-handed piano part needs — the track’s row then shows both colours, split with a hard edge.

A channel channelToTrackIDMap leaves out is still coloured — it’s just always drawn, regardless of which tracks are audible. Setting colorPerChannel to false turns coloring off entirely, so every channel falls back to the plain accent, whether or not it’s paired with a track:

{
  "type": "pianoRoll",
  "mediaID": "notes",
  "colorPerChannel": false,
  "channelToTrackIDMap": { "0": "soprano", "1": "alto" }
}

Colours are handed out by ascending channel number — orange, red, green, blue, purple, brown, pink, gray, olive, cyan, then around again. The first colour is the accent of the player, so a roll with a single channel keeps the appearance of an unpaired one. Every colour is a theming token, so a view can override one on its own:

{
  "type": "pianoRoll",
  "mediaID": "notes",
  "channelToTrackIDMap": { "0": "soprano", "1": "alto" },
  "css": { "--ts-color-channel-2": "#8844cc" }
}

With a piano keyboard

The same transcription again, this time channel-aware and as a falling-notes display. Each channel is linked to its track through channelToTrackIDMap, so the roll follows the audible track selection. pianoKeyboard draws a keyboard column beside the pitch axis, pins the playhead to its edge and scrolls the roll past it, so the notes fly into the keys they sound on.

{
  "type": "pianoRoll",
  "mediaID": "notes",
  "height": 380,
  "pianoKeyboard": true,
  "noteRange": ["F2", "F5"],
  "defaultZoom": 8,
  "palette": "colorblind-dark",
  "channelToTrackIDMap": {
    "0": "soprano",
    "1": "alto",
    "2": "tenor",
    "3": "bass"
  }
}

The keyboard is a real keyboard on its far edge — seven white keys per octave, with the black keys stopping short of it — while its near edge carries one row per semitone, the same grid the roll draws on. That is what lets a note bar meet its own key. Every C is labelled in gray.

A sounding note lights its key in the colour of its channel. Two channels holding the same pitch split that key along its length, one box each, so a unison stays readable.

pianoKeyboard turns on a few things, one of which — defaultZoom — is also available on its own:

noteRange fixes the pitch axis instead of deriving it from the file. Each entry is a note name or a MIDI note number, so ["F2", "F5"] and [41, 77] are the same range. Left at its default of "automatic", the axis spans every note of the file with two semitones of padding.

Labeling channels

channelToLabelMap names each channel — by instrument, part, or anything else — keyed the same way as channelToTrackIDMap:

{
  "type": "pianoRoll",
  "mediaID": "notes",
  "noteTooltip": true,
  "legend": "top-right",
  "channelToLabelMap": {
    "0": "Soprano",
    "1": "Alto",
    "2": "Tenor",
    "3": "Bass"
  }
}

A labelled channel shows its name in place of the plain channel number in the noteTooltip readout. legend draws a small panel — colour swatch and label per channel — at the given position; "top-right" is the only one implemented so far, and "none" (the default) leaves it off. The tooltip and the legend read from the same map, and either can be turned on without the other. A channel channelToLabelMap leaves out falls back to its plain channel number in the tooltip, and is left out of the legend entirely.

The piano roll in the keyboard example above has both switched on — hover a note to see its instrument name, or read the legend in the corner.

Velocity and overlapping notes

Note events are drawn solid by default. Velocity is opt-in, through two switches that work independently:

{
  "type": "pianoRoll",
  "mediaID": "notes",
  "velocityBars": true,
  "velocityOpacity": true
}

velocityBars draws a small bar inside each note event, and velocityOpacity fades the note by its velocity. With both off — the default — every note is a solid block of its channel colour.

Solid notes cannot be told apart by transparency where they overlap, so a pitch held by several channels at once is drawn as a checkerboard: one row per channel, the colours rotating by one row from column to column. Two channels give the two-row pattern; a third adds a third row.

A second note-on for a pitch that is already sounding on the same channel is read as a re-trigger — the note that was running ends there, rather than the two being drawn on top of each other.