Old 11-24-2019, 04:20 AM   #1
o_e
Human being with feelings
 
o_e's Avatar
 
Join Date: May 2016
Posts: 681
Default nudge stretch markers?

Hi,
Is there a way to nudge stretch markers? Or is it possible to write a script that can do that? Or are there other ways to move stretch markers beside dragging with the mouse, I could'nt find anything..?

thanks!
o_e is offline   Reply With Quote
Old 11-26-2019, 01:04 PM   #2
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

How would you like to nudge stretch markers, besides dragging with the mouse?
juliansader is offline   Reply With Quote
Old 11-26-2019, 02:24 PM   #3
o_e
Human being with feelings
 
o_e's Avatar
 
Join Date: May 2016
Posts: 681
Default

Quote:
Originally Posted by juliansader View Post
How would you like to nudge stretch markers, besides dragging with the mouse?
Thanks for the answer!
I would like to use some keys for (fine-) positioning the stretch markers..?
Toolbar buttons would be fine as well..
o_e is offline   Reply With Quote
Old 11-27-2019, 06:46 PM   #4
o_e
Human being with feelings
 
o_e's Avatar
 
Join Date: May 2016
Posts: 681
Default

Quote:
Originally Posted by o_e View Post
Thanks for the answer!
I would like to use some keys for (fine-) positioning the stretch markers..?
Toolbar buttons would be fine as well..
Is it possible to write such a script?
o_e is offline   Reply With Quote
Old 11-30-2019, 07:15 AM   #5
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

Unfortunately, I myself am too busy at the moment to take a stab at it.

However, in principle, it would be straightforward to write such a script.

Could you specify in more detail how you would like to SMs to move?

* All SMs in selected items and within the time selection?

* How far should they move? By frame, grid or millisecond? Should the script pop up a dialog box to ask user inputs?
juliansader is offline   Reply With Quote
Old 11-30-2019, 09:26 AM   #6
o_e
Human being with feelings
 
o_e's Avatar
 
Join Date: May 2016
Posts: 681
Default

Quote:
Originally Posted by juliansader View Post
Unfortunately, I myself am too busy at the moment to take a stab at it.

However, in principle, it would be straightforward to write such a script.

Could you specify in more detail how you would like to SMs to move?

* All SMs in selected items and within the time selection?

* How far should they move? By frame, grid or millisecond? Should the script pop up a dialog box to ask user inputs?
Thank you again for your response!
Moving a single SM around would do it for me. Would be nice if it's possible to choose between frames, grid/ticks or (milli-) seconds. Think that's doable? I'am willing to pay a little bounty for such a script..
o_e is offline   Reply With Quote
Old 12-03-2019, 01:45 AM   #7
o_e
Human being with feelings
 
o_e's Avatar
 
Join Date: May 2016
Posts: 681
Default

Quote:
Originally Posted by o_e View Post
Thank you again for your response!
Moving a single SM around would do it for me. Would be nice if it's possible to choose between frames, grid/ticks or (milli-) seconds. Think that's doable? I'am willing to pay a little bounty for such a script..
Could this thread be moved to the

ReaScript, JSFX, REAPER Plug-in Extensions, Developer Forum


that would be very kind, thanks!
o_e is offline   Reply With Quote
Old 01-17-2020, 04:08 AM   #8
o_e
Human being with feelings
 
o_e's Avatar
 
Join Date: May 2016
Posts: 681
Default

Quote:
Originally Posted by juliansader View Post
Unfortunately, I myself am too busy at the moment to take a stab at it.

However, in principle, it would be straightforward to write such a script.

Could you specify in more detail how you would like to SMs to move?

* All SMs in selected items and within the time selection?

* How far should they move? By frame, grid or millisecond? Should the script pop up a dialog box to ask user inputs?
A script like this would still be very useful to me! Is it possible to select a certain SM (with the mouse) or do I have to make a time selection?
And yes, a dialog box would be very handy..
o_e is offline   Reply With Quote
Old 01-17-2020, 11:01 AM   #9
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

Hope this helps. Please let me know if it works OK:

Code:
-- js_Nudge stretch markers in time selection in active takes of selected items
tUnits = {s = {str = "second", func = function(time, nudge) return time+nudge end},
          m = {str = "milliseconds", func = function(time, nudge) return time+(nudge/1000) end},
          b = {str = "beats", func = function(time, nudge) return reaper.TimeMap2_QNToTime(0, nudge+reaper.TimeMap2_timeToQN(0, time)) end},
          n = {str = "notes", func = function(time, nudge) return tUnits.b.func(time, 4*nudge) end}, --reaper.TimeMap2_QNToTime(0, (4*nudge)+reaper.TimeMap2_timeToQN(0, time)) end,
          f = {str = "frames", func = function(time, nudge) framerate = framerate or reaper.TimeMap_curFrameRate(0) return time+(nudge/framerate) end},
          g = {str = "grid units", func = function(time, nudge) grid = grid or ({reaper.GetSetProjectGrid(0, false, 0, 0, 0)})[2] return tUnits.b.func(time, nudge*4*grid) end},
          }
              
function MAIN()
    iOK, input = reaper.GetUserInputs("Nudge stretch markers", 1, "Amount and unit (s,m,b,n,g or f)", "100 ms")
    if not iOK then return end
    
    if input:match("/") then 
        num, denom = input:match("([%-%d]+)/(%d+)") 
        if num and denom and tonumber(denom) ~= 0 then
            nudge = tonumber(num) / tonumber(denom)
        end
    else
        nudge = tonumber(input:match("[%-%d%.]+"))
    end
    if not nudge then
        reaper.MB("The nudge amount could not be parsed.\n\nNumbers can be entered as \"0.25\" or as \"1/4\".", "ERROR", 0) 
        return
    elseif nudge == 0 then
        reaper.MB("The nudge amount should not be zero.\n\nNumbers can be entered as \"0.25\" or as \"1/4\".", "ERROR", 0) 
        return 
    elseif nudge < 0 then 
        step = 1 
    elseif nudge > 0 then
        step = -1 
    end
    
    unit = input:match("%a") or "m"
    if not tUnits[unit] then reaper.MB("Input must contain a unit, either \n* (s)econds, \n* (m)illiseconds, \n* (g)rid, \n* (n)otes (for example, \"1/128 n\"), \n* (b)eats (one beat is always one quarter note), or \n* (f)rames.", "ERROR", 0) return end
    
    timeSelStart, timeSelEnd = reaper.GetSet_LoopTimeRange2(0, false, false, 0, 0, true)
    if timeSelStart >= timeSelEnd then reaper.MB("A time selection must be made.", "ERROR", 0) return end
    
    total = 0
    tI = {} -- Why store all variables in this table?  For easier debugging, since speed of variable access is not relevant to this script.
    for i = 0, reaper.CountSelectedMediaItems(0)-1 do
        tI[i] = {item = reaper.GetSelectedMediaItem(0, i)}
        if tI[i].item and reaper.ValidatePtr2(0, tI[i].item, "MediaItem*") then
            tI[i].take = reaper.GetActiveTake(tI[i].item)
            if tI[i].take and reaper.ValidatePtr2(0, tI[i].take, "MediaItem_Take*") and not reaper.TakeIsMIDI(tI[i].take) then
                tI[i].numSM = reaper.GetTakeNumStretchMarkers(tI[i].take)
                if tI[i].numSM > 0 then
                    tI[i].itemStart = reaper.GetMediaItemInfo_Value(tI[i].item, "D_POSITION")
                    tI[i].sourceStart = tI[i].itemStart - reaper.GetMediaItemTakeInfo_Value(tI[i].take, "D_STARTOFFS")
                    local tLimits = (step == -1) and {start = tI[i].numSM-1, finish = 0} or {start = 0, finish = tI[i].numSM-1}
                    for s = tLimits.start, tLimits.finish, step do
                        tI[i][s] = {}
                        tI[i][s].sOK, tI[i][s].stretchedTime, tI[i][s].origTime = reaper.GetTakeStretchMarker(tI[i].take, s)
                        if tI[i][s].sOK then
                            projTime = tI[i].itemStart + tI[i][s].stretchedTime
                            if timeSelStart <= projTime and projTime <= timeSelEnd then  
                                tI[i][s].nudgedTime = tUnits[unit].func(projTime, nudge)-tI[i].itemStart
                                sOK, nextTime = reaper.GetTakeStretchMarker(tI[i].take, s-step) -- Check for overlaps
                                if sOK and ((step == -1 and tI[i][s].nudgedTime >= nextTime-0.0000001) or (step == 1 and tI[i][s].nudgedTime <= nextTime+0.0000001)) then
                                    reaper.MB("Nudging the selected stretch markers would have resulted in overlaps with unselected stretch markers.\n\nUse undo to return all stretch markers to their original position.", "ERROR", 0) 
                                    return 
                                else
                                    reaper.SetTakeStretchMarker(tI[i].take, s, tI[i][s].nudgedTime, tI[i][s].origTime)
                                    total = total + 1
                                end
                            end
                        end
                    end -- for s = tLimits.start, tLimits.finish, step do
                    reaper.UpdateItemInProject(tI[i].item)
                end -- if tI[i].numSM > 0
            end -- if tI[i].take
        end -- if tI[i].item and reaper.ValidatePtr2(0, tI[i].item, "MediaItem*")
    end -- for i = 0, reaper.CountSelectedMediaItems(0)-1
