<# 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_octup', 32060, 'MIDI Grid: Pitch up one octave', 'MidiGrid_OctaveUp.lua'), @('RSmidigrid_octdn', 32060, 'MIDI Grid: Pitch down one octave', 'MidiGrid_OctaveDown.lua'), @('RSmidigrid_colup', 32060, 'MIDI Grid: Next note above in this cell', 'MidiGrid_ColumnUp.lua'), @('RSmidigrid_coldn', 32060, 'MIDI Grid: Next note below in this cell', 'MidiGrid_ColumnDown.lua'), @('RSmidigrid_rownext',32060, 'MIDI Grid: Next note at this pitch', 'MidiGrid_RowNext.lua'), @('RSmidigrid_rowprev',32060, 'MIDI Grid: Previous note at this pitch', 'MidiGrid_RowPrev.lua'), @('RSmidigrid_notenext',32060,'MIDI Grid: Next note anywhere', 'MidiGrid_NoteNext.lua'), @('RSmidigrid_noteprev',32060,'MIDI Grid: Previous note anywhere', 'MidiGrid_NotePrev.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, +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'), @(25, 32806, 32060, 'RSmidigrid_octup'), @(25, 32808, 32060, 'RSmidigrid_octdn'), @(17, 32806, 32060, 'RSmidigrid_colup'), @(17, 32808, 32060, 'RSmidigrid_coldn'), @(17, 32807, 32060, 'RSmidigrid_rownext'), @(17, 32805, 32060, 'RSmidigrid_rowprev'), @(25, 32807, 32060, 'RSmidigrid_notenext'), @(25, 32805, 32060, 'RSmidigrid_noteprev'), @(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 (-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."