Jump to content

Coding a MIDI decoder (interpreter/translator) in C


SullX

Recommended Posts

My goal is to write a program in C that will accept MIDI byte's (MIDI input) and will interpret/translate the MIDI bytes into commands that activate certain bits on a microcontroller (move them to the high position).

To begin however I just want to write a simple code in C that will interpret the MIDI code and will display the different functions within each byte of MIDI.

For example, after receiving a MIDI byte and interpreting it the program prints to the screen all the information that was stored in that byte (note, note length, attack velocity, etc.).

Once this step is complete I wish to translate the information within the MIDI byte to commands for a microcontroller. For example: middle C quarter note indicates for one of the bits on the microcontroller to activate for 0.25 seconds.

With all that said I need some direction as to where I can learn about MIDI output (MIDI code). I need to understand what all is communicated with each byte of MIDI, and what determines the note, attack speed, and all the other info contained within the output. I am fairly new to MIDI so any help is appreciated.

If you are curious as to why I wish to write a program, it is because I eventually wish to control (animate) a quadrupedal robot with the MIDI score. Each bit on the microcontroller that is activated will activate a particular pneumatic piston over a certain period of time causing the joint to contract.

Link to comment
Share on other sites

Welcome aboard SullX!

You're in the right place, it's all done already. A MIOS robot has been mentioned in the past, it'll be great to see it became a reality. Have a search around, check out the function reference http://www.ucapps.de/cmios_fun.html

And DOUTx4 modules for outputs, timers if you need them....ahhh... yeh, search around. You'll like what you find I'm sure :)

Link to comment
Share on other sites

Thanks for your response stryd_one. So I have been working through the code for some *.mid files. The code below is for a midi file that plays a middle C whole note. The note in the below code appears on line 0050 it is the 30 bit. If you look at the far right column you can see a pattern ( 0h.@.0 ), this happens every time a note is struck. But the @ character is different if the note length is different (whole note half note) etc.

0000: 4D 54 68 64  00 00 00 06  00 01 00 03  02 D0 4D 54  MThd..........MT

0010: 72 6B 00 00  00 0B 00 FF  51 03 07 A1  20 00 FF 2F  rk......Q... ../

0020: 00 4D 54 72  6B 00 00 00  33 00 B0 07  7F 00 C0 18  .MTrk...3.......

0030: 00 B0 65 00  00 B0 64 00  00 B0 06 18  00 B1 07 7F  ..e...d.........

0040: 00 C1 18 00  B1 65 00 00  B1 64 00 00  B1 06 18 00  .....e...d......

0050: 90 30 68 96  40 80 30 00  00 FF 2F 00  4D 54 72 6B  .0h.@.0.../.MTrk

0060: 00 00 00 2A  00 B2 07 7F  00 C2 21 00  B2 65 00 00  ...*......!..e..

0070: B2 64 00 00  B2 06 18 00  B3 07 7F 00  C3 21 00 B3  .d...........!..

0080: 65 00 00 B3  64 00 00 B3  06 18 00 FF  2F 00          e...d......./.

Is there anyone who can assist me in interpreting the rest of this code? What information is contained within the first four lines? And what other information is dispersed between lines 0050 and 0080 (other than what I discussed above)?

Thanks for your help, seems like a great community.

Link to comment
Share on other sites

I think you are misunderstanding what I am attempting. I need to understand the MIDI code at the most fundamental level. This way I can write a program in C that can interpret the MIDI code (like the MIDI bytes I posted above) and take actions based on specific MIDI bytes. I don't believe a MIDI sequencer is of use to me for this particular situation, but I know very little about them so I could be wrong.

