LUA: How to create a Marker at specific BBT?

I would like to create markers at specific Bars, Beats, Ticks (BBT).
I found some related fonctions:

  local bbt = Timecode.BBT_TIME( 16, 1, 0)
  local loc = Session:locations():mark_at( 22 , 0)
  loc:set_name("Test 2")

But I am missing some pieces. I do not know how to convert BBT to positions
Any direction would be appreciated

In 6.9 we do not export the C++ functions to Lua that you would need for this. One reason for this was that we knew the API for this sort of thing was about to change (and indeed it has). In v7 will export the necessary methods.

1 Like

OK. Thanks for the quick answer.

Just to add some comments, for what it’s worth.

From what I can tell, mark_at() is a “get marker at position” method. There is no “create marker” method that I can find, presumably because of what Paul said.

Below is a function to convert bars, beats and ticks to sample position, but assumes there are 4 beats per bar. I was just experimenting; maybe a proper way already exists to do this.

function bbtToPos(bar, beat, tick)
  local bfc = ARDOUR.DoubleBeatsSamplesConverter(Session:tempo_map(), 0)
  local calcBeat = ((bar - 1) * 4) + (beat - 1) + (tick / 1920)
  return bfc:to(calcBeat)
end

-- example: create a range
local rng = Session:locations():add_range(bbtToPos(10,1,0), bbtToPos(11,1,960))
1 Like

I did this. I will change it when the API change, but it is not that complicated (once it is done …)

I did not see a LUA API to read/write the Session Metadata? It would be nice to have a way to save data. I know the script itself is saved with the session, but as the API will change, the scripts will need to be refreshed.

Also, it would be super helpful to have auto-completion in VS-Code and others, I guess there should be some standard definition file for classes, functions …

ardour {
    ["type"]    = "EditorAction",   --  "DSP", "Session", "EditorHook", "EditorAction"
    name        = "Create Song Markers", 
    -- Optional
    author      = "Bruno VERNAY",
    license     = "MIT",
    description = [[Structure the song with ranges: Verse ; Chorus ; Break ...
        Currently, the song structure is defined in the script
            Could add a dialog box to enter the structure
            Could parse the Session:Metadata description structure if available in Lua
    ]]
} 


function factory (unused_params)
    return function ()

        local prefix = "S| "    -- Name prefix for the ranges
        local offset = 1        -- Measure 1 is the very first (Not 0!)
        local song = {          -- Adapt to your song { Part name, Number of measures }
            { "Intro",    4 },
            { "A 1",      8 },
            { "B 1",     12 },
            { "A 2",      8 },
            { "B 2",     12 },
            { "Outro",    4 },
        }

        function bbtToPos(bar, beat, tick)
            local bfc = ARDOUR.DoubleBeatsSamplesConverter(Session:tempo_map(), 0)
            local calcBeat = ((bar - 1) * 4) + (beat - 1) + (tick / 1920)
            return bfc:to(calcBeat)
        end

        --- Create a range.
        -- @param name Range name 
        -- @param start Measure number where to start the range (offset)
        -- @param length Range size in measures
        -- @return The last measure number (to be used as next offset)
        function create(name, start, length)
            assert( start > 0, "Measures start at 1!")
            local rng = Session:locations():add_range(bbtToPos(start,1,0), bbtToPos(start+length,1,0))
            rng:set_name(prefix .. name)
            -- Should be Glue to the beat
            -- Should be locked?
            return start+length
        end

        -- is there a logger or something?
        print(" * - * Create Song Markers  * - * ")

        print(" Clean up ")   -- Remove previous ranges starting with "prefix ..."
        for l in Session:locations():list():iter() do 
            -- l:is_mark()
            if l:is_range_marker() and string.find (l:name(), "^" .. prefix .. "*") then
                print("Removing previous range: ", l:name())
                Session:locations():remove(l)
            end
        end

        print("Song structure:") 
        print("  # ; Start ; Length ; Name") 
        for i,v in ipairs(song) do             
            print(string.format("  %2d ; %4d ; %2d ;\t%s", i, offset, v[2], prefix .. v[1]))
            offset = create(v[1], offset, v[2])
        end

    end
end

Since I keep simplifying and improving, check the latest version here: lua · master · Bruno Vernay / Ardour-utils · GitLab

1 Like

I did add markers in one script. Can’t remember the details though.

2 Likes

There it is! It works too. Thanks.

It takes a position value, so can be used in the same way as above examples. E.g. …

Editor:add_location_mark(bbtToPos(10,1,0))

2 Likes

Thanks. I added the option to create Markers instead of Ranges. I also Locked and Glued them.

The API definitively need some clean-up and coherence.
add_localtion_mark is defined in this massive 2400 lines file ardour/luainstance.cc at master · Ardour/ardour · GitHub
and it does not return the Location (normal with the Editor API). You must search it in a second step, that may not be reliable and it is less convenient than the Session API for the Ranges.

By the way is there a kind of Try-Catch to always execute restoration and clean-up even in case of errors?? (Similar to this exception handling - How to simulate try-finally or try-except in languages that don't have them - Stack Overflow ?)