Understanding MD_MIDI lib and MIDI structure

Hello all,
my name is Holger, located in Germany. Doing woodworking and electronics for hobby I'm combining both in building a crank organ that will be (beside of the traditional punched paper strip) controlled by MIDI.

Currently I'm building a controller, based on a MEGA with 4x20LCD and rot-encoder that reads a midi file from SD card and sends the timed events to a MIDI cable.
On the other end there will be a receiving controller based on UNO that interprets the incoming events and switched the valves to the 20 organ pipes accordingly. (Btw, the output driver will be a 32bit IIC port extender, 2x MCP23017, that's just under construction)

But step by step...
The controller is set up, LCD, encoder and SD working, I'm using the MD_MIDI lib by MajicDesigns.
Based on the included example MD_MIDIfile_play I'n debugging now.

Here comes a lack of knowledge of mine regarding midi: I do not quite understand the output in the serial monitor, that reads like this:
19:57:30.046 -> 3402 M T3: Ch 2 Data C0 37
19:57:30.046 -> 3404 M T3: Ch 2 Data B0 7 40
19:57:30.046 -> 3438 M T2: Ch 10 Data 80 26 40
19:57:30.093 -> 3439 M T2: Ch 10 Data 80 30 40
19:57:30.233 -> 3601 M T1: Ch 1 Data 90 24 7F
19:57:30.233 -> 3603 M T2: Ch 10 Data 90 26 3F
19:57:30.327 -> 3692 M T2: Ch 10 Data 80 26 40
19:57:30.467 -> 3855 M T1: Ch 1 Data 80 24 0

What I don't get in detail is the "T": As far as I understood the structure of a MIDI file (SMF), it consists of several tracks in sequence. In each track several events are described with their timing data and actions. But tracks are sequential on the file. So how comes that here appear events of track T3 followerd by such of T2 and T1, that are earlier in the file?
It can't be that the whole file is cached in the processor, is it? It is interpreted in a serial way...
Can anyone explain this to me, please? (I thing the Channels are more clear, one is e.g. for a drum, other for violine, next for a melody, is it? Sorry, I'm completely non-musician...)

Thanks so much for enlighting me...
Best regards and take care

(English or German, please.)

A track is just an independent sequence of events that follows the same timing as the rest of the file. All tracks are processed simultaneously and the MIDI file is finished playing when every track has reach the end, which can be at different times.

In practice the applications that create MIDI files seem to allocate a track to a specific instrument, and one track to percussion instruments, although this is not a requirement of the MIDI standard as far as I know. Many simple MIDI files contain just one track with everything mixed in.

The file tracks are processed in parallel as we know where each track starts and the library remembers where it is up to in the file for each of the tracks. There is no caching beyond what would be done by the SDfat library.

This MIDI primer may help you understand MIDI in general https://www.midi.org/articles-old/tutorials

Thank you. Let's see if I got it: :o (I won't understand each detail, but like to know how it works together; blind plug&play isn't mine...)
The SMF type 1 may consist of several tracks. These tracks need to be read and processed in parallel as they may contain events that are (nearly) parallel. So, the library seeks for the begin of each track and then keeps a pointer for each track to read it when it is due to send its next event. That's why messages from T3 may appear before T1 messages.

I assume most MIDI files for my purpose will be type 0.

When the lib is playing the SMF, I need to capture the messages as they arrive on the MIDI out (TX) and create the corresponding action ,e.g. switch valves for all notes on Channel 1-the organ pipes, while notes of Channel 2-the xylophon (Glockenspiel) are routed to an other otput for the xylophon.

Did I understand correctly?

When changing to the other end, can I make use of the same lib to read from serial (the MIDI IN socket) and create my specific actions in the event handler fuction then?

Thanks and take care, Holger :wink:

I assume most MIDI files for my purpose will be type 0.

I guess if you are controlling the creation of the SMF that is a choice you can make. In any event, the stream coming from the library through the callback looks like a file type 0 stream even for a type 1 file.

Did I understand correctly?


can I make use of the same lib to read from serial

No, you are reading from Serial stream.

create my specific actions in the event handler function then

Yes. I am still not convinced that you will need 2 Arduino CPUs as the 'real world' events will occur very slowly compared to the speed of the CPU.

You will see from the Glockenspiel code (GitHub - MajicDesigns/Glockenspiel: Solenoid controlled Glockenspiel sketch) that the MIDI stream in the callback is interpreted into some physical action. In your case it will be the same stream of characters that arrive from the serial port. There could also be additional slight delays in the transmission but these should all be the same and can be ignored unless you are coordinating between two sources of musical information for one output.

Dear Marco, thanks for your comments.

I am still not convinced that you will need 2 Arduino CPUs as the 'real world' events will occur very slowly compared to the speed of the CPU.

For sure this is true; the reason I want to do it that way is only for compatibility, to have the chance to connect a Keyboard with MIDI out to that second "interpretor" and let a musician play the organ...

I'll have a closer look to the Glockenspiel; so far I read about an other interface than MIDI (RTTL) and so put it aside before getting inside. And I have to come back to that for the auto-note-off function reqired for xylophone or drums...

... days should have 48 hours ...

Just one thing, to shorten the debugging: the MD_MIDI lib deals with 8.3 filenames only, correct? And opens files in active directory only, no subdir in the filename (as this is not possible with 8.3) ?
No problem, but good to know from the description.
Cheers, Holger

the MD_MIDI lib deals with 8.3 filenames only, correct?


opens files in active directory only, no subdir in the filename

Correct. Change the current folder with the setFileFolder() method.

Is this method part of MD_MIDI lib?
I used SD.chdir(path) of SDfat lib to do this.

No, you need to use the one I mentioned. Your Sdfat object is not the same as the one used in the library.

ok, will double-check this. It worked, anyhow, I assume I work with the same SDfat object ...

New question: I can pause/unpause or loop a Midifile by .pause() and .looping()
How can I get back the status IsPaused or IsLooping back in my program to adjust n LCD? The internal variables _paused and _looping are declared protected. Can I just move them up in the public area? (may be this is stupid C++ beginner question...) :o

    inline void looping(bool bMode) { _looping = bMode; }
    inline bool isLooping(void) { return _looping; }
    void pause(bool bMode);
    inline bool isPaused(void) { return _paused; }

I added these two inlines to the lib to do this; I just ask in case I overlooked some already existent...

It seems to me that if YOU ask the library to pause YOU should already know it is paused without asking the library again?

That's true, but to remember I would have to provide a flag in my program what would contain the very same information as the already existent flag of the lib. And requires its own care to set/reset. Redundant stuff.

It is running so far. Now comes the other end of the MIDI cable...

Thanks for all the explanations and hints.

Thanks for all explanations 8)