Add a macOS/Linux installer, and manual install steps
install.ps1 could never have worked outside Windows: it reads %APPDATA% for the resource path, guards on Get-Process, and needs PowerShell installed. install.sh mirrors it exactly -- same actions, same key table, same idempotent rewrite and backup -- differing only in resource-path discovery and --real-control, which exists because modifier bit 8 means Ctrl on Windows but Command on macOS. Also document installing by hand from the action list, for anyone who would rather not run either script. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,8 +14,10 @@ when behaviour changes.
|
||||
|
||||
```
|
||||
lua test_lib.lua # run the tests
|
||||
powershell -ExecutionPolicy Bypass -File install.ps1 # install (REAPER must be CLOSED)
|
||||
powershell -ExecutionPolicy Bypass -File install.ps1 # install on Windows (REAPER must be CLOSED)
|
||||
powershell -ExecutionPolicy Bypass -File install.ps1 -Uninstall
|
||||
./install.sh # install on macOS/Linux
|
||||
./install.sh --uninstall
|
||||
```
|
||||
|
||||
There is no build, no lint, no package manager, no test framework. `test_lib.lua`
|
||||
@@ -67,11 +69,17 @@ and friends), not raw ticks, so edits stay correct across tempo changes.
|
||||
|
||||
## Installation model
|
||||
|
||||
`install.ps1` edits `reaper-kb.ini` directly and is idempotent (it strips all
|
||||
`RSmidigrid` lines first, and backs up to `reaper-kb.ini.midigrid-backup`).
|
||||
REAPER rewrites that file on exit, so it must be closed or changes are lost.
|
||||
`install.ps1` (Windows) and `install.sh` (macOS/Linux) edit `reaper-kb.ini`
|
||||
directly and are idempotent (they strip all `RSmidigrid` lines first, and back
|
||||
up to `reaper-kb.ini.midigrid-backup`). REAPER rewrites that file on exit, so
|
||||
it must be closed or changes are lost. The two scripts are deliberate mirrors:
|
||||
same action list, same key table, same output. **Any change to one must be made
|
||||
to the other.** They differ only in resource-path discovery
|
||||
(`%APPDATA%\REAPER` vs `~/Library/Application Support/REAPER`) and in
|
||||
`install.sh --real-control`, which exists because modifier bit 8 means Ctrl on
|
||||
Windows but Command on macOS (physical Control is bit 32).
|
||||
|
||||
Adding a new action means adding a row to **both** tables in `install.ps1`: the
|
||||
Adding a new action means adding a row to **both** tables in each installer: the
|
||||
`$actions` table (`SCR 4 <section> <id> "<description>" <path>`) and, if it needs
|
||||
a key, `$keys` (flags: 1 base, +4 Shift, +8 Ctrl, +16 Alt; section 0 = Main,
|
||||
32060 = MIDI Editor). `KEY` lines must reference the named command — the SCR id
|
||||
|
||||
@@ -26,21 +26,95 @@ Clone or download this repository anywhere you like, then:
|
||||
|
||||
1. **Close REAPER completely.** It rewrites `reaper-kb.ini` when it exits and
|
||||
will throw away anything written while it is running.
|
||||
2. Run:
|
||||
2. Run the installer for your platform:
|
||||
|
||||
**Windows**
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File install.ps1
|
||||
```
|
||||
|
||||
3. Start REAPER, select a MIDI item, press **`Alt+Shift+G`**.
|
||||
**macOS / Linux**
|
||||
|
||||
```bash
|
||||
chmod +x install.sh # first time only
|
||||
./install.sh
|
||||
```
|
||||
|
||||
3. Start REAPER, select a MIDI item, press **`Alt+Shift+G`** (`Option+Shift+G`
|
||||
on a Mac).
|
||||
|
||||
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`
|
||||
To remove it again:
|
||||
|
||||
The installer backs up `reaper-kb.ini` to `reaper-kb.ini.midigrid-backup`
|
||||
before its first change, and is safe to re-run.
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File install.ps1 -Uninstall # Windows
|
||||
```
|
||||
```bash
|
||||
./install.sh --uninstall # macOS / Linux
|
||||
```
|
||||
|
||||
Both installers back up `reaper-kb.ini` to `reaper-kb.ini.midigrid-backup`
|
||||
before their first change, and are safe to re-run.
|
||||
|
||||
### macOS notes
|
||||
|
||||
`install.sh` does exactly what `install.ps1` does — same actions, same
|
||||
bindings — but finds the resource folder at
|
||||
`~/Library/Application Support/REAPER` (falling back to `~/.config/REAPER` on
|
||||
Linux). If yours is elsewhere, find it via **Options → Show REAPER resource
|
||||
path** and pass it:
|
||||
|
||||
```bash
|
||||
./install.sh --resource-path "/path/to/REAPER"
|
||||
```
|
||||
|
||||
**Modifiers.** In `reaper-kb.ini` the modifier bit that means Ctrl on Windows
|
||||
means **Command** on macOS; the physical Control key is a different bit. So by
|
||||
default every `Ctrl+…` binding in the [key table](#keys) is `Command+…` on a
|
||||
Mac, which is what Mac users expect. To bind the physical Control key instead:
|
||||
|
||||
```bash
|
||||
./install.sh --real-control
|
||||
```
|
||||
|
||||
`Alt` in the key table is the `Option` key on a Mac either way.
|
||||
|
||||
### Manual installation
|
||||
|
||||
If you would rather not run a script — or you want a different key layout —
|
||||
you can do the whole thing from REAPER's UI. Nothing about MIDI Grid depends
|
||||
on the installer; it only writes action registrations and key bindings.
|
||||
|
||||
1. **Register the scripts.** In REAPER, open **Actions → Show action list**,
|
||||
set the section selector to **Main**, and choose **New action → Load
|
||||
ReaScript…**. Select these two files from wherever you put the repository:
|
||||
|
||||
- `MidiGrid_OpenInGrid.lua`
|
||||
- `MidiGrid_Daemon.lua`
|
||||
|
||||
Then switch the section selector to **MIDI Editor** and load the remaining
|
||||
`MidiGrid_*.lua` files the same way (everything except `midigrid_lib.lua`
|
||||
and `test_lib.lua`, which are not actions). The section matters: an action
|
||||
loaded into the wrong section will not be offered for the keys you want.
|
||||
|
||||
2. **Bind the keys.** Still in the action list, select an action, click
|
||||
**Add…** under Shortcuts, and press the key combination. Use the
|
||||
[key table](#keys) below for the intended layout; you can of course choose
|
||||
your own. Only `Alt+Shift+G` needs to be in the Main section — everything
|
||||
else belongs in MIDI Editor.
|
||||
|
||||
3. **Say yes when REAPER warns about a key that is already bound.** Grid mode
|
||||
deliberately takes over `Left`, `Right`, `Up`, `Down` and `Enter` in the
|
||||
MIDI editor; each of those forwards to its previous behaviour when grid
|
||||
mode is off, so nothing is lost.
|
||||
|
||||
You do not need to bind everything. The minimum useful set is
|
||||
`Alt+Shift+G` (open), `Alt+G` (toggle), the four arrows, and `Enter`.
|
||||
`MidiGrid_Daemon.lua` needs to be **registered** as an action but never needs
|
||||
a key — it auto-starts.
|
||||
|
||||
### Why not "Import key map"?
|
||||
|
||||
@@ -52,10 +126,10 @@ 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.
|
||||
(`RSmidigrid_open`). Both installers write 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.
|
||||
If you would rather not run a script, see [Manual installation](#manual-installation)
|
||||
above. Binding by hand in the action list always works.
|
||||
|
||||
## Keys
|
||||
|
||||
|
||||
Executable
+191
@@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# MIDI Grid installer for macOS (and Linux REAPER).
|
||||
#
|
||||
# The macOS counterpart of install.ps1, and deliberately a line-for-line
|
||||
# mirror of it: same actions, same key table, same idempotent rewrite of
|
||||
# reaper-kb.ini. If you add an action, add it to *both* files.
|
||||
#
|
||||
# REAPER MUST BE CLOSED. It rewrites reaper-kb.ini on exit and will
|
||||
# discard anything written while it is running.
|
||||
#
|
||||
# Usage: ./install.sh
|
||||
# ./install.sh --uninstall
|
||||
# ./install.sh --resource-path ~/Library/Application\ Support/REAPER
|
||||
# ./install.sh --real-control # see "Modifiers on macOS" below
|
||||
#
|
||||
# Modifiers on macOS
|
||||
# ------------------
|
||||
# In reaper-kb.ini the modifier bit 8 means Ctrl on Windows but *Command*
|
||||
# on macOS; the physical Control key is bit 32. By default this script
|
||||
# keeps bit 8, so every "Ctrl+..." binding in the README is Command+... on
|
||||
# a Mac, which is what Mac users expect. Pass --real-control to bind the
|
||||
# physical Control key instead.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||
resource_path=""
|
||||
uninstall=0
|
||||
real_control=0
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--uninstall) uninstall=1 ;;
|
||||
--real-control) real_control=1 ;;
|
||||
--script-dir) script_dir="$2"; shift ;;
|
||||
--resource-path) resource_path="$2"; shift ;;
|
||||
-h|--help) sed -n '2,25p' "$0"; exit 0 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "$resource_path" ]; then
|
||||
if [ -d "$HOME/Library/Application Support/REAPER" ]; then
|
||||
resource_path="$HOME/Library/Application Support/REAPER"
|
||||
elif [ -d "$HOME/.config/REAPER" ]; then
|
||||
resource_path="$HOME/.config/REAPER"
|
||||
else
|
||||
echo "Could not find your REAPER resource folder." >&2
|
||||
echo "Find it via Options > Show REAPER resource path, then pass" >&2
|
||||
echo " --resource-path '/path/to/REAPER'" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
kb="$resource_path/reaper-kb.ini"
|
||||
[ -f "$kb" ] || { echo "reaper-kb.ini not found at $kb" >&2; exit 1; }
|
||||
|
||||
if pgrep -x REAPER >/dev/null 2>&1 || pgrep -x reaper >/dev/null 2>&1; then
|
||||
echo "REAPER is running. Close it completely and run this again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# id | section | description | script filename
|
||||
actions='
|
||||
RSmidigrid_open|0|MIDI Grid: Open selected item in grid mode|MidiGrid_OpenInGrid.lua
|
||||
RSmidigrid_daemon|0|MIDI Grid: Preview daemon (auto-started)|MidiGrid_Daemon.lua
|
||||
RSmidigrid_mode|32060|MIDI Grid: Toggle grid mode|MidiGrid_ToggleMode.lua
|
||||
RSmidigrid_left|32060|MIDI Grid: Cursor left one cell|MidiGrid_Left.lua
|
||||
RSmidigrid_right|32060|MIDI Grid: Cursor right one cell|MidiGrid_Right.lua
|
||||
RSmidigrid_up|32060|MIDI Grid: Pitch up one scale degree|MidiGrid_Up.lua
|
||||
RSmidigrid_down|32060|MIDI Grid: Pitch down one scale degree|MidiGrid_Down.lua
|
||||
RSmidigrid_cell|32060|MIDI Grid: Toggle note at cursor cell|MidiGrid_ToggleCell.lua
|
||||
RSmidigrid_hold|32060|MIDI Grid: Toggle hold mode|MidiGrid_ToggleHold.lua
|
||||
RSmidigrid_audit|32060|MIDI Grid: Repeat current cell|MidiGrid_AuditionCell.lua
|
||||
RSmidigrid_scale|32060|MIDI Grid: Set root and scale|MidiGrid_SetScale.lua
|
||||
RSmidigrid_triad|32060|MIDI Grid: Toggle diatonic triad|MidiGrid_ChordTriad.lua
|
||||
RSmidigrid_seventh|32060|MIDI Grid: Toggle diatonic seventh chord|MidiGrid_ChordSeventh.lua
|
||||
RSmidigrid_velup|32060|MIDI Grid: Velocity up|MidiGrid_VelUp.lua
|
||||
RSmidigrid_veldn|32060|MIDI Grid: Velocity down|MidiGrid_VelDown.lua
|
||||
RSmidigrid_longer|32060|MIDI Grid: Lengthen note one cell|MidiGrid_Lengthen.lua
|
||||
RSmidigrid_shorter|32060|MIDI Grid: Shorten note one cell|MidiGrid_Shorten.lua
|
||||
RSmidigrid_join|32060|MIDI Grid: Join run of repeated notes|MidiGrid_Join.lua
|
||||
RSmidigrid_shl|32060|MIDI Grid: Shift all notes one cell left|MidiGrid_ShiftLeft.lua
|
||||
RSmidigrid_shr|32060|MIDI Grid: Shift all notes one cell right|MidiGrid_ShiftRight.lua
|
||||
RSmidigrid_shlf|32060|MIDI Grid: Shift all notes left (force)|MidiGrid_ShiftLeftForce.lua
|
||||
RSmidigrid_shrf|32060|MIDI Grid: Shift all notes right (force)|MidiGrid_ShiftRightForce.lua
|
||||
RSmidigrid_playbar|32060|MIDI Grid: Play from start of current bar|MidiGrid_PlayFromBar.lua
|
||||
RSmidigrid_playone|32060|MIDI Grid: Play current bar only|MidiGrid_PlayBarOnly.lua
|
||||
RSmidigrid_loopbar|32060|MIDI Grid: Loop current bar|MidiGrid_LoopBar.lua
|
||||
'
|
||||
|
||||
# flags | keycode | section | id
|
||||
# flags: 1 base, +4 Shift, +8 Ctrl (Command on macOS), +16 Alt
|
||||
# section: 0 = Main, 32060 = MIDI Editor
|
||||
keys='
|
||||
21|71|0|RSmidigrid_open
|
||||
17|71|32060|RSmidigrid_mode
|
||||
1|32805|32060|RSmidigrid_left
|
||||
1|32807|32060|RSmidigrid_right
|
||||
1|32806|32060|RSmidigrid_up
|
||||
1|32808|32060|RSmidigrid_down
|
||||
1|13|32060|RSmidigrid_cell
|
||||
17|72|32060|RSmidigrid_hold
|
||||
17|65|32060|RSmidigrid_audit
|
||||
17|83|32060|RSmidigrid_scale
|
||||
17|51|32060|RSmidigrid_triad
|
||||
17|55|32060|RSmidigrid_seventh
|
||||
21|32806|32060|RSmidigrid_velup
|
||||
21|32808|32060|RSmidigrid_veldn
|
||||
1|76|32060|RSmidigrid_longer
|
||||
5|76|32060|RSmidigrid_shorter
|
||||
17|74|32060|RSmidigrid_join
|
||||
13|32805|32060|RSmidigrid_shl
|
||||
13|32807|32060|RSmidigrid_shr
|
||||
29|32805|32060|RSmidigrid_shlf
|
||||
29|32807|32060|RSmidigrid_shrf
|
||||
17|32|32060|RSmidigrid_playbar
|
||||
25|32|32060|RSmidigrid_playone
|
||||
29|32|32060|RSmidigrid_loopbar
|
||||
'
|
||||
|
||||
backup="$kb.midigrid-backup"
|
||||
if [ ! -f "$backup" ]; then
|
||||
cp "$kb" "$backup"
|
||||
echo "Backed up original to $backup"
|
||||
fi
|
||||
|
||||
tmp=$(mktemp)
|
||||
trap 'rm -f "$tmp" "$tmp.2"' EXIT
|
||||
|
||||
# Drop any previous MIDI Grid lines so re-running is idempotent.
|
||||
grep -v -i 'RSmidigrid' "$kb" > "$tmp" || true
|
||||
|
||||
if [ "$uninstall" -eq 1 ]; then
|
||||
cat "$tmp" > "$kb"
|
||||
echo "Removed MIDI Grid actions and bindings."
|
||||
echo "Note: keys it had taken over (arrows, Enter) are now unbound in the"
|
||||
echo "MIDI editor. Rebind them, or restore $backup."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# SCR lines go at the top, in table order.
|
||||
: > "$tmp.2"
|
||||
while IFS='|' read -r id section desc file; do
|
||||
[ -n "${id:-}" ] || continue
|
||||
path="$script_dir/$file"
|
||||
[ -f "$path" ] || { echo "Missing script: $path" >&2; exit 1; }
|
||||
printf 'SCR 4 %s %s "%s" %s\n' "$section" "$id" "$desc" "$path" >> "$tmp.2"
|
||||
done <<EOF
|
||||
$actions
|
||||
EOF
|
||||
cat "$tmp" >> "$tmp.2"
|
||||
mv "$tmp.2" "$tmp"
|
||||
|
||||
# KEY lines reference the *named command*, which is the SCR id with a
|
||||
# leading underscore. Without it REAPER cannot resolve the binding and the
|
||||
# key silently does nothing.
|
||||
n_keys=0
|
||||
while IFS='|' read -r flags code section id; do
|
||||
[ -n "${flags:-}" ] || continue
|
||||
if [ "$real_control" -eq 1 ] && [ $(( flags & 8 )) -ne 0 ]; then
|
||||
flags=$(( flags - 8 + 32 ))
|
||||
fi
|
||||
# Replace an existing binding for the same key+section, else append.
|
||||
awk -v flags="$flags" -v code="$code" -v section="$section" -v id="$id" '
|
||||
BEGIN { pat = "^KEY " flags " " code " [^ ]+ " section "([ \t]|$)"
|
||||
new = "KEY " flags " " code " _" id " " section
|
||||
done = 0 }
|
||||
!done && $0 ~ pat { print new; done = 1; next }
|
||||
{ print }
|
||||
END { if (!done) print new }
|
||||
' "$tmp" > "$tmp.2"
|
||||
mv "$tmp.2" "$tmp"
|
||||
n_keys=$(( n_keys + 1 ))
|
||||
done <<EOF
|
||||
$keys
|
||||
EOF
|
||||
|
||||
cat "$tmp" > "$kb"
|
||||
|
||||
n_actions=$(printf '%s' "$actions" | grep -c 'RSmidigrid')
|
||||
echo "Installed $n_actions actions and $n_keys key bindings."
|
||||
if [ "$real_control" -eq 1 ]; then
|
||||
echo "Ctrl bindings use the physical Control key."
|
||||
else
|
||||
echo "On macOS the README's Ctrl+... bindings are Command+... here"
|
||||
echo "(re-run with --real-control to use the Control key instead)."
|
||||
fi
|
||||
echo "Start REAPER, select a MIDI item, and press Alt+Shift+G (Option+Shift+G)."
|
||||
Reference in New Issue
Block a user