Question about Ableton link

When I activate the ableton link in my app, how should I behave in the callback for the bpm change ?

void linkRequestedTempoChange(double newBpm)
{

    auto pos = toBeats(trasnport->getPosition(), edit->tempoSequence);

    edit->tempoSequence.getTempoAt(pos).setBpm(newBpm);

}

or

void linkRequestedTempoChange(double newBpm)
{

    auto pos = toBeats(transport->getPosition(), edit->tempoSequence);

    edit->tempoSequence.insertTempo(pos,newBpm,1.f);

}

I ask this question since, in the first case, I notice that sometimes when I set the newBpm this makes the position in beat of the transport go back a little bit in time from what I had before setting the newBpm.
However, if I activate the audio metronome it happens that a few clicks feel doubled as I think what happens is that :

  1. the audio engine makes the metronome click stroke heard.
  2. I set a newBpm via the ableton link callback which takes me a few fractions of a beat back
  3. I play the same click stroke again since then the transport is advanced going back to point 1)

In the second case this does not happen but I add too many points to the tempo sequence, which causes the application to crash.

@dave96 suggestions ?

So I just checked in Waveform and we do:

            e->tempoSequence.getTempoAt (e->getTransport().getPosition()).setBpm (newBpm);
            lastTempoChangeTimeFromLink = std::chrono::steady_clock::now();
            lastTempoFromLink = newBpm;

We keep track of the time of the change and the tempo to avoid sending this change back to Link. Perhaps that’s where your jump is coming from?

You definitely don’t want to insert a new tempo each time as if you loop that will cause all kinds of havoc.

@dave96 where you run this code ? In which class ?

 e->tempoSequence.getTempoAt (e->getTransport().getPosition()).setBpm (newBpm);
            lastTempoChangeTimeFromLink = std::chrono::steady_clock::now();
            lastTempoFromLink = newBpm;

and those class variables “lastTempoChangeTimeFromLink” and “lastTempoFromLink” how do they avoid sending it back to the link?

It’s Waveform code so not public. But it’s in our te::AbletonLink::Listener subclass.

    void linkRequestedTempoChange (double newBpm) override
    {
        if (auto e = editRef.get())
        {
            e->tempoSequence.getTempoAt (e->getTransport().getPosition()).setBpm (newBpm);
            lastTempoChangeTimeFromLink = std::chrono::steady_clock::now();
            lastTempoFromLink = newBpm;
        }
    }

We then poll our own tempo on a 25Hz timer to push tempo changes in our Edits to the Link session:

    void updateTempo()
    {
        if (auto e = editRef.get())
        {
            const auto now = std::chrono::steady_clock::now();

            if ((now - lastTempoChangeTimeFromLink) < 500ms)
                return;

            const auto currentTempo = e->tempoSequence.getTempoAt (e->getTransport().getPosition()).bpm.get();

            if (juce::isWithin (lastTempoFromLink, currentTempo, 0.01))
                return;

            e->getAbletonLink().requestTempoChange (currentTempo);
        }
    }

Thanks @dave96 for the quick response but it’s not my case.

In want to implement a system where 2 apps is connected via ableton link.
The App1 is an ableton link master (sends the tempo changes) and the App2 is only an ableton link slave app (receives the tempo changes and set the bpm).
So the App2 never sends it’s bpm via the ableton link but it can only receives from the App1.

this code :

void linkRequestedTempoChange(double newBpm)
{

    auto pos = toBeats(trasnport->getPosition(), edit->tempoSequence);

    edit->tempoSequence.getTempoAt(pos).setBpm(newBpm);

}

Is implemented in the App2 so it can set it’s bpm according the the master App1.

The problem is that sometimes it could happen that situation :

auto pos = toBeats(trasnport->getPosition(), edit->tempoSequence);
edit->tempoSequence.getTempoAt(pos).setBpm(newBpm);
auto posAfter = toBeats(trasnport->getPosition(), edit->tempoSequence);

Where posAfter is less than pos.

Getting the BarsAndBeat poison of pos and posAfter it could happen that the posAfter.getWholeBeats() < pos.getWholeBeats().

So, I think the audio engine plays a metronome sound and then plays it again (because it went a wholeBars behind).

That shouldn’t be the case. setBpm will adjust all the clips and automation to the new tempo.

If you look at checkForTempoSequenceChanges you’ll see if the tempo sequence has changed, the playhead position is overriden so that the beat position is the same.

Be careful of calling trasnport->getPosition() twice on the message thread, there could well have been multiple audio callbacks in between them.

Have you tried just changing the bpm outside of using Link? Is that smooth?

Have you tried just changing the bpm outside of using Link? Is that smooth?

Yes @dave96.

I tried to implement a timer at 25hz that does this :

auto currentBpm = edit->tempoSequence.getTempoAt(transport->getPosition()).getBpm();

auto rand = Random::getSystemRandom().nextDouble() / 10.0;  //between 0 - 0.1
auto plusOrNot = Random::getSystemRandom().nextDouble() > 0.5 ? 1.0 : -1.0;

auto newBpm = currentBpm + (rand * plusOrNot);

edit->tempoSequence.getTempoAt(tranport->getPosition()).setBpm(newBpm);

And I can still hear the metronome doubled very often.

Instead if I use “edit->tempoSequence.insertTempo(…);” the things seem to be better.

If I have this setup in Waveform and just use the tempo slider at the bottom, the metronome seems fine:

Have you tried this in Waveform at all?


I know this isn’t ideal but can I check what TE license you hold?
If this is going to take a lot of time to look in to and try and replicate, it will need to be prioritised alongside paying customers as per our licensing announcement:

Hi @dave96.
I purchased a tracktion INDIE license (#1190-1744198249 ).

Yes, on waveform I tried moving the tempo slider by changing the bpm by a little and, keeping the audio metronome on, it didn’t seem to work well

Hi @dave96

I have switched to tracktion 3.2.0.

I confirm that using this code:

void linkRequestedTempoChange(double newBpm)
{

    auto pos = toBeats(trasnport->getPosition(), edit->tempoSequence);

    edit->tempoSequence.getTempoAt(pos).setBpm(newBpm);

}

Keep hearing the bpm doubled due to various flickering.

I’m attaching a gif to help you visualize the problem better
Flicker metronome

I would have attached a video with audio so you can hear the flickering too but, with the new tracktion, another problem arose.

If I have the edit’s audio metronome active, after a short while I play and the bpm starts to vary I get this error:

With this stake trace:

If I turn off the metronome in the edit this doesn’t happen anymore.
I need help because this is very limiting.

Thanks in advance @dave96 .

Updates :
@dave96
I tried to use Waveform 13 by activating Ableton Link and, with linkHut (compilable from the official Ableton repo link link/examples at master · Ableton/link · GitHub) and activating the metronome I noticed that the app crashes.
waveform13 crash

Ok thanks for the extra info.
I will bump this up the priority list.

@dave96 thank u.
Keep me updated on it.

Hi @dave96 .
Any news ?