Opening an item in the MIDI editor parks the edit cursor at the item start, and the open action then made that stick. Listening to a phrase and hitting Alt+Shift+G to write on top of it meant arrowing back to where you already were, one cell at a time, on every entry. The cursor now stays where it was and is only tidied onto the nearest grid line, so the first left/right press moves a whole cell rather than a remainder. The action reads the cursor before the open command runs, since the open command is what moves it. snapCursorToCell rounds in quarter notes, like everything else here, so the landing spot survives a tempo change, and clamps the cell index into the item: the floor is the cell containing the item start, which is where the action used to land, and the ceiling is the last cell that starts inside the item. Clamping the index rather than the time means a cursor parked before an off-grid item start rounds forward into the first whole cell instead of into the partial one, where a note would begin outside the item. The action also re-reads the take from the editor handle now, so the grid size and the spoken position come from the take actually being edited rather than from the selected item's active take. Toggling grid mode on with Alt+G is left alone: it does not move the cursor at all, and cell() floors, so an off-grid cursor still works. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
50 lines
1.6 KiB
Lua
50 lines
1.6 KiB
Lua
--[[
|
|
MIDI Grid: open the selected item in the MIDI editor and enter grid mode.
|
|
|
|
This is the counterpart to the normal "open in MIDI editor" action: it does
|
|
the same thing, then arms grid mode and announces the starting state, so a
|
|
single keystroke takes you from a selected item to writing notes.
|
|
]]
|
|
local dir = ({ reaper.get_action_context() })[2]:match("@?(.*[\\/])")
|
|
local G = dofile(dir .. "midigrid_lib.lua")
|
|
|
|
local item = reaper.GetSelectedMediaItem(0, 0)
|
|
if not item then G.say("No item selected") return end
|
|
|
|
local take = reaper.GetActiveTake(item)
|
|
if not take or not reaper.TakeIsMIDI(take) then G.say("Selected item is not MIDI") return end
|
|
|
|
-- Opening the editor parks the cursor at the item start; remember where the
|
|
-- user actually was so it can be handed back.
|
|
local cur = reaper.GetCursorPosition()
|
|
|
|
-- Item: Open in built-in MIDI editor (set default behaviour in preferences)
|
|
reaper.Main_OnCommand(40153, 0)
|
|
|
|
local hwnd = reaper.MIDIEditor_GetActive()
|
|
if not hwnd then G.say("Could not open MIDI editor") return end
|
|
|
|
take = reaper.MIDIEditor_GetTake(hwnd) or take
|
|
|
|
G.set("active", 1)
|
|
|
|
-- Preview must sound through this track's instrument, whatever else is armed.
|
|
G.routePreview(take)
|
|
|
|
-- Stay where you were listening, on the nearest cell boundary, rather than
|
|
-- jumping back to the top of the item.
|
|
G.snapCursorToCell(take, cur)
|
|
|
|
local p = G.getPitch(hwnd)
|
|
if not G.inScale(p) then
|
|
local up = G.scaleStep(p, 1)
|
|
if up then p = up ; G.setPitch(hwnd, p) end
|
|
end
|
|
|
|
G.say(("Grid mode on, %s, grid %s, %s, %s, %s"):format(
|
|
G.scaleName(),
|
|
G.gridLabel(G.gridQN(take)),
|
|
G.isHold() and "hold on" or "hold off",
|
|
G.noteName(p),
|
|
G.cellPosText(take)))
|