Timing of Midi Notes

#1

Hey everyone.

I am new to Juce and tried to answer my question by reading other forum threads and other code, but i wasn’t able to fix the following problem:

I’m trying to expand the arpeggiator from the built-in plugin examples to be synced in e.g. quarter notes.

I am calculating the note length from the sampleRate and the AudioPlayHead::getCurrentPosition BPM.

I figured out, how to calculate the offset of a note in samples, and afaik this has to be passed to the MidiBuffer::addEvent() method.

I logged everything and all the numbers are correct, the AudioPlayHead::positionInfo timeInSamples minus my offset are always a multiple of the calculated note length. (In my case a quarter is 22050 Samples long, 120 bpm at 44100 Hz)

Still though, if i record the midi output of my plugin to a track in Ableton, the notes are placed slightly off the grid, not audible, but visible.

What am i getting wrong? Is there some kind of latency involved, that has to be taken into calculation?

Thanks in advance,
Lennart

PS:
I also tried by getting into the code of LibreArp, an open-source Arppegiator made with Juce, but when you record the output of LibreArp to a Midi Track in Ableton, the notes are not exactly on also.

0 Likes

#2

“not audible” sounds like the notes have a velocity of 0, which makes them silent.

Are the notes always off the grid by the same amount of time? If yes, you might be able to get them on the grid by tweaking the track delay in Ableton Live directly.

0 Likes

#3

Thanks for your reply McMartin!

No, by not audible i mean the Off-Beat-“ness”.

And every note is slightly different in this case.
Using DAW specific configuration is actually no option in regards of planning to build a sellable product :smiley:

0 Likes

#4

Are you sure your sample offsets in MIDI messages are correct?

You can monitor incoming/outgoing MIDI messages with this little function:

void verbose(AudioProcessor &processor, const MidiBuffer &messages)
{
    MidiBuffer::Iterator    it(messages);
    MidiMessage             msg;
    int                     samplePosition = 0;
    
    if (auto *playHead = processor.getPlayHead())
    {
        AudioPlayHead::CurrentPositionInfo info;
        
        playHead->getCurrentPosition(info);
        
        double bpm = info.bpm;
        
        while (it.getNextEvent(msg, samplePosition))
        {
            double beats = info.ppqPosition + double(samplePosition / processor.getSampleRate()) * bpm / 60.0;
            DBG("Beats: " << String::formatted("%4f", beats) << " " << msg.getDescription());
        }
    }
}
0 Likes