Current sample index in AudioPlayHead::CurrentPositionInfo?


In my implementation of the AudioProcessor::processBlock function, I’m trying to get the accurate sample position of the processed block (relatively to the timeline of the host).

I observe that such information is obtained in the VST, RTAS, AudioUnit wrappers, however these are only transitory info, and are not made available to the final AudioProcessor which is available to the juce library user (plug-in developer).

In VST, this is available through the getTimeInfo call (see juce’s VST wrapper).
In RTAS, this is available through the GetCurrentRTASSampleLocation call (or GetCurrentTDMSampleLocation if not playing) (see juce’s RTAS wrapper).
In AudioUnit, this is available through the CallHostTransportState call (see juce’s AudioUnit wrapper).
In AAX, this should be available through a GetCurrentNativeSampleLocation call (not double checked yet).

Is it possible to add such a “current sample location” field into the AudioPlayHead::CurrentPositionInfo structure so that the getCurrentPosition() calls of the various wrappers can deliver this info to the AudioProcessor class?

Thanks for your support! All the best

Good request… Haven’t time right now to code it myself, but if you’re in a hurry and want to suggest some changes, I’d be happy to sanity-check and merge them in!

Well, I did not understand where I could add an equivalent bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) method in the AAX wrapper, so I only added that to the VST, RTAS and AU wrappers.

[code]Index: modules/juce_audio_plugin_client/AU/

— modules/juce_audio_plugin_client/AU/ (revision 10937)
+++ modules/juce_audio_plugin_client/AU/ (working copy)
@@ -469,6 +469,7 @@
info.timeSigNumerator = 0;
info.timeSigDenominator = 0;
info.timeInSeconds = 0;

  •    info.timeInSamples = 0;
       info.editOriginTime = 0;
       info.ppqPositionOfLastBarStart = 0;
       info.isPlaying = false;

@@ -521,6 +522,7 @@
info.isPlaying = playing;
info.timeInSeconds = outCurrentSampleInTimeLine / GetSampleRate();

  •        info.timeInSamples = outCurrentSampleInTimeLine;
       return true;

Index: modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp

— modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp (revision 10937)
+++ modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp (working copy)
@@ -717,7 +717,13 @@
midiTransport->GetCurrentTDMSampleLocation (&sampleLocation);

         midiTransport->GetCustomTickPosition (&ticks, sampleLocation);
  •        info.timeInSamples = sampleLocation;
  •    else
  •    {
  •        info.timeInSamples = 0;
  •    }
       info.bpm = bpm;
       info.timeSigNumerator = num;

Index: modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp

— modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp (revision 10937)
+++ modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp (working copy)
@@ -683,6 +683,7 @@

     info.timeInSeconds = ti->samplePos / ti->sampleRate;
  •    info.timeInSamples = ti->samplePos;
       info.ppqPosition = (ti->flags & kVstPpqPosValid) != 0 ? ti->ppqPos : 0.0;
       info.ppqPositionOfLastBarStart = (ti->flags & kVstBarsValid) != 0 ? ti->barStartPos : 0.0;

Index: modules/juce_audio_processors/processors/juce_AudioPlayHead.h

— modules/juce_audio_processors/processors/juce_AudioPlayHead.h (revision 10937)
+++ modules/juce_audio_processors/processors/juce_AudioPlayHead.h (working copy)
@@ -74,6 +74,9 @@

     /** The current play position, in seconds from the start of the edit. */
     double timeInSeconds;
  •    /** The current play position, in samples from the start of the edit. */
  •    int64 timeInSamples;
       /** For timecode, the position of the start of the edit, in seconds from 00:00:00:00. */
       double editOriginTime;


Cool, thanks, that looks pretty straightforward, I’ll do something like along those lines shortly…

(I’ve not finished the AAX code, which is why it doesn’t yet have any playhead stuff)

