How to access the global time in Tracktion

I’m using Tracktion to build a Midi plugin, in which I am handling the event timing by myself. I quite often have to access the current time of the playhead, by doing edit.getTransport().getCurrentPosition(). In order to do this, I’m passing a reference to the Edit instances to a lot of my classes, but I would like to cut down on this. Is there any way of accessing the time globally from some static method? Something like TracktionEngine::getCurrentPlayheadTime()?

Most classes have a reference to an Edit anyway so I wouldn’t worry too much about this, if you really wanted to avoid it though you could pass through the current transport.

However, using edit.getTransport().getCurrentPosition() isn’t very accurate, it will at best be a few ms behind the actual time as it’s updated on a timer. Using the position from the current PlayHead will be more accurate but again it depends where you’re calling it from?

What you probably want is to implement your timing and MIDI message logic in a Plugin subclass and use the EditTimeWindow passed in to the process callback to determine what messages to send out in that block?

Thanks for the response. I’m not working on the audio thread here; instead I’m using a worker thread to calculate Midi events ahead of time, which the audio thread can then access later on. The worker thread knows that a particular event should happen i.e. two seconds in the future, but it has to know the playhead time in order to schedule it properly for the audio thread. I’ve been using edit.getTransport().getCurrentPosition() for this, but from your suggestion it sounds like edit.getCurrentPlaybackContext()->playhead.getPosition() will be a better option. I see a lock in there, so I’m assuming that this operation is thread safe?

I take your point that passing around an Edit& is quite common, but in my case it’s becoming a bit of a nuisance. I was thinking about cobbling together something in a SharedReferencePointer so that I can access the time globally (using the playhead method you mentioned). Would this be a Bad Idea?

Yes it’s thread safe (it’s lock-free in the new engine we’re transitioning to).

Definitely don’t use a SharedResourcePointer to an Edit. SharedResourcePointer is essentially a static object so if you’re running in a plugin you’ll share your Edit between all instances and all hell will break loose. It’s much, much better to pass things around by reference in plugins.

I still don’t fully understand how you’re managing time, any method of using time outside of the process block will be flawed as it’s not synced to anything. Really you need to react to time in the process block, schedule your events for the future and then send them out in a future process callback. That’s how MIDI clips are played back for example.

In my sequencer, an event is defined by text commands, for instance:

wait(1);
play(33);

which means, “wait one second and then play Midi note 33”. The “wait one second” part is in relative time, i.e. it will wait for one second relative to some starting point. But in order for the audio thread to be able to play it, it will have to know its absolute time (ie. executionTime = delayTime + creationTime).

For most events, the creationTime is the excutionTime of another Event. In this way, the timings chain, and can be kept accurate. (You can picture this as executionTime = delayTime1 + delayTime2 + ... + creationTime). However, there must be a starting point in the chain, which is an Event created by user input (i.e. clicking on a button). This Event has to set its creationTime as the playhead time from when the user clicked, and this is what I am trying to access with the edit.getCurrentPlaybackContext()->playhead.getPosition() function.

(Actually, typing all this out makes me realize that there is probably something wrong with my class design, since querying the playhead position shouldn’t need to happen all that often! I guess this makes my question a bit obsolete, but your input is appreciated anyway).

Well I just wanted to point out that “now” is poorly defined in a DAW/plugin.
In Tracktion Engine, edit.getCurrentPlaybackContext()->playhead.getPosition() will return the position of the Edit timeline when the previous audio callback happened.

1 Like

OK, I’ve worked out most of the errors and no longer need to be querying the playhead time from multiple locations. But what you said in the last post has me wondering whether I’m using the audio thread correctly. I have been querying the playhead time within applyToBuffer() and using that to decide when to swap in a MidiMessageArray. Is that the right way of doing it? It looks something like this:

void SequencerPlugin::applyToBuffer(const te::AudioRenderContext& context)
{
    auto activeEvents = getActiveEvents(); // This is definitely thread-safe. Events are also pre-sorted by execution time.

    for (auto& event : *activeEvents)
    {
        if (context.playhead.getPosition() >= event->getExecutionTime() && !event->hasBeenPlayed())
        {
            context.bufferForMidiMessages->swapWith(event->getMidiMessageArray());

            event->setHasBeenPlayed(true);

            break;
        }
    }
}

am I using playhead.getPosition() correctly here?

Yea I think so but you can get the timeline time directly from the AudioRenderContext can’t you?

I can’t see any other methods inside AudioRenderContext that return a double. Unless I’m missing something?

You can use getEditTime()

Also, what branch are you on? As we’re switching to the new engine there have been some changes to that class. It now passes you a PluginRenderContext which contains an editTime member.

Yeah I’m on an old old branch. (Still using Juce 5 also!) I’ll check out editTime() and update my libraries. Thanks for your help.