Split a MIDI file


#1

Hello everyone !

First of all, excuse my english, I’m just a poor french guy :slight_smile:

So, I want to make a little app that splits a MIDI file (containing n tracks) into n MIDI files.

My code compiles and executes but the output files have a serious rhythm problem… Every file runs too fast and I don’t know why :confused:

[code]
File file ("…/…/…/branches/scofoJUCE/rsrc/midi/test.mid");
FileInputStream inputStream = file.createInputStream();
MidiFile
midiFile = new MidiFile();

if ( !inputStream ){
Logger::outputDebugString( T(“Error : can’t create input stream file.”) );
}

if ( !midiFile->readFrom(*inputStream) ) {
Logger::outputDebugString( T(“Error : can’t read input stream file.”) );
}

int numTracks = midiFile->getNumTracks();
MidiMessageSequence* tracksSeq[numTracks];
MidiFile* tracks[numTracks];

for(int i=0;i<numTracks;i++) {
tracksSeq[i] = new MidiMessageSequence(*midiFile->getTrack(i));
tracks[i] = new MidiFile();
tracks[i]->addTrack(*tracksSeq[i]);

  String dest = "/tmp/track" + String(i) + ".mid";
  FileOutputStream* stream = new FileOutputStream(dest);
  tracks[i]->writeTo(*stream);
  
  delete stream;

}[/code]

Thank you !


#2

You probably need to set the correct tempo and stuff in the MidiFile objects.

(And your code snippet contains a lot of bad coding habits… Spending some time reading a few good C++ books would probably be a smart investment!)


#3

Thank you for your anwser jules !

I know that I’m not a fair C++ coder… That’s not my favourite language and I have a lot of troubles using it.

Thanks to your tip, I’ve found a way to correct my tempo problem.

The solution was to use the method setTicksPerQuarterNote(int) from MidiFile.

But, I don’t know how to get the integer to put as a parameter of this function. I’ve searched both MidiFile and MidiMessageSequence and I couldn’t find a way to get this damn integer.

Could someone help me ?


#4

The number is read/written directly to the file, it’s part of the standard midi spec - I don’t have any links, but if you find the midi file format specs, it should have all the tech details in there.


#5

Here is some MIDI file stuff I played with a while ago to figure out the same sort of issues. It’s possibly incomplete as I haven’t used it for anything serious yet.

[code]class MainComponent : public Component,
public FilenameComponentListener
{
private:
FilenameComponent* selectFileWrite;
FilenameComponent* selectFileRead;

public:
MainComponent ()
{
addAndMakeVisible(selectFileWrite = new FilenameComponent(T(“selectFileWrite”),
File::nonexistent,
false,
false,
true,
T("*.mid"),
String::empty,
T(“Choose a Midi file to write…”)));
selectFileWrite->addListener(this);

	addAndMakeVisible(selectFileRead = new FilenameComponent(T("selectFileRead"), 
															  File::nonexistent, 
															  false, 
															  false, 
															  false, 
															  T("*.mid"), 
															  String::empty,
															  T("Choose a Midi file to read...")));		
	selectFileRead->addListener(this);
}

~MainComponent ()
{
	deleteAllChildren();
}

void resized ()
{
	selectFileWrite->setBounds(10, 10, getWidth()-20, 24);
	selectFileRead->setBounds(10, 40, getWidth()-20, 24);
}

void filenameComponentChanged (FilenameComponent* fileComponentThatHasChanged)
{
	if(selectFileWrite == fileComponentThatHasChanged)
	{
		File file = selectFileWrite->getCurrentFile();
		
		if(file.existsAsFile())
			file.deleteFile();
		
		//writeToFileSMPTE(file);
		writeToFileBBT(file);
	}
	else if(selectFileRead == fileComponentThatHasChanged)
	{
		File file = selectFileRead->getCurrentFile();
		if(file.existsAsFile())
		{
			readFromFile(file);
		}
	}
}

// BBT bars beats ticks
void writeToFileBBT(File const& file)
{
	MidiFile midiFile;
	MidiMessageSequence seq;
	
	const int tickPQN = 96;
	
	midiFile.setTicksPerQuarterNote(96);
	
	// timestamps seem to be in multiples of: ticks per quarter note
	seq.addEvent(MidiMessage::noteOn(1, 60, 1.f), 0 * tickPQN);
	seq.addEvent(MidiMessage::noteOn(1, 60, 0.f), 1 * tickPQN);
	seq.addEvent(MidiMessage::noteOn(1, 72, 1.f), 2 * tickPQN);
	seq.addEvent(MidiMessage::noteOn(1, 72, 0.f), 3 * tickPQN);
	
	midiFile.addTrack(seq);
	
	FileOutputStream outputStream(file);
	midiFile.writeTo(outputStream);
}

/* SMPTE
 Can't get Logic to read this it gives the error "Can't read SMPTE format"
 maybe this is saying that Logic can only read BBT Midi files NOT SMPTE
 ones, not that there's generally a problem with our file?
 */
void writeToFileSMPTE(File const& file)
{
	MidiFile midiFile;
	MidiMessageSequence seq;
	
	const int fps = 25;
	const int smpteSubframeResolution = 80;
	
	midiFile.setSmpteTimeFormat(fps, smpteSubframeResolution);
	
	seq.addEvent(MidiMessage::noteOn(1, 60, 1.f), 0 * fps); // not not sure if this arithmetic is correct: can't check it
	seq.addEvent(MidiMessage::noteOn(1, 60, 0.f), 1 * fps);
	seq.addEvent(MidiMessage::noteOn(1, 72, 1.f), 2 * fps);
	seq.addEvent(MidiMessage::noteOn(1, 72, 0.f), 3 * fps);
	
	midiFile.addTrack(seq);
	
	FileOutputStream outputStream(file);
	midiFile.writeTo(outputStream);
}

void readFromFile(File const& file)
{
	MidiFile midiFile;		
	FileInputStream inputStream(file);
	midiFile.readFrom(inputStream);
	
	for(int trackIndex = 0; trackIndex < midiFile.getNumTracks(); trackIndex++)
	{
		const MidiMessageSequence *seq = midiFile.getTrack(trackIndex);
		
		for(int eventIndex = 0; eventIndex < seq->getNumEvents(); eventIndex++)
		{
			MidiMessageSequence::MidiEventHolder* event = seq->getEventPointer(eventIndex);
			
			printMessage(event->message);
			
			MidiMessageSequence::MidiEventHolder* offEvent = event->noteOffObject;
			
			if(offEvent != 0)
			{
				printMessage(offEvent->message);
			}
			
		}
	}
}

void printMessage(MidiMessage const& message)
{
	const int size = message.getRawDataSize();
	const uint8* data = message.getRawData();
	
	String logMessage = "message[time: ";
	logMessage << message.getTimeStamp();
	logMessage << "] ";
	
	for(int i = 0; i < size; i++)
	{			
		logMessage << data[i] << " ";
	}
			
	Logger::outputDebugString(logMessage);
}

};
[/code]