How do I name a MIDI track in a MidiFile?

?

There’s some kind of meta event for setting the track name, isn’t there?

I thought a lack of track names in my file was causing some softwares to dislike the file. that is of course a crap and unlikely reason!

In JUCE, I create a midi file from one midimessagesequence which consists only of noteons and noteoffs.

Midi File Dissassembler says…

[quote]MThd | Format=1 | # of Tracks=1 | Division=960

Track #0 ******************************************
Time Event
1: 1: 0 |On Note | chan= 1 | pitch=E 3 | vol=127
|On Note | chan= 1 | pitch=C 3 | vol=127
|On Note | chan= 1 | pitch=G 2 | vol=127
|On Note | chan= 1 | pitch=E 2 | vol=127
|On Note | chan= 1 | pitch=C 2 | vol=127
480 |Off Note | chan= 1 | pitch=e 3 | vol=0
|Off Note | chan= 1 | pitch=c 3 | vol=0
|Off Note | chan= 1 | pitch=g 2 | vol=0
|Off Note | chan= 1 | pitch=e 2 | vol=0
|Off Note | chan= 1 | pitch=c 2 | vol=0
|On Note | chan= 1 | pitch=E 3 | vol=127
|On Note | chan= 1 | pitch=C 3 | vol=127
|On Note | chan= 1 | pitch=G 2 | vol=127
|On Note | chan= 1 | pitch=E 2 | vol=127
|On Note | chan= 1 | pitch=C 2 | vol=127
2: 0 |Off Note | chan= 1 | pitch=e 3 | vol=0
[SNIP]
9: 1: 0 |Off Note | chan= 1 | pitch=g 3 | vol=0
|Off Note | chan= 1 | pitch=b 2 | vol=0
|Off Note | chan= 1 | pitch=g 2 | vol=0
|Off Note | chan= 1 | pitch=d 2 | vol=0
|Off Note | chan= 1 | pitch=b 1 | vol=0
|Off Note | chan= 1 | pitch=g 1 | vol=0
|End of track|
[/quote]

now this .mid file imports OK into Tracktion, EnergyXT and Reaper.

GuitarPro and TablEdit both claim the file has no tracks (tho TablEdit will preview play it)

If I import my file into Reaper then export it again it will load fine in GuitarPro! (GuitarPro is kinda the target app)

If I import into Tracktion and export again the file is highly duff! MIDI File DisAssembler says “found an undefined status in the midi file”, and NOTHING will import it (except Tracktion! lol)

WTFIGO?

I need to brush up on midi but is the JUCE/Tracktion midi system broken?

aye, but do i have to manually construct it in juce?

I can see MidiMessage::isTrackNameEvent () and so on, but I see no way to create such things?

Do i have to go buggering about with bytes and reading midi-specs! lol

c

OK, learning here…

I put a time sig and tempo events on track 0 and then me notes on track 1 and now guitarpro sees 1 track and i can import my piece! yay

would still like to name tracks!

Sorry, probably didn’t bother adding a method to create those events, I was mostly worrying about reading them when I wrote that stuff…

I have some code for reading Midi from Chris Cannam, creator of Sonic Visualiser.

 

It's at https://github.com/Venetian/ofxMidiFileLoader/

set up as openframeworks project but pretty easy to run that (replace source in any empty example)

 

It seems that when Chris parses data, he looks for MIDI_FILE_META_EVENT, namely 0xFF

in Juce I'm finding that when I read a file, I have a bunch of data at the beginning that is things like

255,3,11

 

whereas typical note on would be 144,60,100 (note on ch 1, pitch, velocity)

 

