How to loop midi file

OK, I got it finally running. Thanks for all your support! Here the code of the processBlock() function, if anybody faces the same or similar situation. Since I am a beginner in C++ there might be a more efficient or better way to implement this. If you have any suggestions to improve the code, I would appreciate.

void SimpleMidiplayerAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    // We clear all the incoming audio here
    for (auto i = 0; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    AudioPlayHead::CurrentPositionInfo thePositionInfo;
    getPlayHead()->getCurrentPosition(thePositionInfo);

    if (numTracks.load() > 0)
    {
        // The MIDI file is played only when the transport is active
        if (thePositionInfo.isPlaying)
        {
            const MidiMessageSequence *midiSeq = finalMidiFile.getTrack(currentTrack.load());
            auto startTime = thePositionInfo.timeInSeconds;              
            double loopOffset = fmod(startTime, midiSeq->getEndTime());
            loop = startTime - loopOffset;
            auto sampleStartTime = startTime - loop;
            
            if (lastSampleStartTime > sampleStartTime)
                sampleStartTime = 0.0; // set to 0 if new loop starts
            
            lastSampleStartTime = sampleStartTime;
            auto sampleEndTime = sampleStartTime + buffer.getNumSamples() / getSampleRate();
            auto sampleLength = 1.0 / getSampleRate();

            // If the transport bar position has been moved by the user or because of looping
            if (std::abs(sampleStartTime - nextStartTime) > sampleLength && nextStartTime > 0.0)
                sendAllNotesOff(midiMessages);

            nextStartTime = sampleEndTime;

            // If the MIDI file doesn't contain any event anymore
            if (isPlayingSomething && sampleStartTime >= midiSeq->getEndTime())
            {
                sendAllNotesOff(midiMessages);
            }
            else
            {
                // Called when the user changes the track during playback
                if (trackHasChanged)
                {
                    trackHasChanged = false;
                    sendAllNotesOff(midiMessages);
                }
                int curTranspose = transpose, lastTranspose = transpose;
                
                // Iterating through the MIDI file contents and trying to find an event that
                // needs to be called in the current time frame
                for (auto i = 0; i < midiSeq->getNumEvents(); i++)
                {
                    MidiMessageSequence::MidiEventHolder event = *midiSeq->getEventPointer(i);

                    if (event.message.getTimeStamp() >= sampleStartTime && event.message.getTimeStamp() < sampleEndTime)
                    {
                        auto samplePosition = roundToInt((event.message.getTimeStamp() - sampleStartTime) * getSampleRate());
                        midiMessages.addEvent(event.message, samplePosition);
                        
                        /* to avoid that the first element of the next loop will be missed because it has to be sent in the same time frame, send it in the same time frame.
                        Needs to be improved:
                        - only send the first event, if it really is part of the same time frame
                        - could also be more the one event.
                        */
                        if(fmod(startTime, midiSeq->getEndTime()) > fmod(sampleEndTime, midiSeq->getEndTime()))
                        {
                            MidiMessageSequence::MidiEventHolder event2 = *midiSeq->getEventPointer(0);
                            auto samplePosition = roundToInt((event2.message.getTimeStamp()) * getSampleRate());
                            midiMessages.addEvent(event2.message, samplePosition);
                        }

                        isPlayingSomething = true;
                    }
                }
            }
        }
    }
    else
    {
        // If the transport isn't active anymore
        if (isPlayingSomething)
            sendAllNotesOff(midiMessages);
    }
}
2 Likes