If someone could help me interpret the above code I would highly appreciate it. (I found this article http://www.qzx.com/pc-gpe/midi.txt which helped some but I could use further assistance). Thanks

Link to comment
Share on other sites

Ah... you're after

0050: 90 30 68 ....  .... 80 30 00 

9x Note On

x0 Channel 1 (you count up from 0)

30 Note # 30 (that's not middle C)

68 Velocity 68

8x Note Off

x0 Channel 1

30 Note # 30 (that's not middle C)

00 Velocity 0

But I'm not going to figure out the rest for you - as I said, if you load these into your sequencer, it will tell you what they do... not to mention that I'm sure midi files have headers and footers but yeh... I googled 'midi file analyser' and found this http://www.simtel.net/product.php%5Bid%5D89598%5BSiteID%5Dsimtel.net ... There are probably lots of them... But it's not clear why you're interested in the midi file format? You should n't need to worry about it. If you are sending realtime controls then you won't record anything, and if you are recording data for playing back the robot's actions later, then your sequencer will take care of the file, and give you a more comfortable interface. The midi protocol (not the file format) is of more interest to you, and durisian has posted the perfect link.

Link to comment
Share on other sites

It seems I dont understand the capabilities of MIOS or a sequencer. Perhaps you could explain how I can use the sequencer to interpret all of the above code? We wish to be able to create a midi file that can be used for playback later in the robot (do not need to stream). Are you suggesting another approach rather than writing a program in C that interprets a .mid and translates it into activating the bits on a microcontroller that controls our valve system?

Link to comment
Share on other sites

I think there are some misunderstandings here, because writing a *.mid file (and reading / playing it back) are a bit different from just receiving MIDI data, which was not clear to me while reading your first post and the topic title.

As it seems to me that you'd like to work with *.mid files which are already existing, it might be easier not to invent the wheel again: There's a project that sounds like it could be useful to you. Note however that it's not related in any kind with MIDIbox - and it's ATMEL and not PIC-based: http://www.lehmayr.de/e_mrmidi.htm

You could feed the MIDI-Out of this player/recorder into a MIDIbox Core that drives your valves.

Regards,

Michael

Link to comment
Share on other sites

We wish to be able to create a midi file that can be used for playback later in the robot

Heh, you're gonna flip when you realise how easy it is.

Are you suggesting another approach rather than writing a program in C that interprets a .mid and translates it into activating the bits on a microcontroller that controls our valve system?

Well yes and no. Such a program is exactly what you need...and such a program is just what MIOS is... so you don't need to write very much.

You just need to use a bit of simple code to process the incoming midi data.

Rather than explain it to you backwards, I'd prefer to use a real world example to demonstrate it to you I was going to write code to show you how to make the robot's 'elbow' react to incoming notes, but first, I think we need a more clear explanation of what you're trying to do. Don't worry too much about how you're going to do it, just what you're going to do.... as AC said, it seems as though you want to use existing midi files to control the robot, but I'm puzzled as to why one would want to do such a thing. What's the end-goal here?

Link to comment
Share on other sites

Thanks for your help.

We have built a quadrupedal robot with 12 joints (four legs each with a foot, elbow/knee, and hip joint). The goal is eventually to test a mathematical theorem predicting behaviors of neurons in an n-legged creature. We will use our robot along with reconfigurable pattern generators based on nonlinear electronic circuits to test this theory. This MIDI however has no application to that part of the project.

Before we begin developing these nonlinear circuits to test out this theorem we need to script movements (locomotion) for the robot. We need to figure out the proper timing for the extension and contraction of each leg of our quadruped, and the timing of each of these events in a single leg with reference to the other legs (in this way we can write a script that will cause the robot to accurately gallop, run, or walk, etc). The timings will then be implemented in the creation of the circuit that will eventually control this robot and test the theory.

So, this is where MIDI comes in. MIDI provides a very easy and user friendly way to write a script that could control the movements of a robot and the timing of each. We, therefore, don't need streaming input. That is why the .mid file type was attractive. We could, instead, write a program that interprets its own defined script. But MIDI provides a much more intuitive and user friendly environment for writing a movement (animation) script for the robot.

The nitty gritty: The robots twelve joints contract and expand when their relative pneumatic pistons are pressurized. For each piston there is a corresponding valve that when activated pressurizes the piston. So we have a twelve valve system. Currently each valve is hooked up to a key on a very old piano keyboard. Each key basically acts as a current switch, and when it is pressed it provides a current to the valve which causes the pressurization of the piston etc.

We want to write a program in C that can be loaded onto a microprocessor that will translate a MIDI file (.mid) that is loaded in the ram of the controller. Each note in the .mid would correspond to a particular valve, and the length of the note would control to the rate at which the valve pressurizes the piston. But before we write the program for the microcontroller we would like to simply write a program in C that interprets a .mid that we write and then translates it into the activating of twelve place holders over a certain length of time. These place holders obviously represent the 12 valves and 12 of the bits on the microprocessor we will eventually use to activate the valves.

I hope this project description helps you help me. If you need me to elaborate on anything else, just ask. Thanks again.

Link to comment
Share on other sites

Sounds like a lot of fun.

It also sounds like two separate projects (at least)

The MIDIBox usually deals with a live stream of MIDI data.We have all of MIOS to handle the data easily, and using it to control 12 valves or solenoids would be a breeze (that's a pneumatic joke).

To control (and experiment) with your robot, you could use any sequencer on any platform. All the editing facilities of that sequencer would be at your disposal. Just a 2 wire MIDI cable would tether your data stream into your little Frankenstein. A second cable could give a stream of sensor data in return.

Everything I have described so far is easily available, and not that hard to do. You will note, however, that no mention has yet been made of .mid files, or the MIDI file format. See, everything so far has been "live" MIDI streams. The sequencer is in your PC.

IF you want to write a MIDI file player, great! If you want to do it inside the MIDIBox, you'd probably best stick with Format 0. Formats 1 and 2 require buffer space for each track that would quickly exceed what is available in most MIDIBoxes. Format zero is manageable as a single track. You'll need some way of getting the .mid files into the MIDIBox. At this time, you'll probably need a bankstick to do that. There is something easier in the future, but it's not yet ready for release. ;-)

Even if you write a MIDI file player, that still doesn't offer easy editing. You'll still need software on a PC to do that.

Hopefully this has helped to explain the difference between what you are asking and what we seem to be hearing. Still want to write a MIDI file player? you'll find all the information you need on the MIDI file format at

http://home.roadrunner.com/~jgglatt/

You'll also find info there on MIDI commands (not the same as the file format!)

Based solely on information at that site, I was able to write a Format 0 MIDI file player in 2 days. In 2 more, I expanded it to support format 1, but none of this was in a MIDIBox. That would be a different challenge altogether.

Format zero playback in a MIDIBox can be done(and might have been done already), but I don't have any need for that. The PC MIDI file player I wrote last week is written in "C", but "C" for a PC and "C" for a single chip processor like the MIDIBox are NOT the same language.

I hope this helped.

Have Fun,

LyleHaze

p.s. You mentioned that "the length of the note controls the rate at which the valve pressurizes the piston". Two thoughts: You will need proportional control valves. These are neither common nor easy to control, especially since air is not at a constant volume, you will at best be controlling pressure, not position. Assuming you have the means to work with those limitations, you would be far better off using note velocity instead of duration, as velocity is available at the moment the note is struck, but you won't learn the duration of the note until it has ended.

Link to comment
Share on other sites

OK, I have a few monutes, I'll try to show off a bit..

0000: 4D 54 68 64  00 00 00 06  00 01 00 03  02 D0 4D 54   MThd..........MT

0010: 72 6B 00 00  00 0B 00 FF  51 03 07 A1  20 00 FF 2F   rk......Q... ../

0020: 00 4D 54 72  6B 00 00 00  33 00 B0 07  7F 00 C0 18  .MTrk...3.......

0030: 00 B0 65 00  00 B0 64 00  00 B0 06 18  00 B1 07 7F  ..e...d.........

0040: 00 C1 18 00  B1 65 00 00  B1 64 00 00  B1 06 18 00  .....e...d......

0050: 90 30 68 96  40 80 30 00  00 FF 2F 00  4D 54 72 6B  .0h.@.0.../.MTrk

0060: 00 00 00 2A  00 B2 07 7F  00 C2 21 00  B2 65 00 00  ...*......!..e..

0070: B2 64 00 00  B2 06 18 00  B3 07 7F 00  C3 21 00 B3  .d...........!..

0080: 65 00 00 B3  64 00 00 B3  06 18 00 FF  2F 00           e...d......./.

OK. I have a few minutes, and the format should be fresh in my mind. Let's see how much I remember.

MIDI files are divided into chunks. the two "official" chunk types are MThd at the head of the file and MTrk at the beginning of each track.

Your file starts with "MThd 00 00 00 06", this is the header chunk, and always has a length of six, as shown. Following that are six bytes, representing three SHORT numbers. "00 01" is format 1 (multitrack), "00 03" is track count, three tracks should follow. "02 D0" is the divisor, indicating 720 "ticks" per quarter note.

Next is the first track. Like all tracks, it begins with the MTrk chunk header, followed by a LONG length. In this case, this first track has a length of "0B" (11) Following this will be a variable-length time delay, then a MIDI event, then time delay, then event et-cetera until the track is finished. In this case, delay 0, then Meta-Event

"FF 51 03 07 A1 20", which is NOT a MIDI event, it just instructs the sequencer to set the tempo to "07 A1 20" microseconds per quarter-note. In this case, 07a120 is 500000, so we get a tempo of 120 BPM. Then we get another delay of zero, and "FF 2F 00" which marks the end of track.

On to the next track.. MTrk header, track length will be

33(51). Delay zero, then event "B0 07 7F" which is control change, channel 1 Volume to MAX. then delay 0, then "C0 18" which will send a program change, channel 1, to the 24th patch. delay zero again, then "B0 65 00" which will send Control Change, channel one, Portamento OFF. Then delay of zero again, and "B0 64 00" Control change, channel 1, Hold pedal OFF. then delay zero again, and "B0 06 18" setting channel 1 data entry slider(coarse) to 24.(why??)

The next groups, without any delays will send all the same to channel two. "00 B1 07 7F 00 C1 18 00 B1 65 00 00 B1 64 00 00 B1 06 18 00" Then we get on to "90 30 68" which sends NOTE ON, channel 1, the 48th note, velocity 104. Then we have "96 40" which is a delay of 2880 "midi ticks" (remember, all delay values may be variable-length) That should be one whole-note long unless I screwed up the math.(720 from the file header = 1 quarter-note, so 2880 is a whole note) Then comes an "80 30 00" which will turn the previous note on channel 1 OFF. then a delay of zero, and the "FF 2F 00" end of track marker.

On to the third track. (Ugh, getting tired!)

standard track header, track length will be 42 bytes. delay zero, "B2 07 7F" channel 3 volume max. "C2 21" channel 3 program change 33, "B2 65 00" channel 3 Portamento OFF, "B2 64 00"  Channel 3 hold pedal OFF, "B2 06 18"  Channel 3 Data slider (coarse) at 24. Then "B3 07 7F 00 C3 21 00 B3 65 00 00 B3 64 00 00 B3 06 18 00" does much the same for channel 4. Finally, FF 2F 00 ends the track.

There is a lot in this that is not needed to play a single note. It's doing basic setup for channels 1-4. This is nice, but a lot more than you need.

Also, you can see that the length of a note is not encoded in the note-on message, but in the time that passes between the note-on and the note off.

Have Fun,.

LyleHaze

Interesting..

I wonder how many Format 1 MIDI files have multiple channels of data in a single track? I wonder how many players assume that all events in a single track are the same channel? I have at least one sequencer here that makes this mistake. Among other things, it puts the drums into the wrong channel for playback.

I learned something today! No wonder I'm so tired. :-)

Link to comment
Share on other sites

Lyle, your explanation of the code is phenomenal! I have been studying the midi code from various websites and had put a lot together but you have cleared up a LOT of questions I had. I highly appreciate your efforts!

Two questions:

First, is there anyway to simplify this code and remove all the extra channels? Only one midi channel is needed to play the note correct?

Secondly, how can i make the NOTE OFF event execute over a period of time, similar to the NOTE ON event. Particularly, when I am using a synthesizer (one with a musical score such as the Power Tab Editor) how can I give a note the proper command that will cause the MIDI NOTE OFF event to execute over a period of time.

The second question is the most important as I dont want to have to go in and script in by hand how long each NOTE OFF event should take. It would be nice to do it in the synth program.

Thanks again!

Link to comment
Share on other sites

Heya SullX,

Glad you've got your head around it now :) Sorry I didn't post before, but Lyle said basically exactly what I was going to, no need to echo :)

