Old 05-16-2020, 10:52 AM   #1
srdmusic
Human being with feelings
 
Join Date: Dec 2016
Posts: 878
Default Time signature, meter and tempo issues

Hi Reaper family!

I run into an obstetrical often on my film projects whenever I get a new cut of picture. I often add or removed beats from the sessions to hit the new cuts instead of making tempo changes. In doing so, a whole lot of issues with the tempo map arise.

I have attached two reaper sessions. The first one illustrates a problem with the tempo map after I’ve moved a MIDI item to hit a point in the picture. The second session is one that I have re-barred so that the MIDI on track 2 lands on a downbeat and the MIDI on track 1 also lands on a down beat. In order for both MIDI notes to land on a down beat I needed to add a 1/16bar at bar 3 and subsequently a 15/16th bar at bar 11.

What I would like is the ability for Reaper to intelligently help me through this process.

There must be a way to script this concept into an action. In theory, the user would first set left locators at bar they would like to change. Then set the right locator at the end of the section he or she wants to effect. The user would run the script. The script would ask the users what time signature to inserted at the left locator and then the script would insert and convert the right locator time signature to make up the difference.

I have included a screen shot of an option that is included with Digital Performer. It may give you some incite into how other DAWs handle this problem. Cubase also handles meter changes much differently. It may be a good idea to checkout how it handles meter changes because we are trying replicate this within reaper.

Here is the link to the files:
https://www.dropbox.com/sh/g4igqu5lq...-W5YwMhca?dl=0

If any of this needs further explanation please let me know.

Thank you in advance for the help with this.

Last edited by srdmusic; 05-18-2020 at 01:49 AM.
srdmusic is offline   Reply With Quote
Old 05-16-2020, 11:31 AM   #2
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

Could you please check the Dropbox link?

Last edited by juliansader; 05-16-2020 at 12:34 PM.
juliansader is offline   Reply With Quote
Old 05-18-2020, 01:50 AM   #3
srdmusic
Human being with feelings
 
Join Date: Dec 2016
Posts: 878
Default

Quote:
Originally Posted by juliansader View Post
Could you please check the Dropbox link?
Thanks for catching that Julian. Here's the link:

https://www.dropbox.com/sh/g4igqu5lq...-W5YwMhca?dl=0
srdmusic is offline   Reply With Quote
Old 05-18-2020, 03:07 AM   #4
X-Raym
Human being with feelings
 
X-Raym's Avatar
 
Join Date: Apr 2013
Location: France
Posts: 9,900
Default

Setting time signature from time selection reminds me the Advanced Tempo Tool Script


https://forum.cockos.com/showthread.php?t=170987


But I don't succeed to make your desired output with it so it might not be the solution to look for.


So a custom script would do


  • insert tigne signature at time selection start and time selection end
  • insert time signature if there was partial measure before time start
  • insert partial time signature before time selection end if necessary
  • all same tempo
  • prevent items to change in length and position
  • No window needed ?
Is that it ?


I wonder if this doesn't exist somehow... Time signatuire stuffs are usually a bit confusing to script. :S
X-Raym is offline   Reply With Quote
Old 05-18-2020, 01:39 PM   #5
srdmusic
Human being with feelings
 
Join Date: Dec 2016
Posts: 878
Default

Quote:
Originally Posted by X-Raym View Post
Setting time signature from time selection reminds me the Advanced Tempo Tool Script


https://forum.cockos.com/showthread.php?t=170987


But I don't succeed to make your desired output with it so it might not be the solution to look for.


So a custom script would do


  • insert tigne signature at time selection start and time selection end
  • insert time signature if there was partial measure before time start
  • insert partial time signature before time selection end if necessary
  • all same tempo
  • prevent items to change in length and position
  • No window needed ?
Is that it ?


I wonder if this doesn't exist somehow... Time signature stuffs are usually a bit confusing to script. :S
Is it is very similar to the advanced tempo script but it would be working with meter changes rather than tempo changes.

I think the only window or entry the user would need to do is to tell the script/ custom action what kind of partial time signature they desire to convert at the start of the time selection. I the example session, I would input 1/16 at the left locator and the script would convert the bar just before the right locator to 15/16. Everything else would remain at the original position and time signature of 4/4.

I believe it would be easier to create in a script to do some simple subtraction of the left locator signature to determine what the right locator signature should be.
srdmusic is offline   Reply With Quote
Old 05-18-2020, 04:59 PM   #6
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

Quote:
Originally Posted by srdmusic View Post
There must be a way to script this concept into an action. In theory, the user would first set left locators at bar they would like to change. Then set the right locator at the end of the section he or she wants to effect.
How would you prefer to locate the left and right hitpoints? With the time selection, as in the project files that you uploaded?


Quote:
Originally Posted by srdmusic View Post
The user would run the script. The script would ask the users what time signature to inserted at the left locator and then the script would insert and convert the right locator time signature to make up the difference.
For even faster workflow, the script can try to automatically calculate the time signature at the left locator, only asking if it hits a snag.

Can it be assumed that the right hitpoint will always be at the exact start of the measure?
juliansader is offline   Reply With Quote
Old 05-18-2020, 05:05 PM   #7
srdmusic
Human being with feelings
 
Join Date: Dec 2016
Posts: 878
Default

Quote:
Originally Posted by juliansader View Post
How would you prefer to locate the left and right hitpoints? With the time selection, as in the project files that you uploaded?




For even faster workflow, the script can try to automatically calculate the time signature at the left locator, only asking if it hits a snag.

Can it be assumed that the right hitpoint will always be at the exact start of the measure?
Time selection would probably be the easiest way so the user could establish which end point needed to be adjusted. Great idea with the script trying to do the calculations.
srdmusic is offline   Reply With Quote
Old 05-19-2020, 03:30 AM   #8
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

As X-Raym noted, time signature stuff is tricky to script. However, the following seems to do the trick:

