Pages: [1] 2 3   Go Down
Author Topic: MIDI Library  (Read 6882 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
Developer of the MIDI library
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi guys, I'm trying to write down a MIDI library that would be versatile to read and send MIDI messages of any kind (supporting the channel filters, and so far a few common MIDI orders, like CC, PC or NoteOn/Off).

If anyone's intersted to help me with the C# parts, that would be great, i'm a newbee in that language, but my C coded functions shouldn't be that hard to translate..

So far I've got working functions to send a CC, PC or NoteOn/Off message on a specific channel or on all of them sequentially (OMNI mode), and I'm working on the reading part. I can "easily" implement some new messages to send (aftertouch, polypressure ect.. maybe clock), that should be less easier in the reading part..
Logged


0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
Developer of the MIDI library
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Okay, here it is, I don't have the stuff to test it so far, but I will try to get it soon.

http://frankyfuzzfire.free.fr/DIY/Arduino/MIDI.zip

The use is very simple, just take a look at the source code in MIDI.cpp, the read() function is the only one that needs an explanation: it returns a long in which the MIDI message is embedded if it is semantically correct.

There is also a thru() function that copies input MIDI messages matching the channel filter in parameter and sends it back to the MIDI output. Pretty useful if you plan to make a merger between what your code does and a MIDI input.

A quick summary of the chart and the functions:

Code:
 ### DON'T FORGET TO ENABLE THE LIBRARY WITH "MIDI MIDI;" AFTER PREPROCESSOR ###


  ### MIDI Chart: ###
  
  - Channel:
       0: OMNI mode, the message is sent on all channels.
       1 to 16: classic channels. (transposed to 0-15 for binary transmission)
       17+: OFF (nothing sent)

  - Types:
       PC: Program Change
       CC: Control Change

  - Parameters:
       PC: Patch/program number (0-127)
       CC: 1) controller number (0-127)
           2) CC value          (0-127)


  ### Functions: ###
  
  - send:
      Sends a MIDI message, just put the channel (as descripted above),
      the type (idem) and the parameters. If you send a Program Change,
      put anything you want as the second parameter, it will be ignored.

  - genctrl:
      Generates the first control byte, including channel and type.

  - sendPC & sendOther:
      Effectively sends via serial port the entire MIDI message.

  - extracttype:
      Returns the type (CC or PC) of the MIDI message by reading it in
      the header (first/command byte).
      
  - extractcanal:
      Returns the channel of the MIDI message included in the header.

  - buildlong:
      Encodes a MIDI message in a long (4 bytes) this way:
      MSByte                    LSByte
        0     header    data     data
      
  - read:
      Reads the serial buffer and returns a long including the message if
      it matches the MIDI syntax and semantics, and if the channel filter
      matches the channel of the message.
      Else returns -1 (for any possible reasons when the message is unsusable)
      
  - thru:
      Reads the serial input and copies it to the output (only if the message
      matches the channel filter, use OMNI to a full-thru function).
« Last Edit: October 19, 2008, 04:12:40 pm by Franky » Logged


0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
Developer of the MIDI library
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

There it is, fully working!

- Sends every kind of MIDI message
- Reads (almost) every kind of MIDI message

Supported: NoteOn, NoteOff, AfterTouch Poly, Control Change, Program Change, AfterTouch Canal, Pitch Bend.
Unsupported: System messages (SysEx, clock).


Download the library.



How to use it:

- First, you need to instance and activate the library with
Code:
MIDI_Class MIDI;

void setup() {
    MIDI.start(OPTO_PIN);
}

OPTO_PIN stands for the VCC pin of the input optocoupler. Setting it high enables the MIDI receptor, you can turn it off to reject any incoming message.

