Halve the bar-stop lead, and add exact bar looping
60ms was too blunt: at 180 BPM a 32nd note is 42ms, so the lead swallowed the final 32nd of the bar, and it swallowed a whole 32nd from 125 BPM upward. Testing against GetPlayPosition2 rather than GetPlayPosition removes the render-ahead from the equation -- it reports the block about to be rendered rather than what is currently audible -- so the lead only has to cover the daemon's polling interval, not both delays. 35ms suffices, which is safe for 32nds to about 214 BPM. Polling cannot do better than its own interval, so for genuine precision Ctrl+Alt+Shift+Space loops the bar via the loop points instead, letting the audio engine own the boundary. That is sample accurate at any tempo or grid size. It borrows loop range and repeat state; the daemon restores both once transport stops, including when stopped with Space.
This commit is contained in:
@@ -401,6 +401,45 @@ function M.playFromBar(stopAtBarEnd)
|
||||
return barNum
|
||||
end
|
||||
|
||||
--[[
|
||||
Loop the cursor's bar.
|
||||
|
||||
Where the play-once action stops by polling -- and so can only be as precise
|
||||
as its polling interval -- this hands the boundary to REAPER's audio engine
|
||||
via the loop points, which is sample accurate. The next bar's downbeat can
|
||||
never leak in, at any tempo or grid size.
|
||||
|
||||
The trade is that it repeats until you stop it with Space, rather than
|
||||
stopping itself. For checking placement that is arguably better: leave it
|
||||
looping and edit while it plays.
|
||||
|
||||
Loop range and repeat state are saved and restored by the daemon once
|
||||
transport stops, so this does not quietly rearrange the project.
|
||||
]]
|
||||
function M.loopBar()
|
||||
local here = reaper.GetCursorPosition()
|
||||
local barPos, barEnd, barNum = M.barBounds(here)
|
||||
|
||||
local s, e = reaper.GetSet_LoopTimeRange(false, true, 0, 0, false)
|
||||
M.setTemp("savedloop", ("%.17g,%.17g"):format(s, e))
|
||||
M.setTemp("savedrep", reaper.GetSetRepeat(-1))
|
||||
M.setTemp("stoparm", reaper.time_precise() + 0.5)
|
||||
M.setTemp("stopat", "") -- cancel any pending play-once stop
|
||||
|
||||
reaper.PreventUIRefresh(1)
|
||||
reaper.GetSet_LoopTimeRange(true, true, barPos, barEnd, false)
|
||||
reaper.GetSetRepeat(1)
|
||||
reaper.SetEditCurPos(barPos, false, true)
|
||||
if (reaper.GetPlayState() % 2) == 0 then
|
||||
reaper.CSurf_OnPlay()
|
||||
end
|
||||
reaper.SetEditCurPos(here, false, false)
|
||||
reaper.PreventUIRefresh(-1)
|
||||
|
||||
M.ensureDaemon()
|
||||
return barNum
|
||||
end
|
||||
|
||||
---------------------------------------------------------------- time shift
|
||||
|
||||
--[[
|
||||
|
||||
Reference in New Issue
Block a user