Hey Jules, should we also update and juce_VSTPluginFormat.cpp then?

    OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged,
                                Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
                                Float64* outCycleStartBeat, Float64* outCycleEndBeat)
        AudioPlayHead* const ph = getPlayHead();
        AudioPlayHead::CurrentPositionInfo result;

        if (ph != nullptr && ph->getCurrentPosition (result))
            if (outIsPlaying != nullptr)
                *outIsPlaying = result.isPlaying;

            if (outTransportStateChanged != nullptr)
                *outTransportStateChanged = result.isPlaying != wasPlaying;
                wasPlaying = result.isPlaying;

            //CHANGE THIS LINE
            if (outCurrentSampleInTimeLine != nullptr)
                *outCurrentSampleInTimeLine = result.timeInSamples;//roundToInt (result.timeInSeconds * getSampleRate());


void VSTPluginInstance::processBlock (AudioSampleBuffer& buffer,
                                      MidiBuffer& midiMessages)
    const int numSamples = buffer.getNumSamples();

    if (initialised)
        AudioPlayHead* playHead = getPlayHead();

        if (playHead != nullptr)
            AudioPlayHead::CurrentPositionInfo position;
            playHead->getCurrentPosition (position);

            //ADD THIS LINE
            vstHostTime.samplePos = position.timeInSamples;
            vstHostTime.tempo = position.bpm;
            vstHostTime.timeSigNumerator = position.timeSigNumerator;
            vstHostTime.timeSigDenominator = position.timeSigDenominator;
            vstHostTime.ppqPos = position.ppqPosition;

Ah yes, good point, thanks!

For RTAS I made the following changes:

For the structure CurrentPositionInfo I added:

and in juce_RTAS_Wrapper.cpp I changed:

[code]bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info)
// this method can only be called while the plugin is running
jassert (prepared);

    Cmn_Float64 bpm = 120.0;
    Cmn_Int32 num = 4, denom = 4;
    Cmn_Int64 ticks = 0;
    Cmn_Bool isPlaying = false;

    // Moved sampleLocation for scope & check the value for mRTGlobals->mHWBufferSizeInSamples :

    Cmn_Int64 sampleLocation;

    const Cmn_UInt32 bufferSize = mRTGlobals->mHWBufferSizeInSamples;

    if (midiTransport != 0)
        midiTransport->GetCurrentTempo (&bpm);
        midiTransport->IsTransportPlaying (&isPlaying);
        midiTransport->GetCurrentMeter (&num, &denom);

        // (The following is a work-around because GetCurrentTickPosition() doesn't work correctly).
        // Moved for scope...
        // Cmn_Int64 sampleLocation;

        if (isPlaying)
            midiTransport->GetCurrentRTASSampleLocation (&sampleLocation);
            midiTransport->GetCurrentTDMSampleLocation (&sampleLocation);
        // Adjust the sampleLocation value by twice the HW buffer size
        sampleLocation = sampleLocation + (bufferSize * 2);

        midiTransport->GetCustomTickPosition (&ticks, sampleLocation);

    info.bpm = bpm;
    info.timeSigNumerator = num;
    info.timeSigDenominator = denom;
    info.isPlaying = isPlaying;
    info.isRecording = false;
    info.ppqPosition = ticks / 960000.0;
    info.ppqPositionOfLastBarStart = 0; //xxx no idea how to get this correctly..
    info.isLooping = false;
    info.ppqLoopStart = 0;
    info.ppqLoopEnd = 0;

    info.currentSampleLocation = sampleLocation;  


    return true;

In Pro Tools this now displays the accurate sample location of the insertion point.

One other change I made to the plugin demo was to fix the rounding error in the Bars/Beats display in ppqToBarsBeatsString()

The bars/beats display in the demo then matches the Pro Tools timeline with a fixed meter.


Did you see that I’d already made the changes we discussed in this thread? What you’re posting here just looks like a different version of the stuff I’ve already added…

No, just getting going today and hadn’t realized you’d already made the changes… will update to the latest tip right now.