Files
midigrid/install.ps1
T
Talon c9e927ca3d Add diatonic chord entry and velocity control
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.
2026-08-14 18:18:12 +02:00

123 lines
5.4 KiB
PowerShell

<#
MIDI Grid installer.
Registers the scripts as Reaper actions and binds them, by editing
reaper-kb.ini directly. This is done rather than via "Import key map"
because Reaper's importer silently converts unresolved script bindings
into "No-op (no action)" entries -- see README, Installation notes.
REAPER MUST BE CLOSED. It rewrites reaper-kb.ini on exit and will
discard anything written while it is running.
Usage: powershell -ExecutionPolicy Bypass -File install.ps1
powershell -ExecutionPolicy Bypass -File install.ps1 -Uninstall
#>
[CmdletBinding()]
param(
[string] $ScriptDir,
[string] $ResourcePath = "$env:APPDATA\REAPER",
[switch] $Uninstall
)
$ErrorActionPreference = 'Stop'
# $PSScriptRoot is not reliably populated during parameter binding, so
# resolve the script's own directory here instead of in the param block.
if (-not $ScriptDir) {
$ScriptDir = if ($PSScriptRoot) { $PSScriptRoot }
else { Split-Path -Parent $MyInvocation.MyCommand.Definition }
}
if (-not $ScriptDir) { throw "Could not determine script directory; pass -ScriptDir." }
$kb = Join-Path $ResourcePath 'reaper-kb.ini'
if (-not (Test-Path $kb)) { throw "reaper-kb.ini not found at $kb" }
if (Get-Process reaper* -ErrorAction SilentlyContinue) {
throw "REAPER is running. Close it completely and run this again."
}
# 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')
)
# flags, keycode, section, id
# flags: 1 base, +4 Shift, +8 Ctrl, +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')
)
$backup = "$kb.midigrid-backup"
if (-not (Test-Path $backup)) {
Copy-Item $kb $backup
Write-Host "Backed up original to $backup"
}
$lines = [System.Collections.Generic.List[string]]::new()
$lines.AddRange([System.IO.File]::ReadAllLines($kb))
# Drop any previous MIDI Grid lines so re-running is idempotent.
for ($i = $lines.Count - 1; $i -ge 0; $i--) {
if ($lines[$i] -match '(?i)RSmidigrid') { $lines.RemoveAt($i) }
}
if ($Uninstall) {
[System.IO.File]::WriteAllLines($kb, $lines, (New-Object System.Text.UTF8Encoding($false)))
Write-Host "Removed MIDI Grid actions and bindings."
Write-Host "Note: keys it had taken over (arrows, Enter) are now unbound in the"
Write-Host "MIDI editor. Rebind them, or restore $backup."
return
}
foreach ($a in $actions) {
$path = Join-Path $ScriptDir $a[3]
if (-not (Test-Path $path)) { throw "Missing script: $path" }
$lines.Insert(0, ('SCR 4 {0} {1} "{2}" {3}' -f $a[1], $a[0], $a[2], $path))
}
# 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.
foreach ($k in $keys) {
$new = 'KEY {0} {1} _{3} {2}' -f $k[0], $k[1], $k[2], $k[3]
$pat = '^KEY {0} {1} \S+ {2}(\s|$)' -f $k[0], $k[1], $k[2]
$found = $false
for ($i = 0; $i -lt $lines.Count; $i++) {
if ($lines[$i] -match $pat) { $lines[$i] = $new; $found = $true; break }
}
if (-not $found) { $lines.Add($new) }
}
[System.IO.File]::WriteAllLines($kb, $lines, (New-Object System.Text.UTF8Encoding($false)))
Write-Host "Installed $($actions.Count) actions and $($keys.Count) key bindings."
Write-Host "Start REAPER, select a MIDI item, and press Alt+Shift+G."