Add play-from-start-of-bar

Alt+Space plays from the downbeat of the bar the cursor is in. Auditioning a
cell says what a note is; playing from the bar line says whether it is in the
right place, which is the question that matters at 16ths and 32nds and cannot
be answered from a note in isolation.

REAPER can almost do this by chaining a go-to-measure action with play, but
that leaves the edit cursor at the bar line -- and the edit cursor is the grid
cursor, so you would lose your editing position every time you listened. Here
the cursor is moved just long enough to start playback and then restored with
seekplay=false so the restore cannot drag playback with it.

Works regardless of grid mode, being a listening aid rather than an edit.
This commit is contained in:
2026-08-14 19:57:55 +02:00
parent 51dff21e85
commit 8fd1b4755f
4 changed files with 68 additions and 2 deletions
+12
View File
@@ -0,0 +1,12 @@
--[[
MIDI Grid: play from the start of the bar the cursor is in.
Deliberately works whether or not grid mode is on -- it is a listening aid,
not an editing action, and it is just as useful while navigating with OSARA
normally. Stop with Space as usual.
]]
local dir = ({ reaper.get_action_context() })[2]:match("@?(.*[\\/])")
local G = dofile(dir .. "midigrid_lib.lua")
local bar = G.playFromBar()
G.say("Playing from bar " .. bar)
+17
View File
@@ -81,6 +81,7 @@ actions by hand in Actions → Show action list. That always works.
| `Alt+Shift+Up` / `Alt+Shift+Down` | Velocity up / down | — |
| `Alt+G` | **Toggle grid mode on/off** | — |
| `Alt+H` | Toggle hold mode | — |
| `Alt+Space` | Play from start of the current bar | works in both modes |
| `Alt+A` | Repeat current cell (speak + sound) | — |
| `Alt+S` | Set root and scale | — |
| `1``9` | Grid size — REAPER's own grid actions | same |
@@ -149,6 +150,22 @@ survive. They also need no mode: `L` behaves identically whether hold is on or
off, so there is no state to set up beforehand and none to remember to turn
off afterwards.
## Hearing it in context
`Alt+Space` plays from the **top of the bar the cursor is in**, and says which
bar. Stop with `Space` as usual.
Auditioning a cell tells you what a note *is*; playing from the downbeat tells
you whether it is in the right *place*. At 16ths or 32nds that difference is
the whole question, and it cannot be judged from a note in isolation.
Your position is preserved. The edit cursor doubles as the grid cursor, so it
is moved to the bar line only long enough to start playback and then put
straight back — you carry on editing exactly where you were.
This one works whether or not grid mode is on, since it is a listening aid
rather than an editing action.
## Shifting notes in time
If a whole clip landed a couple of cells off the beat, `Ctrl+Shift+Left` and
+4 -2
View File
@@ -59,7 +59,8 @@ $actions = @(
@('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_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')
)
# flags, keycode, section, id
@@ -86,7 +87,8 @@ $keys = @(
@(13, 32805, 32060, 'RSmidigrid_shl'),
@(13, 32807, 32060, 'RSmidigrid_shr'),
@(29, 32805, 32060, 'RSmidigrid_shlf'),
@(29, 32807, 32060, 'RSmidigrid_shrf')
@(29, 32807, 32060, 'RSmidigrid_shrf'),
@(17, 32, 32060, 'RSmidigrid_playbar')
)
$backup = "$kb.midigrid-backup"
+35
View File
@@ -352,6 +352,41 @@ function M.toggleCell(take, pitch)
return result, detail
end
------------------------------------------------------------------ playback
-- Start of the bar containing time t, plus the 1-based bar number.
function M.barStart(t)
local _, measures = reaper.TimeMap2_timeToBeats(0, t)
return reaper.TimeMap2_beatsToTime(0, 0, measures), measures + 1
end
--[[
Play from the top of the bar the cursor is in, without losing your place.
The edit cursor doubles as the grid cursor, so it is moved to the bar line
only long enough to start playback and then put straight back. Restoring it
passes seekplay=false so the restore cannot drag playback along with it.
Playing from the bar line rather than the cursor is what makes fine
subdivisions checkable: you hear the note in relation to the downbeat,
which is the thing you are actually trying to judge.
]]
function M.playFromBar()
local here = reaper.GetCursorPosition()
local barPos, barNum = M.barStart(here)
reaper.PreventUIRefresh(1)
reaper.SetEditCurPos(barPos, false, true)
-- GetPlayState bit 0 is "playing"; avoid bitwise ops for portability.
if (reaper.GetPlayState() % 2) == 0 then
reaper.CSurf_OnPlay()
end
reaper.SetEditCurPos(here, false, false)
reaper.PreventUIRefresh(-1)
return barNum
end
---------------------------------------------------------------- time shift
--[[