Loading midifile it's slow (for acceptable use)

Hi, luckyliuk

I rewrote the MidiPlayer class just now. Still not mature enough, but it can play normal MIDI files and also be able to handle the tempo change. You can further refine it. Hope inspiration and help for your. Thanks.

MidiPlayer.h

/*
================================================================================
            MIDI Playback

    Author: SwingCoder
    Email: SwingCoder2@gmail.com
    Github: https://github.com/SwingCoder

    License: MIT License, Copyright (c) 2012 by SwingCoder
================================================================================
*/

#ifndef __MIDIPLAYER_H_
#define __MIDIPLAYER_H_

/** Used to playback MIDI file.

    Just creat it and call playMidi().
    
    @code
    midiPlayer = new MidiPlayer(MidiOutput::openDevice(0));
    midiPlayer->playMidi(midiFile);
    @endcode
*/
//==============================================================================
class MidiPlayer : private Thread
{
public:
    MidiPlayer(MidiOutput* midiDevice);
    ~MidiPlayer();

    void playMidi(File& file);
    void stopMidi();

private:
    MidiOutput* midiOutput;
    MidiFile midiFile;
    MidiMessageSequence midiMessageSequence;

    void run();
    void noteOffAndReset();
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiPlayer)
};

#endif  // __MIDIPLAYER_H_

MidiPlayer.cpp

#include "../JuceLibraryCode/JuceHeader.h"
#include "MidiPlayer.h"

//===============================================================================
MidiPlayer::MidiPlayer (MidiOutput* midiDevice) : Thread("MidiPlayer"), 
    midiOutput(midiDevice)
{ 
    jassert (midiOutput != nullptr)
}
//=================================================================================
MidiPlayer::~MidiPlayer()
{
    signalThreadShouldExit();
    waitForThreadToExit(2000);

    deleteAndZero(midiOutput);
}
//=================================================================================
void MidiPlayer::playMidi(File& file)
{
    stopMidi();
    midiMessageSequence.clear();

    // get MidiFile..
    FileInputStream fileInputStream(file);
    midiFile.readFrom (fileInputStream); 

    const MidiMessageSequence* currentTrack;

    // add all sequence of the file..
    for (int track = 0; track < midiFile.getNumTracks(); track++) 
    {
        currentTrack = midiFile.getTrack(track);
        midiMessageSequence.addSequence(*currentTrack, 0, 0, currentTrack->getEndTime());
    }

    midiMessageSequence.updateMatchedPairs();

    startThread(8);
}

//=================================================================================
void MidiPlayer::stopMidi()
{   
    noteOffAndReset();
    signalThreadShouldExit();
    waitForThreadToExit(2000);
}
//=================================================================================
void MidiPlayer::run()
{
    int nums = midiMessageSequence.getNumEvents();
    MidiMessage message;
    double msPerTick = 60000.0 / 120.0 / midiFile.getTimeFormat(); 
    double prevTimestamp = 0.0;
    double nextTime = 0.0;

    for (int i = 0; !threadShouldExit() && i < nums; ++i) 
    {
        message = midiMessageSequence.getEventPointer(i)->message;


        nextTime = msPerTick * (message.getTimeStamp() - prevTimestamp);

        Time::waitForMillisecondCounter(Time::getMillisecondCounter() + uint32(nextTime));


        if (message.isTempoMetaEvent()) 
            msPerTick = message.getTempoSecondsPerQuarterNote() / midiFile.getTimeFormat() * 1000;

        else if (!message.isMetaEvent())            
            midiOutput->sendMessageNow(message);

        prevTimestamp = message.getTimeStamp();
    }

    noteOffAndReset();
}
//================================================================================
void MidiPlayer::noteOffAndReset()
{
    // stop MIDI playback and reset midi controllers..
    for (int trackIndex = 0; ++trackIndex <= 16; )
    {
        midiOutput->sendMessageNow(MidiMessage::allNotesOff(trackIndex));
        midiOutput->sendMessageNow(MidiMessage::allControllersOff(trackIndex));
    }
}

What makes you so sure that this delay is caused by loading the file, and not something else?[/quote]

