Saved MIDI file issue


#1

Hi all,

I have been trying without any success to write a simple MidiMessageSequence to a MidiFile. I followed all the advice in the blog and the documentation, and in the examples.

The MIDI data is gathered from the handleIncomingMidiMessage callback.

if (isRecording){
    double timeStampInMS = Time::getMillisecondCounterHiRes() - startTime;
    double msPerTick = (60000.f / 120) / 960.f;
    seq.addEvent(message,0);
    seq.getEventPointer(seq.getNumEvents()-1)->message.setTimeStamp(timeStampInMS / msPerTick);
}

When recording is turned off, I save the sequence to a file.

    isRecording = false;
    midiInput->stop();
    MidiFile mf;
    mf.setSmpteTimeFormat (25, 40);

    seq.updateMatchedPairs();
    seq.sort();
    mf.setTicksPerQuarterNote(960);
    mf.setSmpteTimeFormat(25, 40);
    
    MidiMessageSequence master;
    master.addEvent(MidiMessage::tempoMetaEvent((60000.f / 120) * 1000.f));
    master.addEvent(MidiMessage::timeSignatureMetaEvent (4,4));
    master.addEvent(MidiMessage::endOfTrack());
    seq.addEvent(MidiMessage::endOfTrack());

    mf.addTrack(master);
    mf.addTrack(seq);
    File f (midiFileNameToSave);

    FileOutputStream fos(f);
    mf.writeTo(fos,1);

// fos.flush();
seq.clear();
midiInput->start();

When I try to open the file in Logic or any other MIDI enabled DAW, it does not load.
I checked with MidiKit and it is possible to “fix” the file using the “fix” function.

The error indicated in MidiKit is “the extension/type should be fixed”.

Please advise.
Best regards


#2

You probably have a few other problems, but note that FileOutputStream appends to a file. You should at least delete the file first or you’ll just be adding more junk on the end each time.


#3

Thank you so much Jules.

I can certainly make sure the file doesn’t exist first, however in my case I am always writing to a new directory, so there is no chance the file might already exist.

Strangely enough the file, once “fixed” by MidiKit, looks, internally, exactly the same as the faulty one.

Here is the new code, simplified.

void MidiTrack::startRecording(const File file){
if (toggle)
{
seq.clear();
seq.addEvent(MidiMessage::tempoMetaEvent((60000.f / 120) * 1000.f));
startTime = Time::getMillisecondCounterHiRes();
isRecording = true;
midiFileNameToSave = file.getFullPathName();
}
}

void MidiTrack::stopRecording(){
if (toggle){
isRecording = false;
midiInput->stop();
MidiFile mf;
mf.setSmpteTimeFormat (25, 40);
seq.updateMatchedPairs();
seq.sort();
mf.addTrack(seq);
File f (midiFileNameToSave);

    FileOutputStream fos(f);
    mf.writeTo(fos,1);
    fos.flush();
    seq.clear();
    midiInput->start();

}
}

Here are the two identical files, on of them is the faulty file created by my code. I still have timing issues with the “fixed” file though, and I’ll need to figure that one out. I saw some discussions concerning that. I hope they’re not related.

I was wondering if someone could take a few minutes to try to generate a MIDI file, and see if they can make it work, and perhaps show me how ? I am on Mac Osx 10.13.3 Beta. (I am an Apple Beta tester).

Best regards !


#4

Hmm, for a start you seem to call both setSmpteTimeFormat and setTicksPerQuarterNote, referring to your first post. These sets the used time format and are mutually exclusive, you must decide on using either, have a read of the comments in juce_MidiFile.h for getTimeFormat(), setTicksPerQuarterNote() and setSmpteTimeFormat().

You may also find it useful to look up some midi file description on the we such as http://www.somascape.org/midi/tech/mfile.html.

In other words you have to decide in what time format you want to save your midi file and thereafter calculate the time stamp for each midi event according to that format. And tempo meta events are not used for smtpe formats.

Besides, there’s no need to call updateMatchedPairs() just to save a sequence, it will only update some internal pointers that’s not going to be saved anyway. But if you do (e.g after manually editing a midi sequence) you mustn’t call sort() afterwards while it might screw up the order of the newly updated pair-pointers. But in this case (recording some live midi events as it looks like), both calls will probably do nothing (the sequence is already sorted).


#5

use 3 backtick’s ( ` ) to format your text as code, don’t use the quote button.


#6

one on each line ?
thanks for the pointer !


#7

Thanks Oxxyyd !
I’ll look into this today.
Jacques


#8

no, surround all of your code. 3 backticks on their own line, then your code, then 3 backticks to close it off

//like this
//here's some code
return obj.doSomething();