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.

Hello, I’m returning to this after a while. I’ve updated to the latest Master version of Tracktion, but I’m having some trouble getting things to function as expected. Just as a reminder, I’m trying to access the current global timeline value as a double.

Inside the audio thread, I can do pluginRenderContext.editTime. This works fine.

Outside the audio thread, I used to do this:

edit.getCurrentPlaybackContext()->playhead.getPosition().

But this method doesn’t seem to be available anymore in the latest branch. I tried doing this:
edit.getCurrentPlaybackContext()->getPosition();

but it’s only ever returning 0. Can someone advise? I’d like to find a method which I can call from any thread which will give me the (estimate of) the current playback time.

You probably want EditPlaybackContext::getPosition().
I’d also use the develop branch of the engine.

This method is only returning 0.0 for me. Elsewhere, I’ve initialized it like this:

transport.setLoopRange(tracktion_engine::Edit::getMaximumEditTimeRange());
transport.looping = true;
transport.position = 0.0;
transport.play(true);

Am I missing something, so that the transport is actually off?

That looks correct… Are you hearing any audio output?

Yes, I can get audio output.

Can you step in to EditPlaybackContext::getPosition() to see where it’s failing and just returning 0.0?

The atomic inside PlayHead::getPosition() seems to load correctly. I can chase it all the way into the function

PlayHead::referenceSamplePositionToTimelinePositionUnlooped (int64_t referenceSamplePosition) const

There, after a bit of arithmetic, the expression is multiplied by speed, which has a value of 0. That seems to be the culprit.

If speed it 0 it means it’s not playing.
Speed is set to 1.0 when play starts. So maybe you’re trying to get the position before playback has actually started?

No, this is called dynamically as the program runs. Anything else I can try? I’ll update to the Develop branch tomorrow to see if that fixes anything.