Timing on the transport in external (min-devkit)

robotic-audio's icon

Hi Maxers,

I'm writing some externals to be used inside Max4Live objects. I would like to time events based on the Transport. Right now I'm reading the transport time using the [when] object and then passing the ticks into my external using a [metro] -but I find that a bit clumsy and not very efficient. If there a way to interface the transport directly in c++ ?

Thanks,

Best Hjalte

JBG's icon

Hi,

Sure, it's possible. There are no min wrappers for it afaik, but you can access it through the ITM time objects in the Max SDK.

I have a simple wrapper that I wrote a couple of years ago. It's taken out of context so I wouldn't expect it to work as it is, but it should provide you everything you need to to implement your own solution

#include "c74_min_api.h"
// ...

class MaxTimePoint {
public:
    // ...

    static std::optional<MaxTimePoint> get_time_of(const c74::min::atom& clock_name) {
        c74::max::t_itm* clock_source;
        if (parsing::is_empty_like(clock_name)) {
            clock_source = (c74::max::t_itm*) c74::max::itm_getglobal();
        } else {
            clock_source = (c74::max::t_itm*) c74::max::itm_getnamed(clock_name, nullptr, nullptr, 0L);
        }

        if (clock_source) {
            auto tempo = itm_gettempo(clock_source);
            auto ticks = itm_getticks(clock_source);
            auto [bars, beats, units] = get_bar_beat_units(*clock_source, ticks);
            auto [numerator, denominator] = get_time_signature(*clock_source);
            auto state = itm_getstate(clock_source);

            return MaxTimePoint{tempo, ticks, bars, beats, units, numerator, denominator, state};
        }

        return std::nullopt;
    }

private:
    static std::tuple<long, long, double> get_bar_beat_units(c74::max::t_itm& clock, double ticks) {
        long bars;
        long beats;
        double units;
        itm_tickstobarbeatunits(&clock, ticks, &bars, &beats, &units, (char) c74::max::TIME_FLAGS_LOCATION);
        return {bars, beats, units};
    }


    static std::pair<long, long> get_time_signature(c74::max::t_itm& clock) {
        long numerator;
        long denominator;
        itm_gettimesignature(&clock, &numerator, &denominator);
        return {numerator, denominator};
    }

    // ...

};

robotic-audio's icon

Hi JBG :)

Thank you so much for pointing me in the right direction! But I have a problem:

When have the following code in my external.cpp file it works correctly -when the object is running in plain max. But if I include the object in a Max4Live device it no longer works... Is the Live transport using some secret name or what do I need to do to make it work inside live?

// Get the Global Transport
c74::max::t_itm* clock_source = (c74::max::t_itm*) c74::max::itm_getglobal();
// Get the ticks
auto itm_ticks = itm_getticks(clock_source);
// Get the tempo
double itm_tempo =  itm_gettempo(clock_source);
// Get the song position in beats
auto itm_beats = itm_ticks / 480.0;

// Print
cout << "itm_tempo: " << itm_tempo << " itm_ticks: " << itm_ticks << " itm_beats: " << itm_beats << endl;

JBG's icon

Ahh right, sorry I haven't tried this in Live at all.

Back in the days, we used to specify the clock source explicitly in live (e.g. [transport @clocksource live]), so if you're lucky it might be possible to just write

clock_source = (c74::max::t_itm*) c74::max::itm_getnamed("live", nullptr, nullptr, 0L);

but if that doesn't work, then I'm afraid that Live isn't using the ITM objects at all, but its own (private/inaccessible) mechanism, and in that case you're kind of stuck with your initial solution

robotic-audio's icon

Hi JBG,

I did try that, just because I thought it seemed like the most possible solution, but unfortunately It did not work. I will stick with my current way of doing it for now, but i'm thinking it must be possible.

Thank you again!

Best,

Hjalte