Timing on the transport in external (min-devkit)
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
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};
}
// ...
};
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;
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
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