Playing Midi notes or audio samples at precise times(Novice)

Hi everyone,

I am a beginner to Tracktion and JUCE, I done a couple tutorials, watched some videos and read some of the documentation. I am working on a project for University and I am trying to play MIDI notes (or audio samples) at precise times (<10ms latency ) over a period of a set time. For example

(100ms, 300ms, 600ms) and period 1000ms

And then looping that period so events would play at

100ms ,300ms 600ms, 1100ms, 1300ms, 1600ms….

Or with
(100ms, 300ms, 600ms) and period 900ms

100ms, 300ms, 600ms, 1000ms, 1200ms, 1500ms, 1900ms

I am looking for a starting point of which classes I should be using to store the time sequence and how to create midi events at precise times. And or perhaps a demo/example that would have some required components so I can try and dissect it.

The MIDI Recording Demo is probably the best place to start as it shows how to record MIDI in to a clip and play it back. It also deals with transport looping etc.

Maybe take a look there first and then we can help with any further questions you might have.

By MIDI Recording Demo, did you mean “Handling MIDI events” tutorial example or one of the examples in “examples” folder under the JUCE directory tree?

I believe he was referring to the tracktion engine midi recording demo. You can view all the tracktion examples here: https://github.com/Tracktion/tracktion_engine/tree/master/examples

Thanks for the response, yes it was the midi recording Demo in the examples folder. I have two different methods from other forum posts. I am trying to decide which approach would be best.

One the just add notes every 5 beats

    auto track = tracktion_engine::getAudioTracks(*edit)[trackNumber];

    if (dynamic_cast<tracktion_engine::MidiClip*> (track->getClips()[0]) == nullptr)
    {
        midiClipPtr = dynamic_cast<tracktion_engine::MidiClip*>
            (track->insertNewClip(tracktion_engine::TrackItem::Type::midi, { 0, 9999 }, nullptr));
        
            for (double t = 0;t < 10; t++)
            {
                double beats = edit->tempoSequence.timeToBeats(t);
                midiClipPtr->getSequence().addNote(55, beats, 0.5, 127, 0, nullptr);
            }
        }

and one that injects message when a button is clicked (i will change this to a callback)

    dur = juce::MidiMessage::noteOn(1, 67, (juce::uint8) 100);`
    track->injectLiveMidiMessage(dur, tracktion_engine::MidiMessageArray::notMPE);

To re-iterate my goal i have a set of times i want to play notes over a set peroid. eg( adding notes to a midiClip sequence at certain times and looping over that period) eg notes at 2,5 and 8 seconds looping every 10 seconds

In regards to the first method
My first question is, adding the notes to a sequence at appropriate times before played seem easy enough at the moment. However, I want the users to beat able make changes to the sequence in real time. What are the factors i need to consider, should i be removing the sequence and replace with new sequence or try and edit/change values in the sequence?

In regards to the second method trying to create callbacks to only insert notes are precise times.
Are callbacks time accurate ?
Will having a lot (say Hundreds )of timer callbacks be resource heavy ?
Will they interfere with each other if they occur at the same time, or are/could they be on different threads?

You can get the MidiList from the MidiClip and then edit the notes in there if you need to change them. That’s how an piano-roll based MIDI editor would work in a GUI editor.

injectLiveMidiMessage is a message-thread only method so you can’t call it from background threads. Because the method is called from the message thread and then cued to be be played back from the audio thread there will be some inherent jitter. Both from where the audio callback happens to be when the message is added to the buffer to be played, and from the un-precise nature of UI callbacks.