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));
}
}