end -- MAIN()

reaper.Undo_BeginBlock2(0)
MAIN()
if total and nudge and tUnits[unit] then 
    undoString = "Nudged "..tostring(total).." stretch markers by "..tonumber(nudge).." "..tUnits[unit].str
else
    undoString = "Nudged 0 stretch markers"
end
reaper.Undo_EndBlock2(0, undoString, -1)
Edit: Added descriptive title to script

Last edited by juliansader; 01-17-2020 at 12:15 PM.
juliansader is offline   Reply With Quote
Old 01-17-2020, 11:46 AM   #10
o_e
Human being with feelings
 
o_e's Avatar
 
Join Date: May 2016
Posts: 681
Default

Quote:
Originally Posted by juliansader View Post
Hope this helps. Please let me know if it works OK:

Code:
tUnits = {s = {str = "second", func = function(time, nudge) return time+nudge end},
          m = {str = "milliseconds", func = function(time, nudge) return time+(nudge/1000) end},
          b = {str = "beats", func = function(time, nudge) return reaper.TimeMap2_QNToTime(0, nudge+reaper.TimeMap2_timeToQN(0, time)) end},
          n = {str = "notes", func = function(time, nudge) return tUnits.b.func(time, 4*nudge) end}, --reaper.TimeMap2_QNToTime(0, (4*nudge)+reaper.TimeMap2_timeToQN(0, time)) end,
          f = {str = "frames", func = function(time, nudge) framerate = framerate or reaper.TimeMap_curFrameRate(0) return time+(nudge/framerate) end},
          g = {str = "grid units", func = function(time, nudge) grid = grid or ({reaper.GetSetProjectGrid(0, false, 0, 0, 0)})[2] return tUnits.b.func(time, nudge*4*grid) end},
          }
              