option: you can use the MIDI.start(OPTO_PIN,fast_transmit) syntax, on which fast_transmit is a boolean that enable (or disable) the fast transmission mode on MIDI output (which means that it won't send the status byte for a message if the previous one was the same type and channel).
By default (with the first syntax), the fast transmission mode is active.



- To send a MIDI message, use the send method:
MIDI.send(NoteOn,64,255,3) will trigger a E note with 255 velocity on channel 3.

Valid MIDI message types are:
NoteOn, NoteOff, ATPoly, CC, PC, ATCanal, PitchBend.

Channel value is between 1 & 16. If 0 (or over 16), the message is OFF and won't be send.


- To get a MIDI message, use the read method:
MIDI.read(channel_filter) will read the serial input buffer for a MIDI message on the channel described by channel_filter, and store it into the class' memory slot.

options: you can use OMNI as the filter parameter to get every MIDI message on the serial port.
The read method returns a boolean, true if a valid MIDI message has been stored in memory, false else.

- How to get back my message?
It's stored in a structure inside the class, you can access its parameters this way:

Code:
getType(): returns the type of the message (valid types are decribed above)
getChannel(): returns the channel of the message (1-16)
getData1(): returns the first data byte (0-127)
getData2(): returns the 2nd data byte (0-127)
check(): returns true if the message is valid, else false.


- A thru method is also available, it uses both read & send methods, you can still have access to the stored message (if any) after the call of this method.


Notes:
- Each call of read will overwrite the previous message stored in the structure.
- The read method is compatible with the fast transmission mode (you can't turn it off though), which means that it will keep the previous status byte in memory while no other one is received.
- Sanguino or ATmega644p users: the MIDI library is enabled on the serial port 1 only, so you can use the main one (serial port 0) for anything else.
« Last Edit: January 12, 2009, 05:40:32 am by Franky » Logged


Norway@Oslo
Offline Offline
Edison Member
*
Karma: 12
Posts: 2033
loveArduino(true);
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Looks interesting!

Will definitly look into this when I've got the time.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Awesome! I was about to start writing my own stuff like this, but I'm glad you've beaten me to it smiley

Is there any chance of this lib getting a serial mode? I've written an app in Processing which reads midi messages from a serial port and turns them into "real" midi, so it would be great if I could use this library for that!
Logged


0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
Developer of the MIDI library
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

What do you mean by "serial mode"? And what do you mean by "real MIDI"? I'm kind of a noob at Processing, so I don't know if I can help you..

MIDI messages are only the orders, what you can hear is generated by a software synth on your computer..


I've written a page on the Playground, with reference and how to use it..

http://www.arduino.cc/playground/Main/MIDILibrary
« Last Edit: January 22, 2009, 11:05:51 am by Franky » Logged


0
Offline Offline
Newbie
*
Karma: 0
Posts: 31
What's that sound?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This looks great - trying it out now

thanx!
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Guys,

Here is an example i've written with this library.  It will output a squarewave on pin 9 corresponding to any midi note entered into a keyboard.
It also has a keyboard buffer which allows note rollovers which took me days to get right as my 'C' skills are very rusty.  I'm still new to all this.

Let me know how you get on.
Code:
#include <MIDI.h>

//variables setup
  byte note;
  byte velocity;
  byte OPTO_PIN = 7;
  byte noteBuf[5]={255,255,255,255,255};  // set up keyboard buffer for 5 notes
  int squareout = 9;  // sound is output on pin 9.  Just connect a speaker to it and gnd.
  int channel = 1; // MIDI channel to respond to (in this case channel 1)

// setup midi note value for timer 1 (16bit resolution)
 unsigned int note_val[]={
34321,32395,30577,28860,27240,25711,24268,22906,21620,20407,19261,18180,17160,16197,15288,14429,13619,12855,12133,11452,10809,10203,9630
,9089,8579,8098,7643,7214,6809,6427,6066,5725,5404,5101,4814,4544,4289,4048,3821,3606,3404,3213,3032,2862,2701,2550,2406,2271,2144,2023,1910,1802,1701
,1606,1515,1430,1350,1274,1202,1135,1071,1011,954,900,850,802,757,714,674,636,600,567,535,505,476
};



//setup: declaring iputs and outputs and begin serial
void setup() {

  pinMode(squareout, INPUT); // silence the squarewave out until we get our first note.

// now set up timer1
  TCCR1A = _BV(COM1A0)
         | _BV(COM1B0)      // toggle OC1B on compare match
         | _BV(WGM10)
         | _BV(WGM11);
  TCCR1B = _BV(WGM12)
         | _BV(WGM13);      // Fast PWM mode, OCR1A as TOP
  OCR1B = 0;            // toggle when the counter is zero
  OCR1A = 34321;      // default timer value (could be anything)
  TCCR1B |= _BV(CS11);      // set prescale to div 8 and start the timer
  
  MIDI_Class MIDI;  // instance the midi class
  MIDI.begin (OPTO_PIN); // not sure I need this
  MIDI.useExternalPowerPin(); // use different 5 volt supply for this implementation

}

//loop: wait for serial data, and interpret the message
void loop () {

      if (MIDI.read(channel) > 0) // must be a real midi message
      {
            if (MIDI.getType() == NoteOn) // get midi not on message
            {
                  note = MIDI.getData1();
                  velocity = MIDI.getData2();
                        if (velocity!=0)
                        {
                    note_insert(note);
                        }
                        else if (velocity==0)  // this will be a note off message
                        {
                          note_delete(note);
                        }
            }
            if (MIDI.getType() == NoteOff) // my midi keyboard never sends this.  It's alway noteon with velocity ==0
            {
                  note = MIDI.getData1();
                  velocity = MIDI.getData2();
                  note_delete(note);
            }
                playNote();
      }
}

void playNote()  // play the note
{  
  int ploop;
    if (noteBuf[0]!=255)
    {
      pinMode (squareout, OUTPUT);
      OCR1A = note_val[noteBuf[ploop]-22];
    }
    else
    {
      pinMode (squareout, INPUT);
    }    
}

// insertion note into note_buff
void note_insert(byte note)
{
      int nloop;
      for(int nloop=4; nloop>0;nloop--)
      {
              noteBuf[nloop]=noteBuf[nloop-1];
      }
   noteBuf[0]=note;  
}
    
// delete note into note_buff.  It should first find the notes
// position and then delete it.
void note_delete(byte note)
{
  int nloop, pos;
  
  for (nloop=0; nloop<=4;nloop++)
  {
      if (noteBuf[nloop] == note) {
          pos=nloop;
          break;
      }
   }  
   for(nloop=pos; nloop<=4;nloop++)
   {
         noteBuf[nloop]=noteBuf[nloop+1];
   }
   noteBuf[nloop-1]=255;
}

« Last Edit: January 27, 2009, 06:03:41 pm by cyberheater » Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
Developer of the MIDI library
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm not quite familiar with timer and assembler sections, but this is what I notice:

- You don't need to instance the MIDI_Class, it's already been done in the library file (the instance name is simply MIDI).
- MIDI.begin(OPTO_PIN), only if you want this pin to be used for hardware MIDI input regulation. Calling useExternalPowerPin later could be avoided if you use the MIDI.begin() method. (powerpin is declared external).

Everything else seems OK..

I may need to work on the constructors and the handling of powerpins, it might be not that clear..
« Last Edit: January 27, 2009, 07:56:36 pm by Franky » Logged


0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Franky.  I'll give it a try later on today.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 1
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hello! i have a couple of questions, it would be great to get your help.

1. the library seems do not work together with the serial monitor, so how can one proof his code for debugging if he uses the library?

2. could any one explain how the cyberheater`s program gets the right note_values (frequencies) in the playNote() ?

i thought i should to write an "if (noteNumber == x) {frequency = y;}" for each note, but he catches the values from array using like "frequency = frequencies[noteNumber - 22]", witch is very elegant, but i do not understand how it works, is there a relation off midi notes numbers and notes frequencies?

Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
Developer of the MIDI library
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@ about the Serial Monitor

The MIDI protocol is based on serial communications. If you want to debug your program using serial port AND use MIDI communications, unless you have 2 serial ports on your chip (such as the ATmega 644p), you can't both send debbuging messages for the serial monitor and send MIDI messages.

If you mean to monitor the MIDI messages you send on the Serial monitor (it's kind like using it as a MIDI device), the MIDI baudrate (31250 bauds per second) is not compatible with Arduino IDE. If you want to check that you are sending the right MIDI data, you can use PureData or a program of this kind (that reads MIDI easily).


@ about note numbers:

Every note has a number, from 21 (A0) to 108 (C8), there you can find the corresponding frequency for each note:



I don't know what cyberheater's program does, but then you can understand where the noteNumber-22 comes from (MIDI notes starts at note 21).
Logged


0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 62
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Im working on some thing similar only different lol
Arduino based midi mixing board that can control Reason and Virtual DJ
far from releasing any code yet
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 2
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I downloaded the library,i want to send sysex and noticed it has sendsysex function,but i cant quite figure out how to use it though, anyone know how to send sysex messages?
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
Developer of the MIDI library
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi, if you want to send SysEx messages, you have to write your bytes in an array (without the 0xF0 "start sysex" and the 0xF7 "end sysex" bytes), and pass the adress of this array to the function:

Code:
byte data[6] = {0x7E,0x00,0x2A,0x2A,0x7F,0x2A};

MIDI.sendSysEx(6,&data); // 6 is the length of your array, &data is the adress of the array.

// will send: F0 7E 00 2A 2A 7F 2A F7.

// Start (F0) and End (F7) are automatically sent respectively at the beginning and the end of the message.


If you want to know more about SysEx (and all the MIDI specification), you can find it there:
http://www.somascape.org/midi/tech/spec.html
« Last Edit: December 11, 2009, 07:58:16 am by Franky » Logged


Pages: [1] 2 3   Go Up
Jump to: