How to timestamp midi events in processBlock when not playing?

 

I'm trying to write a VST that displays midi events in a timeline.  So when the events appear in processBlock I calculate a timestamp for them using AudioPlayHead::CurrentPositionInfo.

However, if I want to just "monitor" realtime incoming events (and display them on my timeline) when the Host isn't playing/recording, I can't use AudioPlayHead::CurrentPositionInfo because its not changing since its not "playing".

What should I use to calculate the time of a midi event? Time::getMillisecondCounterHiRes()  when in processBlock?

Or is there a better way? I know in a windows app when you get a midi event it is time stamped by the midi driver which is more accurate than timestamping them yourself. Is there some way in a vst to retrieve that timestamp?

Any help is appreciated!

thanks,

Gord

 

 

MidiMessage::getTimeStamp ?

 

http://www.juce.com/juce/api/classMidiMessage.html#a9a942c96a776e80e3c512058b29011a8

 

MidiMessage::getTimeStamp in processBlock values  are relative sample offsets from the start of the audio buffer. Suppose you are using a 32 sample audio buffer then the timeStamp will be values from 0 to 31. 

So from MidiMessage::getTimeStamp you know where the time location of midi events in the midibuffer reltaive to one another but not their absolute location.

Yeah, this is confusing...

 

This will only work for events from MIDI devices, if you want to time events inside the processBlock coming inside the MidiBuffer the only meaningful inficator is the sample number.

Hi Gord,

you need to increment your own sample counter while the host continues to call processBlock() (albeit w/o moving the playhead anymore). Have your own timeline, computed from the sample counter and sample rate, that continues to move while the host has stopped.

This works fine for me.

 

1 Like

 

Thanks. This is helpful. I guess the host is constantly calling processblock every "cycle" of the audio buffer? For example, if the audio buffer is 32 samples the host is calling processBlock every 32 samples (whether its playing or not)?

 

 

 

 

 

The block size can change between calls if i remember, but yes the MIDI timestamp in processBlock is the sample number inside the audio block you get, and since you get the block of audio the position and the samplerate you can calculate a relative time of that event (relative to the transport start)

 

Here's the code I used in processBlock to timestamp midi events (note ons in my case) even when the audioplayhead isn't playing:


void GmetAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    for (int channel = 0; channel < getNumInputChannels(); ++channel)
    {
        float* channelData = buffer.getSampleData (channel);
        // ..do something to the data...
    }
    // In case we have more outputs than inputs, we'll clear any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
    {
        buffer.clear (i, 0, buffer.getNumSamples());
    }

    // ask the host for the current time so we can display it...
    AudioPlayHead::CurrentPositionInfo newTime;
    if (getPlayHead() != nullptr && getPlayHead()->getCurrentPosition (newTime))
    {
        // Successfully got the current time from the host..
        lastPosInfo = newTime;
    }
    else
    {
        // If the host fails to fill-in the current time, we'll just clear it to a default..
        lastPosInfo.resetToDefault();
    }

    double msPerSample = 1000 / getSampleRate();
    
    if (!midiMessages.isEmpty()) {
        int i;
        MidiMessage m;
        MidiBuffer::Iterator it(midiMessages);
        while(it.getNextEvent(m, i)) {
            if (m.isNoteOn()) {
                m.setTimeStamp(currentTime + i * msPerSample);
                midiMessageSequence.addEvent(m, 0);
                gotMidi = true;
            }
        }
    }
    

// keep track of time even whe the host isn't playing (set currentTime = 0 in prepareToPlay)
    currentTime += msPerSample * buffer.getNumSamples();
}

 

 

cheers,
Gord


 

4 Likes