ppqLoopStart/End not provided by AU wrapper


Is there a particular reason why ppqLoopStart/End values are not filled at all by the AU wrapper? I had problems detecting the loop range in Logic Pro X, and I found out that these values are simply never set by the JUCE AU wrapper.

The information is, however, provided by the AU CallHostTransportState function. And in fact, if I simply put these values into the JUCE playhead, my code works fine.

So shouldn't the highlighted code below be added? (plus some error handling)


bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) override


        info.timeSigNumerator = 0;

        info.timeSigDenominator = 0;

        info.editOriginTime = 0;

        info.ppqPositionOfLastBarStart = 0;

        info.isRecording = false;

        info.ppqLoopStart = 0;

        info.ppqLoopEnd = 0;

        switch (lastTimeStamp.mSMPTETime.mType)


            case kSMPTETimeType24:          info.frameRate = AudioPlayHead::fps24; break;

            case kSMPTETimeType25:          info.frameRate = AudioPlayHead::fps25; break;

            case kSMPTETimeType30Drop:      info.frameRate = AudioPlayHead::fps30drop; break;

            case kSMPTETimeType30:          info.frameRate = AudioPlayHead::fps30; break;

            case kSMPTETimeType2997:        info.frameRate = AudioPlayHead::fps2997; break;

            case kSMPTETimeType2997Drop:    info.frameRate = AudioPlayHead::fps2997drop; break;

            //case kSMPTETimeType60:

            //case kSMPTETimeType5994:

            default:                        info.frameRate = AudioPlayHead::fpsUnknown; break;


        if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr)


            info.ppqPosition = 0;

            info.bpm = 0;


        UInt32 outDeltaSampleOffsetToNextBeat;

        double outCurrentMeasureDownBeat;

        float num;

        UInt32 den;

        if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den,

                                         &outCurrentMeasureDownBeat) == noErr)


            info.timeSigNumerator   = (int) num;

            info.timeSigDenominator = (int) den;

            info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat;


        double outCurrentSampleInTimeLine, outCycleStartBeat, outCycleEndBeat;

        Boolean playing = false, looping = false, playchanged;

        if (CallHostTransportState (&playing,





                                    &outCycleEndBeat) != noErr)


            // If the host doesn't support this callback, then use the sample time from lastTimeStamp:

            outCurrentSampleInTimeLine = lastTimeStamp.mSampleTime;


        info.isPlaying = playing;

        info.timeInSamples = (int64) (outCurrentSampleInTimeLine + 0.5);

        info.timeInSeconds = info.timeInSamples / getSampleRate();

        info.isLooping = looping;


        info.ppqLoopStart = outCycleStartBeat; // ADDED

        info.ppqLoopEnd = outCycleEndBeat; // ADDED

        return true;



Hmm.. Interesting.. Thanks for the heads-up, I'm actually quite puzzled that it didn't already do that - the original code was added 4 years ago so I don't remember the details, but I suspect it might be because there's no info about exactly what the value means, so it was unclear as to whether "beat" means "ppq" in this context.

But, since the top result googling for that parameter name returns this forum thread, I guess there's not a lot of people out there who are using it differently, so I'm going to take an executive decision and implement this.

I'll also add it in the hosting side too, so that the same values get passed through from juce hosts.

Anyone from Apple reading this who knows whether we're misinterpreting the meaning of that value, please let us know!


Indeed difficult to find documentation about this, but at least we've verified that the values correspond nicely to the ppqPosition value.

And the ppqPosition is retrieved from the CallHostBeatAndTempo() function that also has the same nomeclature - "outCurrentBeat" (compared to "outCycleStartBeat" and "outCycleEndBeat").

So I would say there is very little risk of misinterpretation here.

Glad to hear you will implement this!