Pages: [1]   Go Down
Author Topic: Reading MIDI, converting to polyphonic square wave with Tone library  (Read 637 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi folks,

I'm working on a project using my Arduino Uno to read in MIDI via the serial connection and play a polyphonic tone that mimics the MIDI input. The problem I am having is this: It only seems to be able to play three notes (one after another), and then it stops playing altogether. The arduino is still receiving data (I see the RX LED on the board flashing) but it doesn't play any tones. Polyphonic sounds get very distorted and don't stop when I release the keys. Hoping someone can give me a hand here. I have excluded some of the code as it is irrelevant to the MIDI--only mode 2 (MIDI) is giving me problems.

Code:
#include <avr/pgmspace.h>
#include "Tone.h"

// Output for PWM
int PWMPin = 6;
// External PWM enable
int PWMPower = 5;
// 7-segment pins
int A = 13;
int B = 12;
int C = 11;
int D = 10;
int E = 9;
int F = 8;
int G = 7;

volatile int mode;  // 0 = Output Off, 1 = Manual PWM, 2 = PC MIDI, 3 = External MIDI

//Audio output pins
//Using Analog pins simply becaues they were open
int ch0out = A0;
int ch1out = A1;
int ch2out = A2;
int ch3out = A3;
int ch4out = A4;
int ch5out = A5;

//MIDI notes
Tone ch0;
Tone ch1;
Tone ch2;
Tone ch3;
Tone ch4;
Tone ch5;

// Number of modes
#define modeNum 3
// Highest channel (0-15)
// Will eventually use an array to keep track
// of discrete channels we want to play
#define channelNum 5

// midi commands
#define MIDI_CMD_NOTE_OFF 0x80
#define MIDI_CMD_NOTE_ON 0x90

/* Probably don't need these for my project
#define MIDI_CMD_KEY_PRESSURE 0xA0
#define MIDI_CMD_CONTROLLER_CHANGE 0xB0
#define MIDI_CMD_PROGRAM_CHANGE 0xC0
#define MIDI_CMD_CHANNEL_PRESSURE 0xD0
#define MIDI_CMD_PITCH_BEND 0xE0
*/

// a dummy "ignore" state for commands which
// we wish to ignore.
#define MIDI_IGNORE 0x00

// midi "state" - which data byte we are receiving
#define MIDI_STATE_BYTE1 0x00
#define MIDI_STATE_BYTE2 0x01

// store note frequencies
uint16_t frequency[128] PROGMEM = {8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 19, 21,
                                    22, 23, 24, 26, 28, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52,
                                    55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123,
                                    131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262,
                                    277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554,
                                    587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109,
                                    1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093,
                                    2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
                                    4186, 4435, 4699, 4978, 5274, 5588, 5920, 5920, 6645, 7040, 7459,
                                    7902, 8372, 8870, 9397, 9956, 10548, 11175, 11840, 12544};

void setup() {

  // Power on signal to external PWM circuitry
  pinMode(PWMPower, OUTPUT);
  
  // 7-segment display pins
  pinMode(A, OUTPUT);
  pinMode(B, OUTPUT);
  pinMode(C, OUTPUT);
  pinMode(D, OUTPUT);
  pinMode(E, OUTPUT);
  pinMode(F, OUTPUT);
  pinMode(G, OUTPUT);
  
  // Interrupt to watch for mode-change button press
  attachInterrupt(0, changeMode, RISING);
  mode = 0;
  
  displayMode();
  
  // Make sure power signal to external PWM is off
  digitalWrite(PWMPower, LOW);
}

void loop () {
  
  if (mode == 0) {
  }
  else if (mode == 1) {
  }
  
  /*************************************************
   ** This is where the MIDI signal is processed  **
   *************************************************/
  
  else if (mode == 2) {
    static byte note;                                       // Variable to store note number
    static byte state;                                      // Variable to store state (Byte 1, Byte 2)
    static byte lastByte;                                   // Variable to store previous byte value
    static int cmd = MIDI_IGNORE;                           // Variable to store current command
    static int channel;                                     // Variable to store current channel number
  
    while (Serial.available()) {
      
      // read the incoming byte:
      byte thisByte = Serial.read();                        //Read incoming byte
  
      if (thisByte & 0b10000000) {                          //Is this a command byte?
        if (channel <= channelNum) {                        // Is this one of our channels?
          cmd = thisByte & 0xF0;                            //Save the command
          channel = thisByte & 0x0F;                        //Save the channel number
        } else {
          cmd = MIDI_IGNORE;                                // Otherwise, ignore message
        }
        state = MIDI_STATE_BYTE1;                           // Prepare for Byte 1 (note)
      } else if ((state == MIDI_STATE_BYTE1) && (cmd != MIDI_IGNORE)) {    // Is this data Byte 1?
        if ( cmd==MIDI_CMD_NOTE_OFF ) {                     //If command is for NOTE OFF
          state = MIDI_STATE_BYTE2;                         // expect to receive a velocity byte
        } else if ( cmd == MIDI_CMD_NOTE_ON ) {             // If command is for NOTE ON
          if (channel == 0x00) ch0.begin(ch0out);           // prepare to play note on specified channel
          else if (channel == 0x01) ch1.begin(ch1out);
          else if (channel == 0x02) ch2.begin(ch2out);
          else if (channel == 0x03) ch3.begin(ch3out);
          else if (channel == 0x04) ch4.begin(ch4out);
          else if (channel == 0x05) ch5.begin(ch5out);
        }
        lastByte=thisByte;                                  // save the current note
        state = MIDI_STATE_BYTE2;                           // prepare for Byte2 (velocity)
      } else if ((state == MIDI_STATE_BYTE2) && (cmd != MIDI_IGNORE)) {    // Is this data for Byte 2 (velocity)?
        if (cmd == MIDI_CMD_NOTE_OFF) {
          if (channel == 0x00) ch0.stop();                  // turn off note on current channel
          else if (channel == 0x01) ch1.stop();
          else if (channel == 0x02) ch2.stop();
          else if (channel == 0x03) ch3.stop();
          else if (channel == 0x04) ch4.stop();
          else if (channel == 0x05) ch5.stop();
        } else if (cmd == MIDI_CMD_NOTE_ON) {
          if (thisByte != 0) {                              // if we're actually playing a note
            note = lastByte;                                // get note number
            if (channel == 0x00) ch0.play((unsigned int)pgm_read_word(&frequency[note]));        // Play specified note on specified channel
            else if (channel == 0x01) ch1.play((unsigned int)pgm_read_word(&frequency[note]));
            else if (channel == 0x02) ch2.play((unsigned int)pgm_read_word(&frequency[note]));
            else if (channel == 0x03) ch3.play((unsigned int)pgm_read_word(&frequency[note]));
            else if (channel == 0x04) ch4.play((unsigned int)pgm_read_word(&frequency[note]));
            else if (channel == 0x05) ch5.play((unsigned int)pgm_read_word(&frequency[note]));
          }
        }
        state = MIDI_STATE_BYTE1; // message data complete
      }
    }
  }
  
  /**********************************
   ** End MIDI Processing Section  **
   **********************************/
}

void changeMode(){
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  // If interrupts come faster than 200ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 200)
  {
    Serial.end();
    mode++;
    if (mode > modeNum) {
      mode = 0;
    }
  }
  last_interrupt_time = interrupt_time;
  
  if (mode == 0) {
    stopAllChannels();
  } else if (mode == 1) {
    stopAllChannels();
    digitalWrite(PWMPower, HIGH);
  } else if (mode == 2) {
    digitalWrite(PWMPower, LOW);
    Serial.begin(57600);
  } else if (mode == 3) {
    stopAllChannels();
    digitalWrite(PWMPower, LOW);
  }
  displayMode();
}

//For stopping all MIDI channels
void stopAllChannels() {
  ch0.stop();
  ch1.stop();
  ch2.stop();
  ch3.stop();
  ch4.stop();
  ch5.stop();
}

void displayMode(){
  if (mode == 0) {
    digitalWrite(A, HIGH);
    digitalWrite(B, HIGH);
    digitalWrite(C, HIGH);
    digitalWrite(D, HIGH);
    digitalWrite(E, HIGH);
    digitalWrite(F, HIGH);
    digitalWrite(G, LOW);
  }
  if (mode == 1) {
    digitalWrite(A, LOW);
    digitalWrite(B, HIGH);
    digitalWrite(C, HIGH);
    digitalWrite(D, LOW);
    digitalWrite(E, LOW);
    digitalWrite(F, LOW);
    digitalWrite(G, LOW);
  }
  else if (mode == 2) {
    digitalWrite(A, HIGH);
    digitalWrite(B, HIGH);
    digitalWrite(C, LOW);
    digitalWrite(D, HIGH);
    digitalWrite(E, HIGH);
    digitalWrite(F, LOW);
    digitalWrite(G, HIGH);
  }
  else if (mode == 3) {
    digitalWrite(A, HIGH);
    digitalWrite(B, HIGH);
    digitalWrite(C, HIGH);
    digitalWrite(D, HIGH);
    digitalWrite(E, LOW);
    digitalWrite(F, LOW);
    digitalWrite(G, HIGH);
  }
}

If anyone sees any clear issues, please let me know!
Thanks,
Matt
« Last Edit: July 22, 2014, 07:54:09 pm by DerStrom8 » Logged

Offline Offline
Sr. Member
****
Karma: 16
Posts: 453
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It is only a part of a sketch.
When you write a post, you can see above the text input field a '#' button, those are tags that you can place around a sketch.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It is only a part of a sketch.
When you write a post, you can see above the text input field a '#' button, those are tags that you can place around a sketch.

Yes, the entire sketch will not fit because of the 9600 character limit. It is a long sketch. I tried posting the whole thing at first but it wouldn't let me, so I picked out what I thought was the critical sections.

Here is the full sketch pasted into pastebin: http://pastebin.com/EZBwuUPT

Let me know if you need Tone.h and Tone.cpp. They are not mine though, they are out there on the internet somewhere  smiley-lol

EDIT: Sorry about that, looks like the code tags prevent the code from adding to the character limit. My original post has been fixed to display the entire sketch.
« Last Edit: July 22, 2014, 07:55:03 pm by DerStrom8 » Logged

Offline Offline
Sr. Member
****
Karma: 16
Posts: 453
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, I woul like a link to the Tone library if it is not the default Arduino Tone library.
Is it a special polyphone library ? Those claim the complete Arduino and interrupts are often disabled.

Perhaps this one: https://code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation
That might be outdated, and I think there is no advantage over the Arduino Tone library.
http://arduino.cc/en/reference/tone

To be honest, I can't figure out the flow of the code. But what you describe could be an interrupt problem. And you do use an interrupt. And you do funny things in the interrupt. That are 3 red flags for the interrupt.

Why do you even try to Serial.end() the serial connection ?
The Serial library uses buffers and interrupts, so a call to Serial.end() from inside an interrupt routine might be bad.
The same goes for Tone.stop() that you call from inside an interrupt.

You should do a Serial.begin() in the setup() function and keep the Serial open.
The Serial.end() is for very special cases and only for advanced users. I have never used it myself.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, I woul like a link to the Tone library if it is not the default Arduino Tone library.
Is it a special polyphone library ? Those claim the complete Arduino and interrupts are often disabled.

Perhaps this one: https://code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation
That might be outdated, and I think there is no advantage over the Arduino Tone library.
http://arduino.cc/en/reference/tone

To be honest, I can't figure out the flow of the code. But what you describe could be an interrupt problem. And you do use an interrupt. And you do funny things in the interrupt. That are 3 red flags for the interrupt.

Why do you even try to Serial.end() the serial connection ?
The Serial library uses buffers and interrupts, so a call to Serial.end() from inside an interrupt routine might be bad.
The same goes for Tone.stop() that you call from inside an interrupt.

You should do a Serial.begin() in the setup() function and keep the Serial open.
The Serial.end() is for very special cases and only for advanced users. I have never used it myself.


That's great information, thank you.

The link you posted for the Tone is correct--that is the library I am using.

I used the Serial.end() in my experimentation to see if it fixed the problem, but it did not. Tone.end() is required (I believe) to stop the tone once it's started. I believe that's the key difference between the standard tone() function in Arduino and the Tone library--this one allows a note to continue to play until a stop() command is sent to it.

I also was thinking it could be an interrupt issue--I've been running into a few of those here and there--but I have yet to figure out what the issue is. One of my interrupts is tripped when a push-button, which is used to change the mode, is pressed. It is connected to Interrupt 0. However, since the mode never seems to be changed by accident, I didn't think it was tripping the interrupt when it wasn't supposed to. It could still be a timer issue though, I suppose....?
« Last Edit: July 22, 2014, 08:38:12 pm by DerStrom8 » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry for the double-post, but I thought I'd post the (slightly) updated code which takes your suggestions into account. I removed the Serial.end() statements and added the Serial.begin() statement inside the setup() (I did have it there originally, not sure what happened to it....).

Code:
#include <avr/pgmspace.h>
#include "Tone.h"

// Output for PWM
int PWMPin = 6;
// External PWM enable
int PWMPower = 5;
// 7-segment pins
int A = 13;
int B = 12;
int C = 11;
int D = 10;
int E = 9;
int F = 8;
int G = 7;

volatile int mode;  // 0 = Output Off, 1 = Manual PWM, 2 = PC MIDI, 3 = External MIDI

//Audio output pins
//Using Analog pins simply becaues they were open
int ch0out = A0;
int ch1out = A1;
int ch2out = A2;
int ch3out = A3;
int ch4out = A4;
int ch5out = A5;

//MIDI notes
Tone ch0;
Tone ch1;
Tone ch2;
Tone ch3;
Tone ch4;
Tone ch5;

// Number of modes
#define modeNum 3
// Highest channel (0-15)
// Will eventually use an array to keep track
// of discrete channels we want to play
#define channelNum 5

// midi commands
#define MIDI_CMD_NOTE_OFF 0x80
#define MIDI_CMD_NOTE_ON 0x90

/* Probably don't need these for a Tesla coil
 #define MIDI_CMD_KEY_PRESSURE 0xA0
 #define MIDI_CMD_CONTROLLER_CHANGE 0xB0
 #define MIDI_CMD_PROGRAM_CHANGE 0xC0
 #define MIDI_CMD_CHANNEL_PRESSURE 0xD0
 #define MIDI_CMD_PITCH_BEND 0xE0
 */

// a dummy "ignore" state for commands which
// we wish to ignore.
#define MIDI_IGNORE 0x00

// midi "state" - which data byte we are receiving
#define MIDI_STATE_BYTE1 0x00
#define MIDI_STATE_BYTE2 0x01

// store note frequencies
uint16_t frequency[128] PROGMEM = {
  8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 19, 21,
  22, 23, 24, 26, 28, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52,
  55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123,
  131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262,
  277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554,
  587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109,
  1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093,
  2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
  4186, 4435, 4699, 4978, 5274, 5588, 5920, 5920, 6645, 7040, 7459,
  7902, 8372, 8870, 9397, 9956, 10548, 11175, 11840, 12544};

void setup() {

  // Power on signal to external PWM circuitry
  pinMode(PWMPower, OUTPUT);

  // 7-segment display pins
  pinMode(A, OUTPUT);
  pinMode(B, OUTPUT);
  pinMode(C, OUTPUT);
  pinMode(D, OUTPUT);
  pinMode(E, OUTPUT);
  pinMode(F, OUTPUT);
  pinMode(G, OUTPUT);

  // Interrupt to watch for mode-change button press
  attachInterrupt(0, changeMode, RISING);
  mode = 0;
  displayMode();

  // Make sure power signal to external PWM is off
  digitalWrite(PWMPower, LOW);
  Serial.begin(57600);
}

void loop () {

  if (mode == 0) {
  }
  else if (mode == 1) {
  }

  /*************************************************
   ** This is where the MIDI signal is processed  **
   *************************************************/

  else if (mode == 2) {
    static byte note;                                       // Variable to store note number
    static byte state;                                      // Variable to store state (Byte 1, Byte 2)
    static byte lastByte;                                   // Variable to store previous byte value
    static int cmd = MIDI_IGNORE;                           // Variable to store current command
    static int channel;                                     // Variable to store current channel number

    while (Serial.available()) {

      // read the incoming byte:
      byte thisByte = Serial.read();                        //Read incoming byte

      if (thisByte & 0b10000000) {                          //Is this a command byte?
        if (channel <= channelNum) {                        // Is this one of our channels?
          cmd = thisByte & 0xF0;                            //Save the command
          channel = thisByte & 0x0F;                        //Save the channel number
        }
        else {
          cmd = MIDI_IGNORE;                                // Otherwise, ignore message
        }
        state = MIDI_STATE_BYTE1;                           // Prepare for Byte 1 (note)
      }
      else if ((state == MIDI_STATE_BYTE1) && (cmd != MIDI_IGNORE)) {    // Is this data Byte 1 (note)?
        if ( cmd==MIDI_CMD_NOTE_OFF ) {                     //If command is for NOTE OFF
          state = MIDI_STATE_BYTE2;                         // expect to receive a velocity byte
        }
        else if ( cmd == MIDI_CMD_NOTE_ON ) {               // If command is for NOTE ON
          if (channel == 0x00) ch0.begin(ch0out);           // prepare to play note on specified channel
          else if (channel == 0x01) ch1.begin(ch1out);
          else if (channel == 0x02) ch2.begin(ch2out);
          else if (channel == 0x03) ch3.begin(ch3out);
          else if (channel == 0x04) ch4.begin(ch4out);
          else if (channel == 0x05) ch5.begin(ch5out);
        }
        lastByte=thisByte;                                  // save the current note
        state = MIDI_STATE_BYTE2;                           // prepare for Byte2 (velocity)
      }
      else if ((state == MIDI_STATE_BYTE2) && (cmd != MIDI_IGNORE)) {    // Is this data Byte 2 (velocity)?
        if (cmd == MIDI_CMD_NOTE_OFF) {
          if (channel == 0x00) ch0.stop();                  // turn off note on current channel
          else if (channel == 0x01) ch1.stop();
          else if (channel == 0x02) ch2.stop();
          else if (channel == 0x03) ch3.stop();
          else if (channel == 0x04) ch4.stop();
          else if (channel == 0x05) ch5.stop();
        }
        else if (cmd == MIDI_CMD_NOTE_ON) {
          if (thisByte != 0) {                              // if we're actually playing a note
            note = lastByte;                                // get note number
            if (channel == 0x00) ch0.play((unsigned int)pgm_read_word(&frequency[note]));        // Play specified note on specified channel
            else if (channel == 0x01) ch1.play((unsigned int)pgm_read_word(&frequency[note]));
            else if (channel == 0x02) ch2.play((unsigned int)pgm_read_word(&frequency[note]));
            else if (channel == 0x03) ch3.play((unsigned int)pgm_read_word(&frequency[note]));
            else if (channel == 0x04) ch4.play((unsigned int)pgm_read_word(&frequency[note]));
            else if (channel == 0x05) ch5.play((unsigned int)pgm_read_word(&frequency[note]));
          }
        }
        state = MIDI_STATE_BYTE1; // message data complete
      }
    }
  }

  /**********************************
   ** End MIDI Processing Section  **
   **********************************/
}

void changeMode(){
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  // If interrupts come faster than 200ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 200)
  {
    mode++;
    if (mode > modeNum) {
      mode = 0;
    }
  }
  last_interrupt_time = interrupt_time;

  if (mode == 0) {
    stopAllChannels();
  }
  else if (mode == 1) {
    stopAllChannels();
    digitalWrite(PWMPower, HIGH);
  }
  else if (mode == 2) {
    digitalWrite(PWMPower, LOW);
  }
  else if (mode == 3) {
    stopAllChannels();
    digitalWrite(PWMPower, LOW);
  }
  displayMode();
}

//For stopping all MIDI channels
void stopAllChannels() {
  ch0.stop();
  ch1.stop();
  ch2.stop();
  ch3.stop();
  ch4.stop();
  ch5.stop();
}

void displayMode(){
  if (mode == 0) {
    digitalWrite(A, HIGH);
    digitalWrite(B, HIGH);
    digitalWrite(C, HIGH);
    digitalWrite(D, HIGH);
    digitalWrite(E, HIGH);
    digitalWrite(F, HIGH);
    digitalWrite(G, LOW);
  }
  if (mode == 1) {
    digitalWrite(A, LOW);
    digitalWrite(B, HIGH);
    digitalWrite(C, HIGH);
    digitalWrite(D, LOW);
    digitalWrite(E, LOW);
    digitalWrite(F, LOW);
    digitalWrite(G, LOW);
  }
  else if (mode == 2) {
    digitalWrite(A, HIGH);
    digitalWrite(B, HIGH);
    digitalWrite(C, LOW);
    digitalWrite(D, HIGH);
    digitalWrite(E, HIGH);
    digitalWrite(F, LOW);
    digitalWrite(G, HIGH);
  }
  else if (mode == 3) {
    digitalWrite(A, HIGH);
    digitalWrite(B, HIGH);
    digitalWrite(C, HIGH);
    digitalWrite(D, HIGH);
    digitalWrite(E, LOW);
    digitalWrite(F, LOW);
    digitalWrite(G, HIGH);
  }
}
Logged

Offline Offline
Sr. Member
****
Karma: 16
Posts: 453
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The Arduino Tone library can play only one tone and the library you use can do more at the same time.
Do you at some point use more than 2 tones ? Can you try with a maximum of two tones ?
If that doesn't help, I'm out of ideas  smiley-red
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The Arduino Tone library can play only one tone and the library you use can do more at the same time.
Do you at some point use more than 2 tones ? Can you try with a maximum of two tones ?
If that doesn't help, I'm out of ideas  smiley-red

That is actually the exact reason why I chose this particular Tone library. originally I used the simple tone() function, but I had to select just a single channel to play, which often didn't work out very well.

When receiving MIDI data I do expect to receive more than 2 tones, yes. One example would be two instruments playing at the same time (even though they're on different channels), and another example would be a chord on a single instrument. My experimentation to this point has consisted of the Hairless MIDI-Serial bridge and the Virtual MIDI Piano Keyboard (VMPK) to send MIDI data over USB to the Arduino. The first three notes I play work fine (assuming they're played one at a time), but for some reason, after that third note, it simply stops playing anything. Changing the channel on the VMPK does not help either, so it's not a channel-specific problem.

I tried to comment the code the best I could to help explain how it works. It's based on some code written by Greg Kennedy (who posted it here on the Arduino forums somewhere), though his code was designed to use the Arduino tone library, and thus was not suitable for polyphonic MIDI inputs. Effectively, the code retrieves the current byte being sent over Serial and determines whether it is a command byte or a data byte. If it is a command byte, it stores the command and the channel. Otherwise, if it's not a command byte, it checks to see which byte it is--Byte 1 (note data) or Byte 2 (velocity data). The values are saved and then the Tone library plays the specified note on the specified channel.

Once again, don't worry about the rest of the code--there are several modes, and three of them have nothing to do with this MIDI input. I am only concerned with Mode 2 at this point, which is the mode that I have placed comments around (at the beginning and end) to highlight the code in question.

Changing the "channelNum" to "1" (2 channels) does not appear to help.

It actually occurred to me, I may be confusing multiple tones from one channel with multiple tones from multiple channels. Or perhaps it's just the terminology I'm using in the code is misleading--using "ch1" instead of, say, "tone1"? Just curious what your take on it is. "ch1.stop()" does not actually stop the channel, it just stops the specified tone. If I am not mistaken, I should be able to access all of the channels, but I can only play 6 tones at once (between all of the channels).

Right now I'm just thinking out loud, and I'm hoping it might spark some ideas.

Thanks for looking it over, I do appreciate your help!
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry for making another double-post, but I think I'd better start over again from the beginning.

I have the code to read in each byte from the serial (MIDI) signal. That part is all fine and dandy. The part I need help with is using the Tone library (not the standard Arduino tone, but the one mentioned earlier) to play multiple sounds at once that mimic the MIDI input. Effectively I need to convert the MIDI to polyphonic square wave audio.

If anyone needs to see how the library is normally used, here's an example:

Quote
#include "Tone.h"

Tone tone1;
Tone tone2;
Tone tone3;

void setup()
{
  tone1.begin(A0);
  tone2.begin(A1);
  tone3.begin(A2);
  tone1.play(NOTE_C4);
  tone2.play(NOTE_E4);
  tone3.play(NOTE_G4);
}

void loop()
{
}


I only have 6 I/O ports to work with here, all the rest are being used elsewhere.
Logged

Pages: [1]   Go Up
Jump to: