Read and Play MIDI file in arduino

I'm starting with Arduino and I need developer a project for read and play a MIDI file in Arduino UNO.

If anypeople can help me, I'll make me feel very happy! :slight_smile:

Best Regards

What do you mean by:-

read and play a MIDI file in Arduino UNO.

Where is the MIDI file to be stored and are you expecting the arduino to generate the sound?
If so you will be disappointed as the arduno can't produce many tones let alone anything approaching the MIDI GM sound set.

Although you cannot generate any quality audio with the UNO alone, it is possible to use something else - the midi shield!
http://www.skpang.co.uk/catalog/midi-shield-p-669.html

That should sort out the issues of bad sound quality.

Onions.

Thanks Grumpy_Mike and Oniosn :slight_smile:

My ideia is: read a MIDI file maybe in my computer (I started with arduino in few weeks :S) and after play in piezo buzzer.

Example of OUTPUT: Play a melody with Arduino - YouTube

But I don't undertand how Interpret the Structure of MIDI file and if I can put my MIDI file in my computer.

Best Regards

You need something on your PC that reads MIDI files and outputs MIDI data. You also need a MIDI interface on your PC.
Then you need a MIDI interface on the Arduino and some software.
This project:-
http://www.thebox.myzen.co.uk/Hardware/Glockenspiel.html
fires solenoids when it receives MIDI data but you can modify it so that it generates a tone instead using the tone function.

Maybe this project ttymidi

But I don't understand how send the mid music

:frowning:

But I don't understand how send the mid music

That is what I said at the start, you need an application on your computer to read a MIDI file and output it. There are lots around most MIDI musics apps allow you to do this.

