cppMusicTools

April 28, 2026 · View on GitHub

A collection of C++ tools for MIDI and music development, built with the JUCE framework.

MidiTools

A namespace containing a Chord class, a Scale class, and utility functions for handling MIDI notes and chords.

The Chord Class

MidiTools::Chord represents a musical chord. It can be constructed from a chord-name string (e.g. "CM", "Am7", "F#M") and exposes its constituent semitones as degrees (indices 0–6). Raw MIDI note arrays are used internally for the "chord played as is" mode.

The Scale Class

MidiTools::Scale represents a diatonic or non-diatonic scale. Construct it with a root semitone (0–11) and a Scale::Type enum value. getNotes() returns the semitone set; fromScaleAndDegree() builds a Chord voiced from a given scale degree.


Arpeggiator

The Arpeggiator class drives a MIDI arpeggio engine. It takes a Chord, an octave, and a pattern string and generates time-stamped MIDI events.

How It Works

Call prepareToPlay(sampleRate) once, then call processBlock(numSamples, midiChannel) every audio block. The method returns a juce::MidiBuffer containing any note-on / note-off events that fall inside the block.

The arpeggiator maintains a position (pos) in the pattern string that advances one musical step per call to the internal getNext() function. All state (global octave, global velocity, scope stack, root-relative flag) persists between calls.


Pattern String Syntax

The pattern string is read character by character. A step is any single note command. Prefix commands are consumed before the note command of the same step and modify how that note (or subsequent notes) sounds.

Note Commands

CommandDescription
19Plays a specific degree of the chord/scale (1 = root, 2 = second, …).
+Advances to the next chord degree (wraps).
-Goes back to the previous chord degree (wraps).
?Plays a random valid degree from the chord.
=Repeats the last played degree.
_Sustains the previous note (no new note-on).
.Rest — no note is played.

Pitch Prefix Commands

CommandDescription
#Raises the next note by one semitone (local, one step only).
bLowers the next note by one semitone (local, one step only).

Velocity Prefix Commands

Velocity levels 1–8 map to MIDI velocities 16–127.

CommandDescription
vNLocal — sets velocity for the next note only (N = 1–8).
v+Local — increases velocity one level for the next note only.
v-Local — decreases velocity one level for the next note only.
VNGlobal — sets velocity for all subsequent notes until the next V command.
V+Global — increases velocity one level for all subsequent notes.
V-Global — decreases velocity one level for all subsequent notes.

Octave Prefix Commands

CommandDescription
oNLocal — sets octave to N (0–7) for the next note only.
o+Local — increases octave by one for the next note only.
o-Local — decreases octave by one for the next note only.
ONGlobal — sets octave to N for all subsequent notes.
O+Global — increases octave by one for all subsequent notes.
O-Global — decreases octave by one for all subsequent notes.

Block Commands

Block commands wrap one or more steps and alter the evaluation context for everything inside them.

Local Modifier Scope ( … )

When the parser encounters ( it pushes the current global octave and global velocity onto an internal scope stack. When it encounters ) it pops the stack, restoring both values. Any O+/O-/V+/V- (or absolute ON/VN) used inside the parentheses therefore have no lasting effect outside the block.

1 2 (O+ 1 2 3) 1 2

Steps 3–5 are played one octave higher; the global octave is restored for steps 6 and 7. Parentheses may be nested.

Root-Relative Notes " … " (single-note / scale mode only)

In scale chord-method mode the arpeggiator normally voices notes relative to the degree of the currently pressed MIDI note within the selected scale. Steps placed between double-quotes are instead voiced relative to degree 0 of the scale (the scale root), regardless of which key is held.

1 2 "1 2 3" 4

Steps 3–5 always follow the root of the scale; steps 1, 2, 4 follow the pressed note's degree as usual.

The root chord is supplied by the host via setRootChord(). In non-scale modes the "…" block has no effect.

Note: " is a block delimiter and is not a note command. Use = to repeat the last degree.


Public API (selected)

MethodDescription
setChord(chord)Sets the chord the arpeggiator voices against.
setRootChord(chord)Sets the chord used inside "…" root-relative blocks.
setPattern(string)Sets a new pattern string and resets position and scope state.
setTempo(bpm)Sets the tempo in BPM.
setSubdivision(index)Sets the note subdivision (0 = 1/4, 4 = 1/16, …).
setChordMethod(index)0 = notes played, 1 = chord as is, 2 = scale/single note.
setBaseOctaveFromNote(midi)Derives the base octave from an incoming MIDI note number.
numSteps()Returns the number of musical steps in the current pattern.
getPatternIndexForStep(n)Maps a step index to its character position in the pattern string.
getLastPlayedNote()Returns the last MIDI note number that was output (-1 if none).
makeEuclidianPattern(hits, steps, rot)Generates a Euclidean rhythm pattern string.
makeRandomPattern()Generates a random but balanced pattern string.
reset()Resets position, octave, velocity, and scope state.
turnOff()Generates a note-off for the last played note and resets state.