Chords are built by stacking scale thirds rather than from a quality table, so the chord follows the degree automatically -- in C major, Alt+3 on C gives C major, on D gives D minor, on B gives B diminished. Toggling delegates to toggleCell, so chord tones inherit hold-mode joining, splitting and trimming unchanged. Velocity keys act on the note under the cursor if there is one, and on the default for new notes otherwise, announcing which. That avoids a mode switch between setting a level to draw at and shaping dynamics after the fact. Also fixes install.ps1 failing to resolve its own directory: $PSScriptRoot is not reliably populated during parameter binding, so it is resolved in the body. Tests cover chord quality per scale degree and range clamping.
MIDI Grid — accessible quick MIDI entry for REAPER
A grid / step-sequencer entry mode for REAPER's MIDI editor, built for keyboard-and-speech use with OSARA.
Left and right walk the timeline one grid cell at a time. Up and down walk the pitch axis by scale degree. Enter toggles a note in the current cell. Everything you land on is spoken and sounded, so you can hear what is already there as you move.
It is not a replacement for the MIDI editor. It is a fast way to block out a clip; switch grid mode off and OSARA's normal editing is right there, unchanged, on the same cursor.
Requirements
- REAPER
- OSARA — for speech output
- SWS — for the note-naming octave preference
No other extensions. No ReaPack, no js_ReaScriptAPI, no ReaImGui.
Install
Clone or download this repository anywhere you like, then:
-
Close REAPER completely. It rewrites
reaper-kb.iniwhen it exits and will throw away anything written while it is running. -
Run:
powershell -ExecutionPolicy Bypass -File install.ps1 -
Start REAPER, select a MIDI item, press
Alt+Shift+G.
You should hear something like "Grid mode on, C major, grid 1/16, hold off, C5, 1.1.00".
To remove it again: powershell -ExecutionPolicy Bypass -File install.ps1 -Uninstall
The installer backs up reaper-kb.ini to reaper-kb.ini.midigrid-backup
before its first change, and is safe to re-run.
Why not "Import key map"?
MidiGrid.ReaperKeyMap is included for reference, but importing it does not
reliably work, and the way it fails is silent and confusing: REAPER accepts
the file, registers the script actions, and then converts every key binding it
cannot resolve into a No-op (no action) entry. The keys appear bound in the
action list but do nothing.
The cause is that a KEY line must reference the named command — the SCR
id with a leading underscore (_RSmidigrid_open), not the bare id
(RSmidigrid_open). install.ps1 writes the underscored form directly.
If you would rather not run a script, you can bind the ten MIDI Grid:
actions by hand in Actions → Show action list. That always works.
Keys
Main section
| Key | Action |
|---|---|
Alt+Shift+G |
Open selected item in grid mode |
MIDI editor section
| Key | Grid mode ON | Grid mode OFF (passes through to) |
|---|---|---|
Left / Right |
Cursor back / forward one grid cell | OSARA previous / next chord |
Up / Down |
Pitch up / down one scale degree | OSARA higher / lower note in chord |
Enter |
Toggle note at cursor cell | Edit: Event properties |
Alt+3 |
Toggle diatonic triad on cursor pitch | — |
Alt+7 |
Toggle diatonic seventh chord | — |
Alt+Shift+Up / Alt+Shift+Down |
Velocity up / down | — |
Alt+G |
Toggle grid mode on/off | — |
Alt+H |
Toggle hold mode | — |
Alt+A |
Repeat current cell (speak + sound) | — |
Alt+S |
Set root and scale | — |
1–9 |
Grid size — REAPER's own grid actions | same |
Alt+Shift+G opens grid mode; it does not close it. Use Alt+G inside
the editor to toggle back off.
How it integrates
- Grid size is REAPER's grid. Read via
MIDI_GetGrid(), so whatever you already have bound to the MIDI editor's grid actions (by default1= 1/1,2= 1/2,4= 1/4,6= 1/16,8= 1/8,3= 1/32) drives this too. Nothing new to learn. - The cursor is REAPER's cursor. Time is the native edit cursor; pitch is
the MIDI editor's
active_note_row. No private cursor state exists, so grid mode and OSARA cannot drift out of sync — you can switch between them mid-phrase. - Turning grid mode off restores everything. Each bound key forwards to what it did before, so nothing is permanently taken away.
What gets spoken
- Left / right — cell position and everything sounding in it:
"3.2.00, C5, E5", and those notes are sounded together. - Up / down — the note name, plus
"on"if this cell already holds that pitch:"E5, on". - Enter —
"E5 on, added"or"E5 off, removed".
Hold mode
With hold off, toggling adjacent cells at the same pitch gives separate repeated notes.
With hold on, a new note touching an existing note of the same pitch joins it into one sustained note. Toggling a cell off in the middle of a sustained run splits it in two; at either end, it shortens it. So you can draw a long note by arrowing right and pressing Enter across several cells.
Chords
Alt+3 stamps a diatonic triad on the cursor pitch; Alt+7 a seventh
chord. Both are built by stacking scale thirds, so the quality follows the
degree automatically — in C major, Alt+3 on C gives C major, on D gives D
minor, on B gives B diminished. There is no chord-quality table to configure
and no way to land on a chord outside the key.
If every tone of the chord is already in the cell, the keystroke removes them all; otherwise it fills in whatever is missing. Chord tones obey hold mode, splitting and trimming exactly like single notes.
You can also build chords by hand at any time — arrow to a pitch, press Enter,
arrow to another, press Enter. The cursor never moves horizontally when you
toggle, so stacking notes in one cell has always worked. Alt+3 and Alt+7
are just the one-keystroke version.
In the chromatic scale, "stacking thirds" degenerates into stacked whole tones. Predictable, but not musically useful — pick a real scale first.
Velocity
Alt+Shift+Up and Alt+Shift+Down change velocity in steps of 8.
The key does one of two things depending on where you are, and says which:
- On a note — changes that note's velocity: "E5 velocity 104", and replays it at the new level so you can hear the change.
- On an empty cell — changes the default for newly inserted notes: "Default velocity 104".
So you can set a level and then draw at it, or draw first and shape the dynamics afterwards, without a mode switch either way.
Set velstep in ExtState if steps of 8 are too coarse or too fine.
Scales
Alt+S prompts for a root (C, F#, Bb…) and a scale number. Up and down
then move by scale degree, which is what makes entry fast — you cannot land on
a wrong note by accident. Pick chromatic for plain semitone movement.
If the cursor sits on a note outside the current scale, up/down move to the nearest scale tone in that direction rather than getting stuck.
Architecture notes
The action scripts never call reaper.defer. This matters. If a script is
still alive when you press its key again, REAPER interrupts you with a "script
is already running — terminate or launch new instance?" prompt, which makes
fast key repeat impossible and, if you answer "terminate" and tick remember,
permanently costs you every second keypress.
So preview note timing lives in MidiGrid_Daemon.lua, a single background loop
that auto-starts on first use and relaunches itself if it ever dies. Action
scripts post a request to transient ExtState and exit within a millisecond. A
new request cuts the previous note, so fast arrowing retriggers cleanly. The
daemon releases everything on atexit, so notes cannot hang.
Preview requests use non-persisted ExtState — persisting them would write
to reaper.ini on every keypress.
Tunables
In ExtState section midigrid:
| Key | Default | Meaning |
|---|---|---|
vel |
96 | Velocity of inserted notes |
velstep |
8 | Velocity change per keypress |
chan |
0 | MIDI channel (0-based) |
previewdur |
0.4 | Preview note length, seconds |
Tests
test_lib.lua covers the pure logic — scale stepping, note naming, grid
labels — against a stubbed reaper table, so it runs outside REAPER:
lua test_lib.lua
Everything touching MIDI data or the REAPER API is only exercised in REAPER itself.
Limitations
- Preview uses
StuffMIDIMessage, which plays through the track's instrument via the virtual-keyboard input, so the track must be armed / monitored — exactly as with REAPER's own MIDI preview. - The grid aligns to the project timeline (bar lines), not the item start. For an item starting off-grid, the first cell will be partial.
- Note octave naming follows the
midioctoffspreference; with the default, note 60 reads as C5.