I have an app which loads and plays midi files and there’s no slowdown on loading, even in debug mode. Have you tried profiling ? The problem must lie somewhere else IMHO …

Hi, luckyliuk

I rewrote the MidiPlayer class just now. Still not mature enough, but it can play normal MIDI files and also be able to handle the tempo change. You can further refine it. Hope inspiration and help for your. Thanks.

MidiPlayer.h

/*
================================================================================
            MIDI Playback

    Author: SwingCoder
    Email: SwingCoder2@gmail.com
    Github: https://github.com/SwingCoder

    License: MIT License, Copyright (c) 2012 by SwingCoder
================================================================================
*/

#ifndef __MIDIPLAYER_H_
#define __MIDIPLAYER_H_

/** Used to playback MIDI file.

    Just creat it and call playMidi().
    
    @code
    midiPlayer = new MidiPlayer(MidiOutput::openDevice(0));
    midiPlayer->playMidi(midiFile);
    @endcode
*/
//==============================================================================
class MidiPlayer : private Thread
{
public:
    MidiPlayer(MidiOutput* midiDevice);
    ~MidiPlayer();

    void playMidi(File& file);
    void stopMidi();

private:
    MidiOutput* midiOutput;
    MidiFile midiFile;
    MidiMessageSequence midiMessageSequence;

    void run();
    void noteOffAndReset();
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiPlayer)
};

#endif  // __MIDIPLAYER_H_

MidiPlayer.cpp

[code]
#include “…/JuceLibraryCode/JuceHeader.h”
#include “MidiPlayer.h”

//===============================================================================
MidiPlayer::MidiPlayer (MidiOutput* midiDevice) : Thread(“MidiPlayer”),
midiOutput(midiDevice)
{
jassert (midiOutput != nullptr)
}
//=================================================================================
MidiPlayer::~MidiPlayer()
{
signalThreadShouldExit();
waitForThreadToExit(2000);

deleteAndZero(midiOutput);

}
//=================================================================================
void MidiPlayer::playMidi(File& file)
{
stopMidi();
midiMessageSequence.clear();

// get MidiFile..
FileInputStream fileInputStream(file);
midiFile.readFrom (fileInputStream); 

const MidiMessageSequence* currentTrack;

// add all sequence of the file..
for (int track = 0; track < midiFile.getNumTracks(); track++) 
{
    currentTrack = midiFile.getTrack(track);
    midiMessageSequence.addSequence(*currentTrack, 0, 0, currentTrack->getEndTime());
}

midiMessageSequence.updateMatchedPairs();

startThread(8);

}

//=================================================================================
void MidiPlayer::stopMidi()
{
noteOffAndReset();
signalThreadShouldExit();
waitForThreadToExit(2000);
}
//=================================================================================
void MidiPlayer::run()
{
int nums = midiMessageSequence.getNumEvents();
MidiMessage message;
double msPerTick = 60000.0 / 120.0 / midiFile.getTimeFormat();
double prevTimestamp = 0.0;
double nextTime = 0.0;

for (int i = 0; !threadShouldExit() && i < nums; ++i) 
{
    message = midiMessageSequence.getEventPointer(i)->message;


    nextTime = msPerTick * (message.getTimeStamp() - prevTimestamp);

    Time::waitForMillisecondCounter(Time::getMillisecondCounter() + uint32(nextTime));


    if (message.isTempoMetaEvent()) 
        msPerTick = message.getTempoSecondsPerQuarterNote() / midiFile.getTimeFormat() * 1000;

    else if (!message.isMetaEvent())            
        midiOutput->sendMessageNow(message);

    prevTimestamp = message.getTimeStamp();
}

noteOffAndReset();

}
//================================================================================
void MidiPlayer::noteOffAndReset()
{
// stop MIDI playback and reset midi controllers…
for (int trackIndex = 0; ++trackIndex <= 16; )
{
midiOutput->sendMessageNow(MidiMessage::allNotesOff(trackIndex));
midiOutput->sendMessageNow(MidiMessage::allControllersOff(trackIndex));
}
}
[/code][/quote]

I’ve missed this reply from some time :frowning:

Anyway… Tnx for your code ! I tried it but timing seems have some troubles… have you any ideas on it ?

Luca