What you are referring to, is the job of a sequencer. A sequencer will play back a series of messages to be sent down the midi cable, in a given order. It works just like sheet music, but instead of saying "play a C note for 1 quarter of the bar", it says "turn the C note on"...and then, one quarter later, "turn the C note off".

Most MIDI sequencers will give you what is called a piano roll. It looks like this: (ignore the menus on the left, it's the stuff on the right you want)

midi-piano-roll-editor.jpg

In that picture, each row represents a midi note number. In the musical world, that relates to a key on a piano, or a fret on a guitar string. In your robot, each row could represent one valve.

You can see vertical lines separating the sequence into musical bars. Just right of the 2nd bar, you can see a darker vertical line - that's where the sequencer is now. As the sequencer plays, that line travels from left to right, and as it crosses over the beginning of one of the blue rectangles, it sends a midi Note On message.... as it crosses over the end, it sends a Note Off. You draw in the rectangles with your mouse, and stretch and shrink them to be the correct length.

In your robot, you could set it so that when a Note On is recieved, it opens a valve, and when the Note Off is recieved, it closes the valve. You can probably imagine how a timeline of events to animate your bot, would look on a piano roll.

Of course, it doesn't all have to be locked to a musical timeframe. You can place your notes wherever you like :)

Edit: Oops forgot. The pic above, would represent one midi channel. Because the midi channel is part of the midi note on message, you can't just ignore it - but you can just pick one channel and go with that. This means that your controls would be very simple:

90 xx yy

These are three hex bytes.

The first is split in two; 9 and 0. The first half (most significant byte) 9 is a note on message - but it can also be a note-off message - I'll come back to that.

The second half (least significant byte), the 0, is the channel. You only needone, so hey, we'll go with channel 1 (the computer counts from 0, but it's still the 1st channel, so channel 1 it is ;))