then what happens is you parse the second data byte to find the type of MIDI data and you get some text (returned to you by setting std::string name = "Track Name" or "Copyright", etc, depending on the type of meta MIDI data it is.

In the example above 255 specifies meta event and the second byte '3' specifies that it is MIDI_TRACK_NAME (0x03) and Track name turns out to be "GM Device 1" when I run the code below. not sure if the 11 is relevant.

 

the code that parses it in Chris's midiFileLoader is:

if (j->isMeta()) {//looking for the 0xFF data byte
                int code = j->getMetaEventCode();
                std::string name;
                bool printable = true;
                switch (code) {

case MIDI_TEXT_EVENT: name = "Text"; break;
                    case MIDI_COPYRIGHT_NOTICE: name = "Copyright"; break;
                    case MIDI_TRACK_NAME: name = "Track name"; break;
                    case MIDI_INSTRUMENT_NAME: name = "Instrument name"; break;
                    case MIDI_LYRIC: name = "Lyric"; break;
                    case MIDI_TEXT_MARKER: name = "Text marker"; break;
                    case MIDI_SEQUENCE_NUMBER: name = "Sequence number"; printable = false; break;
                    case MIDI_CHANNEL_PREFIX_OR_PORT: name = "Channel prefix or port"; printable = false; break;
                    case MIDI_CUE_POINT: name = "Cue point"; break;
                    case MIDI_CHANNEL_PREFIX: name = "Channel prefix"; printable = false; break;
                    case MIDI_SEQUENCER_SPECIFIC: name = "Sequencer specific"; printable = false; break;
                    case MIDI_SMPTE_OFFSET: name = "SMPTE offset"; printable = false; break;

 

 

somehow this would need to be inverted to generate the correct data in your MIDI file.

 

looking at the way this is structured, we have:

private:
    unsigned long  m_deltaTime;
    unsigned long  m_duration;
    MIDIByte       m_eventCode;
    MIDIByte       m_data1;         // or Note
    MIDIByte       m_data2;         // or Velocity
    MIDIByte       m_metaEventCode;
    std::string    m_metaMessage;
};

 

and different ways to create the event:

//looks like simple note on, note off, cc message, i.e. no strings or meta info

   MIDIEvent(unsigned long deltaTime,
              MIDIByte eventCode,
              MIDIByte data1 = 0,
              MIDIByte data2 = 0) :
    m_deltaTime(deltaTime),
    m_duration(0),
    m_eventCode(eventCode),
    m_data1(data1),
    m_data2(data2),
    m_metaEventCode(0)
    { }

 

//this next one looks like the meta event std::string stuff i.e. name above

    MIDIEvent(unsigned long deltaTime,
              MIDIByte eventCode,
              MIDIByte metaEventCode,
              const std::string &metaMessage) :
    m_deltaTime(deltaTime),
    m_duration(0),
    m_eventCode(eventCode),
    m_data1(0),
    m_data2(0),
    m_metaEventCode(metaEventCode),
    m_metaMessage(metaMessage)
    { }

    MIDIEvent(unsigned long deltaTime,
              MIDIByte eventCode,
              const std::string &sysEx) :
    m_deltaTime(deltaTime),
    m_duration(0),
    m_eventCode(eventCode),
    m_data1(0),
    m_data2(0),
    m_metaEventCode(0),
    m_metaMessage(sysEx)
    { }

 

okay I think I've also figured out what the other byte is. In the example above the 11 is the number of characters in the string. So the MIDI spec probably says for meta messages, set 0xFF, message type - track etc, then num characters

so if you read the midi data with our current JUCE routines you get

255,3,11 for a message that also contains the string of what the Track Name is (in my example was "GM Device 1").

 

would something need to be done to JUCE's

http://www.juce.com/api/classMidiMessage.html ?

 

 

you can see here:

http://www.sonicspot.com/guide/midifiles.html

definition of MIDI types.  In particular:

Meta Events
Events that are not to be sent or received over a MIDI port are called Meta Events. These events are defined by an event type value of 0xFF and have a variable size of parameter data which is defined after the event type.

Meta Event Type Length Data
255 (0xFF) 0-255 variable-length type specific

Meta Event Values

There are currently fifteen defined Meta Events. Each one is described in detail below.

Sequence/Track Name
This meta event defines the name of a sequence when in a Type 0 or Type 2 MIDI file or in the first track of a Type 1 MIDI file. It defines a track name when it appears in any track after the first in a Type 1 MIDI file. This meta event should always have a delta time of 0 and come before all MIDI Channel Events and non-zero delta time events.

Meta Event Type Length Text
255 (0xFF) 3 (0x03) string length ASCII text