function MAIN()
    iOK, input = reaper.GetUserInputs("Nudge stretch markers", 1, "Amount and unit (s,m,b,n,g or f)", "100 ms")
    if not iOK then return end
    
    if input:match("/") then 
        num, denom = input:match("([%-%d]+)/(%d+)") 
        if num and denom and tonumber(denom) ~= 0 then
            nudge = tonumber(num) / tonumber(denom)
        end
    else
        nudge = tonumber(input:match("[%-%d%.]+"))
    end
    if not nudge then
        reaper.MB("The nudge amount could not be parsed.\n\nNumbers can be entered as \"0.25\" or as \"1/4\".", "ERROR", 0) 
        return
    elseif nudge == 0 then
        reaper.MB("The nudge amount should not be zero.\n\nNumbers can be entered as \"0.25\" or as \"1/4\".", "ERROR", 0) 
        return 
    elseif nudge < 0 then 
        step = 1 
    elseif nudge > 0 then
        step = -1 
    end
    
    unit = input:match("%a") or "m"
    if not tUnits[unit] then reaper.MB("Input must contain a unit, either \n* (s)econds, \n* (m)illiseconds, \n* (g)rid, \n* (n)otes (for example, \"1/128 n\"), \n* (b)eats (one beat is always one quarter note), or \n* (f)rames.", "ERROR", 0) return end
    
    timeSelStart, timeSelEnd = reaper.GetSet_LoopTimeRange2(0, false, false, 0, 0, true)
    if timeSelStart >= timeSelEnd then reaper.MB("A time selection must be made.", "ERROR", 0) return end
    
    total = 0
    tI = {} -- Why store all variables in this table?  For easier debugging, since speed of variable access is not relevant to this script.
    for i = 0, reaper.CountSelectedMediaItems(0)-1 do
        tI[i] = {item = reaper.GetSelectedMediaItem(0, i)}
        if tI[i].item and reaper.ValidatePtr2(0, tI[i].item, "MediaItem*") then
            tI[i].take = reaper.GetActiveTake(tI[i].item)
            if tI[i].take and reaper.ValidatePtr2(0, tI[i].take, "MediaItem_Take*") and not reaper.TakeIsMIDI(tI[i].take) then
                tI[i].numSM = reaper.GetTakeNumStretchMarkers(tI[i].take)
                if tI[i].numSM > 0 then
                    tI[i].itemStart = reaper.GetMediaItemInfo_Value(tI[i].item, "D_POSITION")
                    tI[i].sourceStart = tI[i].itemStart - reaper.GetMediaItemTakeInfo_Value(tI[i].take, "D_STARTOFFS")
                    local tLimits = (step == -1) and {start = tI[i].numSM-1, finish = 0} or {start = 0, finish = tI[i].numSM-1}
                    for s = tLimits.start, tLimits.finish, step do
                        tI[i][s] = {}
                        tI[i][s].sOK, tI[i][s].stretchedTime, tI[i][s].origTime = reaper.GetTakeStretchMarker(tI[i].take, s)
                        if tI[i][s].sOK then
                            projTime = tI[i].itemStart + tI[i][s].stretchedTime
                            if timeSelStart <= projTime and projTime <= timeSelEnd then  
                                tI[i][s].nudgedTime = tUnits[unit].func(projTime, nudge)-tI[i].itemStart
                                sOK, nextTime = reaper.GetTakeStretchMarker(tI[i].take, s-step) -- Check for overlaps
                                if sOK and ((step == -1 and tI[i][s].nudgedTime >= nextTime-0.0000001) or (step == 1 and tI[i][s].nudgedTime <= nextTime+0.0000001)) then
                                    reaper.MB("Nudging the selected stretch markers would have resulted in overlaps with unselected stretch markers.\n\nUse undo to return all stretch markers to their original position.", "ERROR", 0) 
                                    return 
                                else
                                    reaper.SetTakeStretchMarker(tI[i].take, s, tI[i][s].nudgedTime, tI[i][s].origTime)
                                    total = total + 1
                                end
                            end
                        end
                    end -- for s = tLimits.start, tLimits.finish, step do
                    reaper.UpdateItemInProject(tI[i].item)
                end -- if tI[i].numSM > 0
            end -- if tI[i].take
        end -- if tI[i].item and reaper.ValidatePtr2(0, tI[i].item, "MediaItem*")
    end -- for i = 0, reaper.CountSelectedMediaItems(0)-1
end -- MAIN()

reaper.Undo_BeginBlock2(0)
MAIN()
if total and nudge and tUnits[unit] then 
    undoString = "Nudged "..tostring(total).." stretch markers by "..tonumber(nudge).." "..tUnits[unit].str
else
    undoString = "Nudged 0 stretch markers"
end
reaper.Undo_EndBlock2(0, undoString, -1)
Thanks a lot for your effort!
It is working fine although it is a misunderstanding. I was looking for nudging the placement of the SM at the item. Is that possible too?
It's a bit cumbersome to make a time selection for every SM, but I assume there is no other way to tell the script which SM is meant..
o_e is offline   Reply With Quote
Old 01-17-2020, 12:13 PM   #11
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

I added a title to the script: "Nudge all stretch markers in time selection in active takes of selected items", which describes it a bit better.

Do you really want to nudge only a single SM? I'm curious: what would be the advantage of a script with dialog box, over dragging the SM with the mouse?

It would be possible to change the script to adjust only the SM closest to the mouse.
juliansader is offline   Reply With Quote
Old 01-17-2020, 01:29 PM   #12
o_e
Human being with feelings
 
o_e's Avatar
 
Join Date: May 2016
Posts: 681
Default

Quote:
Originally Posted by juliansader View Post
I added a title to the script: "Nudge all stretch markers in time selection in active takes of selected items", which describes it a bit better.

Do you really want to nudge only a single SM? I'm curious: what would be the advantage of a script with dialog box, over dragging the SM with the mouse?

It would be possible to change the script to adjust only the SM closest to the mouse.
You are right, it is a bit embarrassing but I can do it absolutly with the mouse..
Apologizes for not thinking the thing through..
Can I make a little donation somewhere?
o_e is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 04:40 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.