Yes, I have timidity (it's works), but in tutorial don't show that send the MIDI file (messages).

# start ttymidi
ttymidi -s /dev/ttyUSB0 &

# start some ALSA compatible MIDI 
# program (timidity, in this case)
timidity -iA &                    

# list available MIDI input clients
aconnect -i                       

# list available MIDI output clients
aconnect -o                       

# connect
aconnect 128:0 129:0

# ...where 128 and 129 are the client 
# numbers for ttymidi and timidity,
# found with the commands above

But where is the MIDI file?
example: timidity abba.mid (with tty already connected com arduino)
and after my buzzer in arduino singing abba :slight_smile:

Thanks :slight_smile:

Timidity does not do anything with MIDI files, it acts as a conduit for you to put MIDI data through. You need an application that handles MIDI files and you point it at timidity an tell it to play the file.

Some idea how to do this?

I just want:
1 - Create a program in arduino that understand MIDI music file and reproduce.
2 - A way to put(send) MIDI file (messages, bytes, WHETEVER) to arduino.
3 - My piezo buzzer singing simple sound.

:S
I will go cry!!!

This is very difficulty

2 - A way to put(send) MIDI file (messages, bytes, WHETEVER) to arduino.

I use Garage Band, do you have that?
No well you need to get a program on your PC to do this. I am sorry but you haven't even said what operating system you are using.
You need to search and find a MIDI sequencer or other sort of file player.
I am not sure how many time I have to tell you?

This is very difficulty

You are making it difficult for your self by not reading my replies.

You are making it difficult for your self by not reading my replies.

Ok, sorry and thanks a lot about your help.

No well you need to get a program on your PC to do this. I am sorry but you haven't even said what operating system you are using.

My O.S. is ubuntu 11.10.

I thought that timitidy is a file player

[My O.S. is ubuntu 11.10](http://My O.S. is ubuntu 11.10)
That makes all the difference, sorry but I know little about Linux.

I thought that timitidy is a file player

The link you posted is for ttymidi, the first sentence says

ttymidi is a GPL-licensed program that allows external serial devices to interface with ALSA MIDI applications.

What you need is an ALSA MIDI applications, that is the thing that reads the file. Sorry but I can't find one for you due to my lack of experience with you operating system.

I tried to use this http://lau.linuxaudio.org/TiMidity-howto.html.

I just finished a project that might be do something similar to what you are asking here.
It takes a midi file (saved as musicxml) and allows you to select up to three parts that will be played on the arduino using the three timers.

This post has more information: http://jarv.org/2011/11/arduino-music-from-a-midi-file/
I also used this lib to protype creating a cheap musical greeting card using an attiny - http://jarv.org/2011/11/custom-musical-greeting-card-for-less-than-5/

How a midi conversion turned out for the SMB theme - PlayTune Mario - YouTube

Neat project! I'm trying to accomplish something similar. Specifically, I'd like the Arduino to output midi signals to my SparkFun Music Instrument Shield SparkFun Music Instrument Shield - DEV-10587 - SparkFun Electronics. Ideally, I'd like to combine some "canned" midi signals (including a variety of different drum loops) with some "live" signals generated from sensors. I had some success using the NewSoftSerial library to feed MIDI data to the shield.

Here's a sketch I did (based on one of the Sparkfun example sketches) which plays a simple beat. It reads the notes from characters in an array ("b" is kick, "t" is hi-hat, "k" is snare, space is a rest). It reads the delays attached to each note from another array, which in this case I've set up to allow swing on the 16ths.

#include <NewSoftSerial.h>
NewSoftSerial mySerial(2, 3); //Soft TX on 3, we don't use RX in this code

byte note = 0; //The MIDI note value to be played
byte resetMIDI = 4; //Tied to VS1053 Reset line
byte ledPin = 13; //MIDI traffic inidicator
int instrument = 0;


int length;
char beat1[] = { "bbttktttbktbkttt"};  //Beat pattern #1
char beat2[] = { "bttbb   b  bb   "};    //Beat pattern #2
bool tempoUp = 1;
float swing = 0.33;                           //0 is no swing; 0.33 should approximate typical "triplet" swing (2/3 + 1/3)
float beats[] = { 1+swing, 1-swing, 1+swing, 1-swing, 1+swing, 1-swing, 1+swing, 1-swing,
  1+swing, 1-swing, 1+swing, 1-swing, 1+swing, 1-swing, 1+swing, 1-swing };  

float tempo = 60;

void playNote(char note, float duration) {
  char names[] = { 'b', 't', 'k' };
  int tones[] = { 35, 42, 38 };

  for (int i = 0; i < 3; i++) {
    if (names[i] == note) {
      Serial.print(note);
      noteOn(0,tones[i],100);
      // 16th note in ms         1 beat             1 min               60000 ms    
      //  = 15000/tempo        ------------      x -------          x  ----------
      //                               4 16ths notes     beats (bpm)     min          
      delay(duration*15000/tempo);  //duration adjustment allows for swing
      noteOff(0,tones[i],0);
    }
  }
}

void playLoop(char beat[]) {
  Serial.print("Swing:");
  Serial.print(float(swing));
  Serial.print(" ");
 
  Serial.print("Tempo:");
  Serial.print(int(tempo));
  Serial.print(" ");
  for (int i = 0; i < 16; i++) {
    if (beat[i] == ' ') {
      Serial.print("-");
      delay(beats[i]*15000/tempo);  //See above; each 16th's length in ms is 15000/tempo
    } else {
      playNote(beat[i], beats[i]);
    }
    delay(0);
    if (tempoUp) { tempo *= 1.005; }  //accelerando...
    else { tempo /= 1.005; }          //or decelerando
    if (tempo > 150) { tempoUp = 0; } //slow down after 150 bpm
    if (tempo < 50) { tempoUp = 1; }  //speed up after 50 bpm
  }
  Serial.println("");
} 


void setup() {
  Serial.begin(57600);
  //Setup soft serial for MIDI control
  mySerial.begin(31250);

  //Reset the VS1053
  pinMode(resetMIDI, OUTPUT);
  digitalWrite(resetMIDI, LOW);
  delay(100);
  digitalWrite(resetMIDI, HIGH);
  delay(100);
}

void loop() {
  talkMIDI(0xB0, 0x07, 50);      //0xB0 is channel message, set channel volume to near max (127)
  talkMIDI(0xB0, 0, 0x78);       //Bank select drums
  talkMIDI(0xC0, instrument, 0); //Select instrument; doesn't matter which for drums 
  
  playLoop(beat1);
  playLoop(beat2);
  
  for (int i = 0; i < 16; i++) {  //Set swing for each pair in beats[]
    if (i%2 == 0) { beats[i] = 1+swing; }
    else { beats[i] = 1-swing; }
  }
}


void noteOn(byte channel, byte note, byte attack_velocity) {
  talkMIDI( (0x90 | channel), note, attack_velocity);
}

void noteOff(byte channel, byte note, byte release_velocity) {
  talkMIDI( (0x80 | channel), note, release_velocity);
}

//Plays a MIDI note. Doesn't check to see that cmd is greater than 127, or that data values are less than 127
void talkMIDI(byte cmd, byte data1, byte data2) {
  digitalWrite(ledPin, HIGH);
//  Serial.write(cmd);
//  Serial.write(data1);
  mySerial.print(cmd, BYTE);
  mySerial.print(data1, BYTE);
  if( (cmd & 0xF0) <= 0xB0 || (cmd & 0xF0) == 0xE0)
//     Serial.write(data2);
  mySerial.print(data2, BYTE);
  digitalWrite(ledPin, LOW);
}

To be continued in next post....

1 Like

[Continuation of my prior post]
That seems to work well enough, but one thing I'd really like to improve is the "feel" of the beat and the ability to plug in more complicated sequences. As currently set up, each beat is locked in a static "grid" and I can't easily add additional subdivisions (such as 32nd notes or triplets), or play notes longer than a 16th. Also, the timing might be a bit off since calculations happen between each delay. I can't tell.

So, for my next step I'd like to play a MIDI file, or perhaps a sanitized, simplified version thereof, using a timer instead of delays. I've seen examples of how to accomplish a single task using a timer, but I get confused when trying to apply to a long list of events. In pseudo-code, I'd like to:

  1. Load 2D array with a sequence of { int tick, int note, int velocity } lines, with a noteOn and noteOff for each note
  2. Loop through the array as a timer counts ticks for each measure.
  3. With each array item, check if the tick has passed. If so, trigger the noteOn. If not, wait a bit (1ms?) and check that point in the array again. (I think that triggering within about 10ms of the desired time will be good enough for me.)
  4. If the timer count hits 384 (384 = 4 beats x 4 subdivisions x 24 ticks; I think that's a tenth as many as the MIDI file I was working from), reset it to zero and go back to step 1.

Is that a sensible way to set it up? How could I optimize it better to allow more cycles for reading and interpreting input?

I've given a first shot at coding this, but I'm getting errors relating to pointers (which I'm trying to learn about) in the loop() section where I look up the note in the array (2nd field) based on the tick in the array (1st field). Should I be setting this up differently, or would it be a simple syntax tweak to fix this?

#include <NewSoftSerial.h>
NewSoftSerial mySerial(2, 3); //Soft TX on 3, we don't use RX in this code

byte note = 0; //The MIDI note value to be played
byte resetMIDI = 4; //Tied to VS1053 Reset line
byte ledPin = 13; //MIDI traffic indicator
int instrument = 0;
int scale_major[] = {0, 2, 4, 5, 7, 9, 11};        //one octave of major scale
int scale_minpent[] = {0, 2, 3, 5, 6, 7, 10};     //minor pentatonic scale
int scale_consonant[] = {0, 2, 4, 7, 9, 12, 14}; //major pentatonic scale plus octave and 9th (to be same array length as others)


int drum_motown[100][3] = //Tick (out of 384 per bar), note, velocity
{                         //This is an attempt to adapt data from an actual MIDI file, with
  { 0,54,94 },            //matching 0-velocity lines in place of noteOff messages
  { 0,48,97 },
  { 8,54,0 },
  { 48,54,75 },
  { 51,48,0 },
  { 54,54,0 },
  { 96,50,106 },
  { 96,54,96 },
  { 103,54,0 },
  { 127,50,0 },
  { 144,54,73 },
  { 153,54,0 },
  { 171,50,92 },
  { 177,50,0 },
  { 192,54,82 },
  { 201,54,0 },
  { 219,48,85 },
  { 240,54,84 },
  { 240,48,0 },
  { 246,54,0 },
  { 267,48,86 },
  { 273,48,0 },
  { 288,54,101 },
  { 288,50,103 },
  { 296,54,0 },
  { 318,50,0 },
  { 336,54,77 },
  { 344,54,0 },
  { 363,50,70 },
  { 368,50,0 }
};
int drumSpot = 0;
int drumSpotTime = 0;

long curTime = 0;
int timeclock96 = 0;  //96 ticks per beat, based on 4 subdivs per beat and 24 ticks per subdiv (not 240 like the source midi file)




void setup() {
  Serial.begin(57600);

  //Setup soft serial for MIDI control
  mySerial.begin(31250);

  //Reset the VS1053
  pinMode(resetMIDI, OUTPUT);
  digitalWrite(resetMIDI, LOW);
  delay(100);
  digitalWrite(resetMIDI, HIGH);
  delay(100);
  
  talkMIDI(0xB0, 0x07, 30); //0xB0 is channel message for channel 1 (0), set channel volume to 30
  talkMIDI(0xB0, 0, 0x00); //Bank select GM1
  
  talkMIDI(0xB9, 0x07, 50); //0xB0 is channel message for channel 10 (9), set channel volume to 50
  talkMIDI(0xB9, 0, 0x78); //Bank select drums
  
  curTime = millis();
}




void loop() {
  timeclock96 = (millis() - curTime)/96;
  Serial.println(timeclock96); 
  for (int j = drumSpot; j<100; j++) {
    drumSpotTime = (int)drum_motown[drumSpot,0]; 
    if (drumSpotTime < timeclock96) {
      noteOn(0, (byte)10, drum_motown[drumSpot,1], drum_motown[drumSpot,2]);   //ERROR HERE: invalid conversion from int* to byte
    }
  if (timeclock96 >= 96) {
    curTime = millis();
  }
  
//  playNoodling(0, random(48,59), scale_consonant, 180, 0);

}


void playNoodling(byte channel, int key, int scale[], float tempo, int instrument) {
  Serial.print(" Instrument: ");
  Serial.println(instrument, DEC);
  talkMIDI(0xC0, instrument, 0); //Set instrument number. 0xC0 is a 1 data byte command
  talkMIDI(0xCA, 30, 0); //Set instrument number. 0xC0 is a 1 data byte command
  
  for (int i = 0; i < 16; i++) {
    int note = key + scale[random(0, 6)];
    noteOn(1, channel, note, 80);
    noteOn(0, 10, drum_pattern1[i], 50);
    
    //1 minute      60 seconds    1000 ms                
    //---------  x  ---------  x  --------
    //120 Beats     1 minute      1 second
    delay(60000/tempo);
    noteOff(1, channel, note, 0);
    noteOff(0, 10, drum_pattern1[i], 0);
  }
}



void allOff(byte channel) {
  talkMIDI ( (0xB0 | channel), 123, 0);
}

//Send a MIDI note-on message.  Like pressing a piano key
//channel ranges from 0-15
void noteOn(boolean instr, byte channel, byte note, byte attack_velocity) {
  talkMIDI( (0x90 | channel), note, attack_velocity);
}

//Send a MIDI note-off message.  Like releasing a piano key
void noteOff(boolean instr, byte channel, byte note, byte release_velocity) {
  talkMIDI( (0x80 | channel), note, release_velocity);
}

//Plays a MIDI note. Doesn't check to see that cmd is greater than 127, or that data values are less than 127
void talkMIDI(byte cmd, byte data1, byte data2) {
  digitalWrite(ledPin, HIGH);
//  Serial.write(cmd);
//  Serial.write(data1);
  mySerial.print(cmd, BYTE);
  mySerial.print(data1, BYTE);

  //Some commands only have one data byte. All cmds less than 0xBn have 2 data bytes 
  //(sort of: http://253.ccarh.org/handout/midiprotocol/)
  if( (cmd & 0xF0) <= 0xB0)
//     Serial.write(data2);
   mySerial.print(data2, BYTE);

  digitalWrite(ledPin, LOW);
}

Many thanks in advance to anyone who can provide a suggestion as to how to improve this code! My apologies that it's so scrappy!

This may be your Answer: Midi To Arduino

try this one.it worked for me

Here is the link for what I have made by reading MIDI files and playig drum:

Full Video: Imagine Dragons - Believer - Arduino Drum - YouTube
Code and Everything: Imagine Dragons – Believer – Arduino – TinAndJar