The second byte, xx, would be a number, between 0 and 127, which would identify which valve the message refers to. This in midi is the 'Note Number'

The third byte, yy is known in midi as velocity. The velocity is supposed te represent how fast you hit the piano key, so it usually represents how loud the recieving device will play the sound. If the velocity is 0, that equates to a note off message (told you I'd come back to that).

You only need the valves open or closed, so you can just 127 to open the valve, and 0 to close it.

This is very simple to do in MIOS. You can find an example of how to drive 128 LEDs from a midibox, the same code applies to you - just, instead of lighting an LED, you're opening a valve :)

And yes, that's really as much code as you need. OK you could make it more flashy if you wanted to, but.... That will do the job. Certainly for a prototype :)

Link to comment
Share on other sites

Good Morning,

OK, so I was just showing off. If I hadn't written a MIDI file player last week, I would have been as blank as the wall in front of me.

Regarding removing excess channels, that sounds like a good idea, but it's up to whatever program you are using to edit/save the MIDI file. Yes, you could do it by hand.. (remove the final MTrk and everything after it, then change the MThd track counter from 3 to 2. channels 3 and 4 are gone) BUT.. since you are handling .mid files in your PC (I hope!), it doesn't really matter yet.

Regarding making NOTE_ON and NOTE_OFF execute over a period of time.. Think about this. Neither of them execute over a period of time, they are each a brief, instantaneous MOMENT in time. You need to adjust the amount of time in between them.

Example:

"90 3C 40" Note On, channel 1, Middle C

"7F"      Wait 127 "ticks"

"80 3C 00" Note Off, channel 1, Middle C

Will sound a middle c for a period of time. How long also depends on Tempo, and divisions, not shown here.

We change it to

"90 3C 40"

"81 7E"  delay 254 "ticks"

"80 3C 00"

Will sound the same note, but will hold it for twice as long. The NOTE_ON and NOTE_OFF took the same amount of time, but we doubled the delay between them.

But, as mentioned before, let your sequencer do it for you!

Don't try to manage MIDI files yourself. Find software like Stryd_One showed you, and you can ignore all this detail stuff.

A few more suggestions:

Find a sequencer that supports "A-B-A" editing. Then you can write short, separate tracks for each primitive motion (like leg B step forward) and then arrange those TRACKS repetitively for more complex actions like walking.

Instead of using consecutive notes for related actions, choose a group of notes that are within a musical scale and key. That will make "listening" to your compositions much more tolerable.

The website that I pointed you towards (Jeff Glatts site) has absolutely ALL the information I have presented, and a lot more.

Finally, if you want to see the C code for a MIDI file player, I can offer mine, but you won't be able to compile it unless you have an AmigaOne with OS4. The chances of that are about 0.0001%. Otherwise you can just see one way of parsing the data.. BUT YOU SHOULDN'T NEED TO BOTHER!!

Have Fun,

LyleHaze

Link to comment
Share on other sites

Thank you both for your thorough explanations. I now plan on utilizing the MIOS software to do what I require, but I still plan to write my own program to do this also. MIOS is going to help a great deal with hammering out bugs in the program and timing issues with my own program.

As far as I can see there is only one other problem..

This is how I am seeing it: the time between the NOTE ON event and the NOTE OFF event will be the length of time a particular joint will be in the extended state, and the time between the NOTE OFF event and the NOTE ON event will be the length of time a particular joint will be in the contracted (flexed) state.

This is good. But I also need the capability of adjusting the rate at which each joint extends/contracts. In other words, I need to be able to vary (with the midi code) the time it takes for a particular joint to move from its extended position to its contracted position.

I have been thinking about this and it doesn't seem (at least to me) that there is an easy way to do this with the NOTE ON/OFF commands. So, I was thinking that in the program that I end up writing, I could have it interpret the volume for Channel 1 as the rate at which the joints move from one state to another (contracted and extended). This wouldn't allow us to vary the rate of the joints expansion and/or contraction midway through the MIDI file but there is no need for this anyway (no animation of going from walking to running is need, for example). It should suffice to simply set the rate of joint movement for each MIDI file at the beginning (eg low volume == slow joint movement ; high volume == fast joint movement).

What are your thoughts?

Link to comment
Share on other sites

OK, so you want proportional control. You want to control the rate of change, instead of just on/off.

Expressing that in MIDI is easy, on a note-for-note basis. Every note_on and every note_off has it's own "velocity" byte, which may be anything from 0 to 127. (Actually, a note_on with a velocity of zero is considered a note-off, as Stryd pointed out earlier). In musical terms, the velocity may be mapped to volume, or to sound timbre or pitch in some more exotic cases. Velocity is controlled by how hard you hit the note. Velocity in a note-off is supposedly how hard you release the note, but it's less supported in keyboard manufacturing. In any case, the data is there for you to control or edit as you desire.

So much for the easy part. the trickier part will be the electronics and valves to control your pneumatics. You are leaving typical valve ON/Valve OFF behind, and now looking for "Valve open 10%, Valve Open 65%, valve closed." It's no longer a simple digital circuit. It's also no longer a simple valve. This is all well outside of MIDI and music, but I have had some experience with such problems.

One way to get variable control is to try PWM on the valve. Yeah, it will be somewhat variable, but it won't be repeatable over any time. and even under the best circumstances, you won't be controlling position, you'll be controlling pressure. So your timing will change under different loads, under different conditions, under different air pressures, or even under different phases of the moon. It will never be reliably repeatable.

I've tried some high-end proportional control valves for hydraulics as well. Most of the same problems exist. EVEN IF you can monitor feedback from the valve core, it's not telling you squat about the axis position. and energy transmitted is relative to flow rate, not load.

If you want this thing to work well, you have just dropped pneumatics and moved over to full electric control. even simple hobby servo motors have position feedback integrated, solving many of the problems mentioned above.

Of course the feedback loop stops at the servo itself, it's not reporting true position back to the processor, so you'll have to operate on faith, and/or overbuild the motors for margin.

BUT, you came here to ask about the MIDIBox, not for a class in designing automation.

It sounds to me like you would benefit from a bit more study. Read all of the posts in this thread again. Carefully. Follow the links that have been provided.

Look up PWM on the net and try to understand what I'm talking about. Look up "Closed Loop feedback" and study some more.

You mentioned that you have a prototype that is controlled now from an old piano keyboard. Does that work well for you? If it does, you don't need proportional control.

Perhaps you should start with a smaller project, instead of trying to change it over to MIDI and add proportional control at the same time.

Or you could just simulate it all in a CAD system and believe that it will work that way in reality.

Good Luck,

LyleHaze

[edit] I just read your post #12 in this thread, which stated pretty clearly that you want on/off control of 12 bits for 12 valves. When did the specification change?

Your project seems to be a moving target.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...