Code:
--[[
ReaScript name: js_Insert timesig markers to align measures with hitpoints
Version: 1.01b
Author: juliansader
Website: http://forum.cockos.com/showthread.php?t=193258
About:
  # Description

  This script tries to align hitpoints with the downbeat of measures by imserting time signature markers at each hitpoint, 
      and then converting the preceding partial measure into normal -- but shorter -- measures with lower timesig numerator:denominator ratios.
      
  If a time selection is present, the script assumes that hitpoints are at the start and end of the selections.  
      v1.01 update:  If no time selection is present, the script will create a measure at the edit cursor, which allows the user to click (from left to right) through multiple hitpoints.
  
  If the script can align the hitpoints by *only* inserting time signature markers and *not* changing any tempos -- i.e. without changing the beat positions of the hitpoints -- it will do so automatically.
  
  If it can not, it will pop up REAPER's standard "Create measure from time selection" dialog window.
  
  In the script file's "USER AREA", the user can customize the "musicalDivisions" table, which contains all the timesig denominators that the script should try to fit into the shortened measures.
  If this table is commented out, the script will try any division, even weird ones such as 1/71.
]]

--[[
  Changelog:
  * v1.00 (2020-05-19)
    + Initial beta release
  * v1.01 (2020-05-19)
    + If no time selection, create measure at edit cursor.
  * v1.01b (2020-05-30)
    + To prevent later timesig markers from shifint, make sure that all are set to allow partial measures.
]]

-- USER AREA
-- Which musical divisions does this project use?
--musicalDivisions = {2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16, 18, 32, 64, 128}

-- TIMESIGS ARE TRICKY!
-- Must set to allow partial measures, otherwise may skip to nearest measure start
function AllowPartialMeasures(leftTime, rightTime)
    leftTime = leftTime or -math.huge
    rightTime = rightTime or math.huge
    local function gsubHelper(s)
        a, timeStr, b, dataStr, c = s:match("(\nPT )(%S+)( %S+ %S+ %S+ %S+ )(%S+)(.*)")
        if timeStr and dataStr then
            time, data = tonumber(timeStr), tonumber(dataStr)
            if time and leftTime-0.000001 <= time and time <= rightTime+0.000001 then
                if data and data == data//1 then
                    data = data|4
                    dataStr = string.format("%d", data)
                    return a..timeStr..b..dataStr..c
                end 
            end
        end
        return nil
    end
    tempoEnv = reaper.GetTrackEnvelope(reaper.GetMasterTrack(), 0)
    getChunkOK, chunk = reaper.GetEnvelopeStateChunk(tempoEnv, "", false)
    if not getChunkOK then reaper.MB("Could not get state chunk of tempo envelope.", "ERROR", 0) return false end
    chunk = chunk:gsub("\nPT [^\n]+", gsubHelper)
    setChunkOK = reaper.SetEnvelopeStateChunk(tempoEnv, chunk, false)
    if not setChunkOK then reaper.MB("Could not set state chunk of tempo envelope to allow partial measures.", "ERROR", 0) return false end
    return true
end


function Main()

    -- GET HITPOINTS
    -- Left and right hitpoints indicated by time selection.
    -- The two hitpoints will be handled similarly, so place in table and use the same code for both.
    tTimes = ( { reaper.GetSet_LoopTimeRange2(0, false, false, 0, 0, false) } )
    if tTimes[1] >= tTimes[2] then 
        tTimes = {reaper.GetCursorPositionEx(0)}
    end  
    
    -- SET TIMEBASES TO TIME
    -- This is not sufficient for allowing partial measures.
    timesigTimebase = reaper.SNM_GetIntConfigVar("tempoenvtimelock", -100)
    if timesigTimebase == -100 then reaper.MB("Error getting timebase for tempo/timesig markers.", "ERROR", 0) return end
    tOK = reaper.SNM_SetIntConfigVar("tempoenvtimelock", 0)
    if not tOK then reaper.MB("Error setting temporary timebase for tempo/timesig markers.", "ERROR", 0) return end
    tTimebase = {} -- Store item timebases so that can reset later
    for i = 0, reaper.CountMediaItems(0)-1 do
        item = reaper.GetMediaItem(0, i)
        tTimebase[item] = reaper.GetMediaItemInfo_Value(item, "C_BEATATTACHMODE")
        reaper.SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", 0)
    end    
    
    -- REAPER's timesigs are so finicky, that even those *after* tTimes[2] may skip around if they aren't *all* set to allow partial measures
    if not AllowPartialMeasures(tTimes[1], nil) then return end
    
    -- INSERT TIME SIGS AT BOTH HITPOINTS
    -- This creates temporary partial measures.
    -- This also protects against changes to right of hitpoints.
    for i = #tTimes, 1, -1 do
        hitTime = tTimes[i]
        ttidx = reaper.FindTempoTimeSigMarker(0, hitTime+0.0000001)
        tempoOK, tempoTime, _, _, _, timesig_numer, timesig_denom, hitLinear = reaper.GetTempoTimeSigMarker(0, ttidx)
        -- If no marker at hitPoint, or if marker is only tempo, not timesig, insert/edit marker
        if tempoTime < hitTime-0.0000001 or (timesig_numer <= 0 or timesig_denom <= 0) then 
            if tempoTime >= hitTime-0.0000001 then hitIndex = ttidx else hitIndex = -1 end -- New marker, or change existing?
            hitNumer, hitDenom, hitBpM = reaper.TimeMap_GetTimeSigAtTime(0, hitTime) -- Effective timesig and bpm
            reaper.SetTempoTimeSigMarker(0, hitIndex, hitTime, -1, -1, hitBpM, hitNumer, hitDenom, hitLinear)
            if not AllowPartialMeasures(hitTime, hitTime) then return end
        end
    end
    
    -- INSERTS TIMESIGS AT SHORTENED MEASURES
    for i = 1, #tTimes do
        hitTime = tTimes[i]
       
        _, measures = reaper.TimeMap2_timeToBeats(0, hitTime-0.00000001)
        measureTime, _, _, _, measureDenom, measureBpM = reaper.TimeMap_GetMeasureInfo(0, measures)
        measureQN = reaper.TimeMap_timeToQN_abs(0, measureTime) -- Beware of partial measures with hidden QNs - use abs
        hitQN = reaper.TimeMap_timeToQN_abs(0, hitTime)         
        len = (hitQN - measureQN)/4
        
        -- FIND DIVISION THAT DIVIDES INTO SHORTENED MEASURE
        -- if newNumer then newNumer = newDenom - newNumer end -- For righthand hitpoint, 
        local newNumer, newDenom
        if not (type(musicalDivisions) == "table" and #musicalDivisions > 0) then musicalDivisions = {} for i = 1, 128 do musicalDivisions[i] = i end end
        for _, d in ipairs(musicalDivisions) do
            if d >= measureDenom then -- Skip divisions larger than current denom.  For example, if current timesig is 8/8 and the shortened measure is two QN long, use 4/8 instead of 1/2 or 2/4.
                n = len*d
                nRound = (0.5+n)//1
                --frac = n/nRound
                if -0.00000001 < len-nRound/d and len-nRound/d < 0.00000001 then --len-nRound/d is (error in QNs)/4 
                    newNumer = nRound
                    newDenom = d
                    break
                end
            end
        end
        
        if newNumer and newDenom then
            ttidx = reaper.FindTempoTimeSigMarker(0, measureTime+0.0000001)
            tempoOK, tempoTime, _, _, _, _, _, measureLinear = reaper.GetTempoTimeSigMarker(0, ttidx)
            if tempoTime >= measureTime-0.0000001 then measureIndex = ttidx else measureIndex = -1 end -- New marker, or change existing?
            _, _, measureBpM = reaper.TimeMap_GetTimeSigAtTime(0, measureTime) -- Effective timesig and bpm
            reaper.SetTempoTimeSigMarker(0, measureIndex, measureTime, -1, -1, measureBpM, newNumer, newDenom, measureLinear)
        else
            reaper.GetSet_LoopTimeRange2(0, true, false, measureTime, hitTime, false)
            reaper.Main_OnCommand(40801, 0)
            userNumer, userDenom, userBpM = reaper.TimeMap_GetTimeSigAtTime(0, measureTime) -- Effective timesig and bp
        end
    end
    
    -- RESET TIMEBASES AND TIME SELECTION
    for item, timebase in pairs(tTimebase) do
        reaper.SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", timebase)
    end
    tOK = reaper.SNM_SetIntConfigVar("tempoenvtimelock", timesigTimebase)
    if not tOK then reaper.MB("Error re-setting temporary timebase for tempo/timesig markers.", "ERROR", 0) end
    
    reaper.GetSet_LoopTimeRange2(0, true, false, tTimes[1], tTimes[#tTimes], false)
end

reaper.Undo_BeginBlock2(0)
Main()
reaper.UpdateTimeline()
reaper.Undo_EndBlock2(0, "Insert timesigs to align measures with hitpoints", -1)
EDIT: Updated to v1.01 to allow single hitpoints at edit cursor.
EDIT 2: Updated to v1.01b to address swisscomposer's bug first report.

Last edited by juliansader; 05-30-2020 at 10:44 AM.
juliansader is offline   Reply With Quote
Old 05-19-2020, 04:20 AM   #9
X-Raym
Human being with feelings
 
X-Raym's Avatar
 
Join Date: Apr 2013
Location: France
Posts: 9,900
Default

@juliansader
Thanks for digging this ^^ I have made several tempo related scripts and they always took way more time than than expected. :P


When your script will be ready we could put it in reateam script repo.
X-Raym is offline   Reply With Quote
Old 05-19-2020, 07:20 AM   #10
_Stevie_
Human being with feelings
 
_Stevie_'s Avatar
 
Join Date: Oct 2017
Location: Black Forest
Posts: 5,067
Default

@Julian, this looks really really good!

And yes, I think we can assume, that the right locator would end on a bar.
__________________
My Reascripts forum thread | My Reascripts on GitHub
If you like or use my scripts, please support the Ukraine: Ukraine Crisis Relief Fund | DirectRelief | Save The Children | Razom
_Stevie_ is offline   Reply With Quote
Old 05-19-2020, 08:17 AM   #11
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 2,009
Default

wow thanks for that script very useful! Would be great to have in reapack also
daniellumertz is offline   Reply With Quote
Old 05-19-2020, 09:28 AM   #12
vsthem
Human being with feelings
 
Join Date: Nov 2018
Posts: 660
Default

Having a hard time wrapping my head around this. Is there any chance one of you could post a gif of this thing in action?
vsthem is offline   Reply With Quote
Old 05-19-2020, 09:32 AM   #13
srdmusic
Human being with feelings
 
Join Date: Dec 2016
Posts: 878
Default

Quote:
Originally Posted by juliansader View Post
EDIT: Update to v1.01 to allow single hitpoints at edit cursor.
Holy mother of time signature Batman!!! You've done it Julian, Thanks you. Sending a donation your way.
srdmusic is offline   Reply With Quote
Old 05-30-2020, 06:06 AM   #14
swiiscompos
Human being with feelings
 
swiiscompos's Avatar
 
Join Date: Mar 2011
Location: London
Posts: 1,211
Default

Great job Julian! Your script however will mess up with the position of later time signatures. Here is a fix that prevents that:

Code:
--[[
ReaScript name: js_Insert timesig markers to align measures with hitpoints
Version: 1.1
Author: juliansader
Website: http://forum.cockos.com/showthread.php?t=193258
About:
  # Description

  This script tries to align hitpoints with the downbeat of measures by inserting time signature markers at each hitpoint, 
      and then converting the preceding partial measure into normal -- but shorter -- measures with lower timesig numerator:denominator ratios.
      
  If a time selection is present, the script assumes that hitpoints are at the start and end of the selections.  
      v1.01 update:  If no time selection is present, the script will create a measure at the edit cursor, which allows the user to click through multiple hitpoints.

  If the script can align the hitpoints by *only* inserting time signature markers and *not* changing any tempos, it will do so automatically.

  The script will not automatically change tempos, since tempo changes will necessarily also change either the beat positions or time positions of other markers and items.
  
  If the script cannot align the hitpoints automatically, it will pop up REAPER's standard "Create measure from time selection" dialog window, in which the user can set a new tempo.
  
  In the script file's "USER AREA", the user can customize the "musicalDivisions" table, which contains all the timesig denominators that the script should try to fit into the shortened measures.
  If this table is commented out, the script will try any division, even weird ones such as 1/71.
]]

--[[
  Changelog:
  * v1.00 (2020-05-19)
    + Initial beta release
  * v1.01 (2020-05-19)
    + If no time selection, create measure at edit cursor.
  * v1.1 (2020-05-20)
    + Avoid shifting of later Time Sig position (swiiscompos)
]]

-- USER AREA
-- Which musical divisions does this project use?
--musicalDivisions = {2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16, 18, 32, 64, 128}

-- TIMESIGS ARE TRICKY!
-- Must set to allow partial measures, otherwise may skip to nearest measure start


function PlaceBackLaterTempMarkers (rightTime)
  local lastTempoMark = reaper.FindTempoTimeSigMarker(0, rightTime)
  retval, timepos, measurepos, beatpos, bpm, timesig_num, timesig_denom, lineartempo = reaper.GetTempoTimeSigMarker( 0, lastTempoMark + 1 )
  reaper.SetTempoTimeSigMarker( 0, lastTempoMark+1, -1, measurepos-1, beatpos, bpm, timesig_num, timesig_denom, lineartempo )
--end
end

function AllowPartialMeasures(leftTime, rightTime)
    leftTime = leftTime or -math.huge
    rightTime = rightTime or math.huge
    local function gsubHelper(s)
        a, timeStr, b, dataStr, c = s:match("(\nPT )(%S+)( %S+ %S+ %S+ %S+ )(%S+)(.*)")
        if timeStr and dataStr then
            time, data = tonumber(timeStr), tonumber(dataStr)
            if time and leftTime-0.000001 <= time and time <= rightTime+0.000001 then
                if data and data == data//1 then
                    data = data|4
                    dataStr = string.format("%d", data)
                    return a..timeStr..b..dataStr..c
                end 
            end
        end
        return nil
    end
    tempoEnv = reaper.GetTrackEnvelope(reaper.GetMasterTrack(), 0)
    getChunkOK, chunk = reaper.GetEnvelopeStateChunk(tempoEnv, "", false)
    if not getChunkOK then reaper.MB("Could not get state chunk of tempo envelope.", "ERROR", 0) return false end
    chunk = chunk:gsub("\nPT [^\n]+", gsubHelper)
    setChunkOK = reaper.SetEnvelopeStateChunk(tempoEnv, chunk, false)
    if not setChunkOK then reaper.MB("Could not set state chunk of tempo envelope.", "ERROR", 0) return false end
    return true
end


function Main()

    -- GET HITPOINTS
    -- Left and right hitpoints indicated by time selection.
    -- The two hitpoints will be handled similarly, so place in table and use the same code for both.
    tTimes = ( { reaper.GetSet_LoopTimeRange2(0, false, false, 0, 0, false) } )
    local timeSel = true
    if tTimes[1] >= tTimes[2] then 
        tTimes = {reaper.GetCursorPositionEx(0)}
        timeSel = false
    end  
    
    -- SET TIMEBASES TO TIME
    -- This is not sufficient for allowing partial measures.
    timesigTimebase = reaper.SNM_GetIntConfigVar("tempoenvtimelock", -100)
    if timesigTimebase == -100 then reaper.MB("Error getting timebase for tempo/timesig markers.", "ERROR", 0) return end
    tOK = reaper.SNM_SetIntConfigVar("tempoenvtimelock", 0)
    if not tOK then reaper.MB("Error setting temporary timebase for tempo/timesig markers.", "ERROR", 0) return end
    tTimebase = {} -- Store item timebases so that can reset later
    for i = 0, reaper.CountMediaItems(0)-1 do
        item = reaper.GetMediaItem(0, i)
        tTimebase[item] = reaper.GetMediaItemInfo_Value(item, "C_BEATATTACHMODE")
        reaper.SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", 0)
    end    
    
   if not AllowPartialMeasures(tTimes[1], tTimes[2]) then return end
    
    -- INSERT TIME SIGS AT BOTH HITPOINTS
    -- This creates temporary partial measures.
    -- This also protects against changes to right of hitpoints.
    for i = #tTimes, 1, -1 do
        hitTime = tTimes[i]
        i = reaper.FindTempoTimeSigMarker(0, hitTime+0.0000001)
        tempoOK, tempoTime, _, _, _, timesig_numer, timesig_denom, hitLinear = reaper.GetTempoTimeSigMarker(0, i)
        -- If no marker at hitPoint, or if marker is only tempo, not timesig, insert/edit marker
        if tempoTime < hitTime-0.0000001 or (timesig_numer <= 0 or timesig_denom <= 0) then 
            if tempoTime >= hitTime-0.0000001 then hitIndex = i else hitIndex = -1 end -- New marker, or change existing?
            hitNumer, hitDenom, hitBpM = reaper.TimeMap_GetTimeSigAtTime(0, hitTime) -- Effective timesig and bpm
            reaper.SetTempoTimeSigMarker(0, hitIndex, hitTime, -1, -1, hitBpM, hitNumer, hitDenom, hitLinear)
            if not AllowPartialMeasures(hitTime, hitTime) then return end 
        end
    end
    
    -- INSERTS TIMESIGS AT SHORTENED MEASURES
    for i = 1, #tTimes do
        hitTime = tTimes[i]
       
        _, measures = reaper.TimeMap2_timeToBeats(0, hitTime-0.00000001)
        measureTime, _, _, _, measureDenom, measureBpM = reaper.TimeMap_GetMeasureInfo(0, measures)
        measureQN = reaper.TimeMap_timeToQN_abs(0, measureTime) -- Beware of partial measures with hidden QNs - use abs
        hitQN = reaper.TimeMap_timeToQN_abs(0, hitTime)         
        len = (hitQN - measureQN)/4
        
        -- FIND DIVISION THAT DIVIDES INTO SHORTENED MEASURE
        -- if newNumer then newNumer = newDenom - newNumer end -- For righthand hitpoint, 
        local newNumer, newDenom
        if not (type(musicalDivisions) == "table" and #musicalDivisions > 0) then musicalDivisions = {} for i = 1, 128 do musicalDivisions[i] = i end end
        for _, d in ipairs(musicalDivisions) do
            if d >= measureDenom then -- Skip divisions larger than current denom.  For example, if current timesig is 8/8 and the shortened measure is two QN long, use 4/8 instead of 1/2 or 2/4.
                n = len*d
                nRound = (0.5+n)//1
                --frac = n/nRound
                if -0.00000001 < len-nRound/d and len-nRound/d < 0.00000001 then --len-nRound/d is (error in QNs)/4 
                    newNumer = nRound
                    newDenom = d
                    break
                end
            end
        end
        
        if newNumer and newDenom then
            i = reaper.FindTempoTimeSigMarker(0, measureTime+0.0000001)
            tempoOK, tempoTime, _, _, _, _, _, measureLinear = reaper.GetTempoTimeSigMarker(0, i)
            if tempoTime >= measureTime-0.0000001 then measureIndex = i else measureIndex = -1 end -- New marker, or change existing?
            _, _, measureBpM = reaper.TimeMap_GetTimeSigAtTime(0, measureTime) -- Effective timesig and bpm
            reaper.SetTempoTimeSigMarker(0, measureIndex, measureTime, -1, -1, measureBpM, newNumer, newDenom, measureLinear)
        else
            reaper.GetSet_LoopTimeRange2(0, true, false, measureTime, hitTime, false)
            reaper.Main_OnCommand(40801, 0)
            userNumer, userDenom, userBpM = reaper.TimeMap_GetTimeSigAtTime(0, measureTime) -- Effective timesig and bp
        end
    end
    
    if timeSel == true then
      PlaceBackLaterTempMarkers (tTimes[2])
    end
    
    -- RESET TIMEBASES AND TIME SELECTION
    for item, timebase in pairs(tTimebase) do
        reaper.SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", timebase)
    end
    tOK = reaper.SNM_SetIntConfigVar("tempoenvtimelock", timesigTimebase)
    if not tOK then reaper.MB("Error re-setting temporary timebase for tempo/timesig markers.", "ERROR", 0) end
    
    reaper.GetSet_LoopTimeRange2(0, true, false, tTimes[1], tTimes[#tTimes], false)
end

reaper.Undo_BeginBlock2(0)
Main()
reaper.UpdateTimeline()
reaper.Undo_EndBlock2(0, "Insert timesigs to align measures with hitpoints", -1)

Last edited by swiiscompos; 05-30-2020 at 06:31 AM.
swiiscompos is offline   Reply With Quote
Old 05-30-2020, 07:47 AM   #15
swiiscompos
Human being with feelings
 
swiiscompos's Avatar
 
Join Date: Mar 2011
Location: London
Posts: 1,211
Default

A new update: Now when there is only one hit point the script checks if there are time signatures after the hit point. If yes he will use the first time signature change (tempo changes don't count) to add a shorter bar before it. This way we avoid shorter bars that don't exist in the MIDI as those create many problems when they are exported. If no time signature exist later, then the script will simply move the hit point on a downbeat, adding a short measure before. The great thing is that all of this works even if there are tempo changes in the middle!

Code:
--[[
ReaScript name: js_Insert timesig markers to align measures with hitpoints
Version: 1.11
Author: juliansader
Website: http://forum.cockos.com/showthread.php?t=193258
About:
  # Description

  This script tries to align hitpoints with the downbeat of measures by inserting time signature markers at each hitpoint, 
      and then converting the preceding partial measure into normal -- but shorter -- measures with lower timesig numerator:denominator ratios.
      
  If a time selection is present, the script assumes that hitpoints are at the start and end of the selections.  
      v1.01 update:  If no time selection is present, the script will create a measure at the edit cursor, which allows the user to click through multiple hitpoints.

  If the script can align the hitpoints by *only* inserting time signature markers and *not* changing any tempos, it will do so automatically.

  The script will not automatically change tempos, since tempo changes will necessarily also change either the beat positions or time positions of other markers and items.
  
  If the script cannot align the hitpoints automatically, it will pop up REAPER's standard "Create measure from time selection" dialog window, in which the user can set a new tempo.
  
  In the script file's "USER AREA", the user can customize the "musicalDivisions" table, which contains all the timesig denominators that the script should try to fit into the shortened measures.
  If this table is commented out, the script will try any division, even weird ones such as 1/71.
]]

--[[
  Changelog:
  * v1.00 (2020-05-19)
    + Initial beta release
  * v1.01 (2020-05-19)
    + If no time selection, create measure at edit cursor.
  * v1.1 (2020-05-20)
    + Avoid shifting of later Time Sig position (swiiscompos)
  * v1.11 (2020-05-20)
    + If no time selection, create short bar before next Time Sig if it exists (swiiscompos) 
]]

-- USER AREA
-- Which musical divisions does this project use?
--musicalDivisions = {2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16, 18, 32, 64, 128}

-- TIMESIGS ARE TRICKY!
-- Must set to allow partial measures, otherwise may skip to nearest measure start


function PlaceBackLaterTempMarkers (rightTime)
  local lastTempoMark = reaper.FindTempoTimeSigMarker(0, rightTime)
  retval, timepos, measurepos, beatpos, bpm, timesig_num, timesig_denom, lineartempo = reaper.GetTempoTimeSigMarker( 0, lastTempoMark + 1 )
  reaper.SetTempoTimeSigMarker( 0, lastTempoMark+1, -1, measurepos-1, beatpos, bpm, timesig_num, timesig_denom, lineartempo )
--end
end

function AllowPartialMeasures(leftTime, rightTime)
    leftTime = leftTime or -math.huge
    rightTime = rightTime or math.huge
    local function gsubHelper(s)
        a, timeStr, b, dataStr, c = s:match("(\nPT )(%S+)( %S+ %S+ %S+ %S+ )(%S+)(.*)")
        if timeStr and dataStr then
            time, data = tonumber(timeStr), tonumber(dataStr)
            if time and leftTime-0.000001 <= time and time <= rightTime+0.000001 then
                if data and data == data//1 then
                    data = data|4
                    dataStr = string.format("%d", data)
                    return a..timeStr..b..dataStr..c
                end 
            end
        end
        return nil
    end
    tempoEnv = reaper.GetTrackEnvelope(reaper.GetMasterTrack(), 0)
    getChunkOK, chunk = reaper.GetEnvelopeStateChunk(tempoEnv, "", false)
    if not getChunkOK then reaper.MB("Could not get state chunk of tempo envelope.", "ERROR", 0) return false end
    chunk = chunk:gsub("\nPT [^\n]+", gsubHelper)
    setChunkOK = reaper.SetEnvelopeStateChunk(tempoEnv, chunk, false)
    if not setChunkOK then reaper.MB("Could not set state chunk of tempo envelope.", "ERROR", 0) return false end
    return true
end


function Main()

    -- GET HITPOINTS
    -- Left and right hitpoints indicated by time selection.
    -- The two hitpoints will be handled similarly, so place in table and use the same code for both.
    tTimes = ( { reaper.GetSet_LoopTimeRange2(0, false, false, 0, 0, false) } )
    local timeSel = true
    local laterMarkers = true
    tempoOK = true
    i = -1
    if tTimes[1] >= tTimes[2] then 
        timeSel = false
        tTimes[1] = reaper.GetCursorPositionEx(0)
        prevTempoMark = reaper.FindTempoTimeSigMarker(0, tTimes[1])
        while (i == -1 ) and (tempoOK == true) do
          prevTempoMark = prevTempoMark + 1
          tempoOK, tempoTime, _, _, _, timesig_numer, timesig_denom, hitLinear = reaper.GetTempoTimeSigMarker(0, prevTempoMark)
          i = timesig_numer
        end
        if i >= 0  then
          tTimes[2] = tempoTime
        else
          table.remove(tTimes, 2)
          laterMarkers = false
        end
    end  
    
    -- SET TIMEBASES TO TIME
    -- This is not sufficient for allowing partial measures.
    timesigTimebase = reaper.SNM_GetIntConfigVar("tempoenvtimelock", -100)
    if timesigTimebase == -100 then reaper.MB("Error getting timebase for tempo/timesig markers.", "ERROR", 0) return end
    tOK = reaper.SNM_SetIntConfigVar("tempoenvtimelock", 0)
    if not tOK then reaper.MB("Error setting temporary timebase for tempo/timesig markers.", "ERROR", 0) return end
    tTimebase = {} -- Store item timebases so that can reset later
    for i = 0, reaper.CountMediaItems(0)-1 do
        item = reaper.GetMediaItem(0, i)
        tTimebase[item] = reaper.GetMediaItemInfo_Value(item, "C_BEATATTACHMODE")
        reaper.SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", 0)
    end    
    
   --if not AllowPartialMeasures(tTimes[1], tTimes[2]) then return end
    
    -- INSERT TIME SIGS AT BOTH HITPOINTS
    -- This creates temporary partial measures.
    -- This also protects against changes to right of hitpoints.
    for i = #tTimes, 1, -1 do
        hitTime = tTimes[i]
        i = reaper.FindTempoTimeSigMarker(0, hitTime+0.0000001)
        tempoOK, tempoTime, _, _, _, timesig_numer, timesig_denom, hitLinear = reaper.GetTempoTimeSigMarker(0, i)
        -- If no marker at hitPoint, or if marker is only tempo, not timesig, insert/edit marker
        if tempoTime < hitTime-0.0000001 or (timesig_numer <= 0 or timesig_denom <= 0) then 
            if tempoTime >= hitTime-0.0000001 then hitIndex = i else hitIndex = -1 end -- New marker, or change existing?
            hitNumer, hitDenom, hitBpM = reaper.TimeMap_GetTimeSigAtTime(0, hitTime) -- Effective timesig and bpm
            reaper.SetTempoTimeSigMarker(0, hitIndex, hitTime, -1, -1, hitBpM, hitNumer, hitDenom, hitLinear)
            --if not AllowPartialMeasures(hitTime, hitTime) then return end 
        end
    end
    
    -- INSERTS TIMESIGS AT SHORTENED MEASURES
    for i = 1, #tTimes do
        hitTime = tTimes[i]
       
        _, measures = reaper.TimeMap2_timeToBeats(0, hitTime-0.00000001)
        measureTime, _, _, _, measureDenom, measureBpM = reaper.TimeMap_GetMeasureInfo(0, measures)
        measureQN = reaper.TimeMap_timeToQN_abs(0, measureTime) -- Beware of partial measures with hidden QNs - use abs
        hitQN = reaper.TimeMap_timeToQN_abs(0, hitTime)         
        len = (hitQN - measureQN)/4
        
        -- FIND DIVISION THAT DIVIDES INTO SHORTENED MEASURE
        -- if newNumer then newNumer = newDenom - newNumer end -- For righthand hitpoint, 
        local newNumer, newDenom
        if not (type(musicalDivisions) == "table" and #musicalDivisions > 0) then musicalDivisions = {} for i = 1, 128 do musicalDivisions[i] = i end end
        for _, d in ipairs(musicalDivisions) do
            if d >= measureDenom then -- Skip divisions larger than current denom.  For example, if current timesig is 8/8 and the shortened measure is two QN long, use 4/8 instead of 1/2 or 2/4.
                n = len*d
                nRound = (0.5+n)//1
                --frac = n/nRound
                if -0.00000001 < len-nRound/d and len-nRound/d < 0.00000001 then --len-nRound/d is (error in QNs)/4 
                    newNumer = nRound
                    newDenom = d
                    break
                end
            end
        end
        
        if newNumer and newDenom then
            i = reaper.FindTempoTimeSigMarker(0, measureTime+0.0000001)
            tempoOK, tempoTime, _, _, _, _, _, measureLinear = reaper.GetTempoTimeSigMarker(0, i)
            if tempoTime >= measureTime-0.0000001 then measureIndex = i else measureIndex = -1 end -- New marker, or change existing?
            _, _, measureBpM = reaper.TimeMap_GetTimeSigAtTime(0, measureTime) -- Effective timesig and bpm
            reaper.SetTempoTimeSigMarker(0, measureIndex, measureTime, -1, -1, measureBpM, newNumer, newDenom, measureLinear)
        else
            reaper.GetSet_LoopTimeRange2(0, true, false, measureTime, hitTime, false)
            reaper.Main_OnCommand(40801, 0)
            userNumer, userDenom, userBpM = reaper.TimeMap_GetTimeSigAtTime(0, measureTime) -- Effective timesig and bp
        end
    end
    
   if laterMarkers == true then
      PlaceBackLaterTempMarkers (tTimes[2])
   end
    
    -- RESET TIMEBASES AND TIME SELECTION
    for item, timebase in pairs(tTimebase) do
        reaper.SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", timebase)
    end
    tOK = reaper.SNM_SetIntConfigVar("tempoenvtimelock", timesigTimebase)
    if not tOK then reaper.MB("Error re-setting temporary timebase for tempo/timesig markers.", "ERROR", 0) end
    
    if timeSel == true then
      reaper.GetSet_LoopTimeRange2(0, true, false, tTimes[1], tTimes[#tTimes], false)
    end
end

reaper.Undo_BeginBlock2(0)
Main()
reaper.UpdateTimeline()
reaper.Undo_EndBlock2(0, "Insert timesigs to align measures with hitpoints", -1)
swiiscompos is offline   Reply With Quote
Old 05-30-2020, 10:36 AM   #16
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

Thanks for reporting bugs! The script is not supposed to change anything past the second, righthand hitpoint. Could you perhaps post an RPP or screenshot of the timesigs that are altered by my original version?
juliansader is offline   Reply With Quote
Old 05-30-2020, 10:41 AM   #17
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

Ah, I noticed a possible bug if timesigs to the right of the second hitpoint were not already set to allow partial measures.

In line 87, could you please try changing "tTimes[2]":
Code:
if not AllowPartialMeasures(tTimes[1], tTimes[2]) then return end
to "nil":
Code:
if not AllowPartialMeasures(tTimes[1], nil) then return end
and check if it now works OK when a time selection with two hitpoints is present? (I updated my code above.)

Thank you very much for accompanying the bug report with code fixes! I will study the changes that you made to see what other problems you fixed.

Last edited by juliansader; 05-30-2020 at 10:57 AM.
juliansader is offline   Reply With Quote
Old 05-30-2020, 10:48 AM   #18
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

BTW, those who do video scoring may be interested in the scripts for video thumbnails that I uploaded yesterday.
juliansader is offline   Reply With Quote
Old 05-30-2020, 12:12 PM   #19
swiiscompos
Human being with feelings
 
swiiscompos's Avatar
 
Join Date: Mar 2011
Location: London
Posts: 1,211
Default

Quote:
Originally Posted by juliansader View Post
Ah, I noticed a possible bug if timesigs to the right of the second hitpoint were not already set to allow partial measures.

In line 87, could you please try changing "tTimes[2]":
Code:
if not AllowPartialMeasures(tTimes[1], tTimes[2]) then return end
to "nil":
Code:
if not AllowPartialMeasures(tTimes[1], nil) then return end
and check if it now works OK when a time selection with two hitpoints is present? (I updated my code above.)
Yes, it works, and it's actually better than my marker replacement system. I added it to my updated code which improves the one hit behavior (it tracks the next time signature, discarding tempo changes, and adds the short measure before it to avoid any problem with incomplete measures.

Code:
--[[
ReaScript name: js_Insert timesig markers to align measures with hitpoints
Version: 1.1
Author: juliansader
Website: http://forum.cockos.com/showthread.php?t=193258
About:
  # Description

  This script tries to align hitpoints with the downbeat of measures by inserting time signature markers at each hitpoint, 
      and then converting the preceding partial measure into normal -- but shorter -- measures with lower timesig numerator:denominator ratios.
      
  If a time selection is present, the script assumes that hitpoints are at the start and end of the selections.  
      v1.01 update:  If no time selection is present, the script will create a measure at the edit cursor, which allows the user to click through multiple hitpoints.

  If the script can align the hitpoints by *only* inserting time signature markers and *not* changing any tempos, it will do so automatically.

  The script will not automatically change tempos, since tempo changes will necessarily also change either the beat positions or time positions of other markers and items.
  
  If the script cannot align the hitpoints automatically, it will pop up REAPER's standard "Create measure from time selection" dialog window, in which the user can set a new tempo.
  
  In the script file's "USER AREA", the user can customize the "musicalDivisions" table, which contains all the timesig denominators that the script should try to fit into the shortened measures.
  If this table is commented out, the script will try any division, even weird ones such as 1/71.
]]

--[[
  Changelog:
  * v1.00 (2020-05-19)
    + Initial beta release
  * v1.01 (2020-05-19)
    + If no time selection, create measure at edit cursor.
  * v1.1 (2020-05-20)
    + Avoid shifting of later Time Sig position (swiiscompos)
  * v1.11 (2020-05-20)
    + If no time selection, create short bar before next Time Sig if it exists (swiiscompos) 
  + v1.12 (2020-05-20) Better fix for later time signature shift.
]]

-- USER AREA
-- Which musical divisions does this project use?
--musicalDivisions = {2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16, 18, 32, 64, 128}

-- TIMESIGS ARE TRICKY!
-- Must set to allow partial measures, otherwise may skip to nearest measure start


function AllowPartialMeasures(leftTime, rightTime)
    leftTime = leftTime or -math.huge
    rightTime = rightTime or math.huge
    local function gsubHelper(s)
        a, timeStr, b, dataStr, c = s:match("(\nPT )(%S+)( %S+ %S+ %S+ %S+ )(%S+)(.*)")
        if timeStr and dataStr then
            time, data = tonumber(timeStr), tonumber(dataStr)
            if time and leftTime-0.000001 <= time and time <= rightTime+0.000001 then
                if data and data == data//1 then
                    data = data|4
                    dataStr = string.format("%d", data)
                    return a..timeStr..b..dataStr..c
                end 
            end
        end
        return nil
    end
    tempoEnv = reaper.GetTrackEnvelope(reaper.GetMasterTrack(), 0)
    getChunkOK, chunk = reaper.GetEnvelopeStateChunk(tempoEnv, "", false)
    if not getChunkOK then reaper.MB("Could not get state chunk of tempo envelope.", "ERROR", 0) return false end
    chunk = chunk:gsub("\nPT [^\n]+", gsubHelper)
    setChunkOK = reaper.SetEnvelopeStateChunk(tempoEnv, chunk, false)
    if not setChunkOK then reaper.MB("Could not set state chunk of tempo envelope.", "ERROR", 0) return false end
    return true
end

function Main()

    -- GET HITPOINTS
    -- Left and right hitpoints indicated by time selection.
    -- The two hitpoints will be handled similarly, so place in table and use the same code for both.
    hitTimeTable = {}
    tTimes = ( { reaper.GetSet_LoopTimeRange2(0, false, false, 0, 0, false) } )
    local timeSel = true
    laterMarkers = true
    tempoOK = true
    i = -1
    if tTimes[1] >= tTimes[2] then 
        timeSel = false
        tTimes[1] = reaper.GetCursorPositionEx(0)
        prevTempoMark = reaper.FindTempoTimeSigMarker(0, tTimes[1])
        while (i == -1 ) and (tempoOK == true) do
          prevTempoMark = prevTempoMark + 1
          tempoOK, tempoTime, _, _, _, timesig_numer, timesig_denom, hitLinear = reaper.GetTempoTimeSigMarker(0, prevTempoMark)
          i = timesig_numer
        end
        if i >= 0  then
          tTimes[2] = tempoTime
        else
          table.remove(tTimes, 2)
          laterMarkers = false
        end
    end  
    
    -- SET TIMEBASES TO TIME
    -- This is not sufficient for allowing partial measures.
    timesigTimebase = reaper.SNM_GetIntConfigVar("tempoenvtimelock", -100)
    if timesigTimebase == -100 then reaper.MB("Error getting timebase for tempo/timesig markers.", "ERROR", 0) return end
    tOK = reaper.SNM_SetIntConfigVar("tempoenvtimelock", 0)
    if not tOK then reaper.MB("Error setting temporary timebase for tempo/timesig markers.", "ERROR", 0) return end
    tTimebase = {} -- Store item timebases so that can reset later
    for i = 0, reaper.CountMediaItems(0)-1 do
        item = reaper.GetMediaItem(0, i)
        tTimebase[item] = reaper.GetMediaItemInfo_Value(item, "C_BEATATTACHMODE")
        reaper.SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", 0)
    end   
    
    if not AllowPartialMeasures(tTimes[1], nil) then return end
    
    -- INSERT TIME SIGS AT BOTH HITPOINTS
    -- This creates temporary partial measures.
    -- This also protects against changes to right of hitpoints.
    for i = #tTimes, 1, -1 do
        hitTime = tTimes[i]
        j=i
        a = reaper.FindTempoTimeSigMarker(0, hitTime+0.0000001)
        tempoOK, tempoTime, _, _, _, timesig_numer, timesig_denom, hitLinear = reaper.GetTempoTimeSigMarker(0, a)
        hitTimeTable[j]=a
        -- If no marker at hitPoint, or if marker is only tempo, not timesig, insert/edit marker
        if (tempoTime < hitTime-0.0000001) or (timesig_numer <= 0 or timesig_denom <= 0) then 
            if tempoTime >= hitTime-0.0000001 then hitIndex = a else hitIndex = -1 end -- New marker, or change existing?
            hitNumer, hitDenom, hitBpM = reaper.TimeMap_GetTimeSigAtTime(0, hitTime) -- Effective timesig and bpm
            reaper.SetTempoTimeSigMarker(0, hitIndex, hitTime, -1, -1, hitBpM, hitNumer, hitDenom, hitLinear)
        if not AllowPartialMeasures(hitTime, hitTime) then return end 
        end
    end
    

    -- INSERTS TIMESIGS AT SHORTENED MEASURES
    for i = 1, #tTimes do
        hitTime = tTimes[i]
       
        _, measures = reaper.TimeMap2_timeToBeats(0, hitTime-0.00000001)
        measureTime, _, _, _, measureDenom, measureBpM = reaper.TimeMap_GetMeasureInfo(0, measures)
        measureQN = reaper.TimeMap_timeToQN_abs(0, measureTime) -- Beware of partial measures with hidden QNs - use abs
        hitQN = reaper.TimeMap_timeToQN_abs(0, hitTime)         
        len = (hitQN - measureQN)/4
        
        -- FIND DIVISION THAT DIVIDES INTO SHORTENED MEASURE
        -- if newNumer then newNumer = newDenom - newNumer end -- For righthand hitpoint, 
        local newNumer, newDenom
        if not (type(musicalDivisions) == "table" and #musicalDivisions > 0) then musicalDivisions = {} for i = 1, 128 do musicalDivisions[i] = i end end
        for _, d in ipairs(musicalDivisions) do
            if d >= measureDenom then -- Skip divisions larger than current denom.  For example, if current timesig is 8/8 and the shortened measure is two QN long, use 4/8 instead of 1/2 or 2/4.
                n = len*d
                nRound = (0.5+n)//1
                --frac = n/nRound
                if -0.00000001 < len-nRound/d and len-nRound/d < 0.00000001 then --len-nRound/d is (error in QNs)/4 
                    newNumer = nRound
                    newDenom = d
                    break
                end
            end 
        end
    
        
        if newNumer and newDenom then
            i = reaper.FindTempoTimeSigMarker(0, measureTime+0.0000001)
            tempoOK, tempoTime, _, _, _, _, _, measureLinear = reaper.GetTempoTimeSigMarker(0, i)
            if tempoTime >= measureTime-0.0000001 then measureIndex = i else measureIndex = -1 end -- New marker, or change existing?
            _, _, measureBpM = reaper.TimeMap_GetTimeSigAtTime(0, measureTime) -- Effective timesig and bpm
            reaper.SetTempoTimeSigMarker(0, measureIndex, measureTime, -1, -1, measureBpM, newNumer, newDenom, measureLinear)
        else
            reaper.GetSet_LoopTimeRange2(0, true, false, measureTime, hitTime, false)
            reaper.Main_OnCommand(40801, 0)
            userNumer, userDenom, userBpM = reaper.TimeMap_GetTimeSigAtTime(0, measureTime) -- Effective timesig and bp
        end
    end 
    
    -- RESET TIMEBASES AND TIME SELECTION
    for item, timebase in pairs(tTimebase) do
        reaper.SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", timebase)
    end
    tOK = reaper.SNM_SetIntConfigVar("tempoenvtimelock", timesigTimebase)
    if not tOK then reaper.MB("Error re-setting temporary timebase for tempo/timesig markers.", "ERROR", 0) end
    
    if timeSel == true then
      reaper.GetSet_LoopTimeRange2(0, true, false, tTimes[1], tTimes[#tTimes], false)
    end
end

reaper.Undo_BeginBlock2(0)
Main()
reaper.UpdateTimeline()
reaper.Undo_EndBlock2(0, "Insert timesigs to align measures with hitpoints", -1)
EDIT: I believe all my changes are now at the beginning of MAIN()

Last edited by swiiscompos; 05-30-2020 at 12:19 PM.
swiiscompos 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 12:04 PM.


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