Pages: [1] 2 3 4   Go Down
Author Topic: MIDI library conflict?  (Read 4349 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've been using the MIDI library to 'map' certain notes to program changes and also send MIDI clock.  But recently, I wanted to play MIDI files from an SD card, which is proving to be quite a challenge.  I'm not sure why my code isn't working.  Either the MIDI library functions work or the MIDIfile library functions work.  But not both. 

Note: the MIDIfile library is something I found that plays MIDI files from and SD card.  It works, I know that.  But not in my code with the MIDI library code:

Code:
#include <MIDIFile.h>
#include <MIDI.h>
#include <SdFat.h>

int MIDI_ENABLE = 19;
/*
8/9/13:
 
 Things this sketch does:
 ------------------------------------
 1. Program change up/down
 a.  note 60 is up, note 62 is down
 
 2. "Mute"  parts 1-7b (sets the volume to zero)
 a.  note 64 is part 1
 b.  note 66 is part 2
 c.  note 68 is part 3
 d.  note 70 is part 4
 e.  note 72 is part 5
 f.  note 74 is part 6
 g.  note 76 is part 6a
 h.  note 78 is part 6b
 i.  note 79 is part 7a
 j.  note 80 is part 7b
 
 2.  Can send a hard coded MIDI master clock signal. 
 
 
 
 
 TO DO:
 ----------------------------
Play a MIDI file from SD card, in sync with Master clock.
 
 
 */
//SoftwareSerial SSerial(2, 3);
#define DEBUG(x) Serial.print(x)
#define DEBUGX(x) Serial.print(x, HEX)
int led = 13;



const int chipSelect = 10;
//SD2Card SD;
SdFat SD;
MIDIFile SMF;


/*  this is the MIDIfile section that handles playing of the MIDI files on the SD card*/

void midiCallback(midi_event *pev)
// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
{
  if ((pev->data[0] >= 0x80) && (pev->data[0] <= 0xe0))
  {
    Serial.write(pev->data[0] | pev->channel);
    Serial.write(&pev->data[1], pev->size-1);
  }
  else
    Serial.write(pev->data, pev->size);
 }

void sysexCallback(sysex_event *pev)
// Called by the MIDIFile library when a system Exclusive (sysex) file event needs
// to be processed thru the midi communications interface. MOst sysex events cannot
// really be processed, so we just ignore it here.
// This callback is set up in the setup() function
{
  DEBUG("\nS T");
  DEBUG(pev->track);
  DEBUG(": Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
    DEBUGX(pev->data[i]);
DEBUG(' ');
  }
}

void midiSilence(void)
// Turn everything off on every channel.
// Some midi files are badly behaved and leave notes hanging, so between songs turn
// off all the notes and sound
{
  midi_event ev;

  // All sound off
  // When All Sound Off is received all oscillators will turn off, and their volume
  // envelopes are set to zero as soon as possible.
  ev.size = 0;
  ev.data[ev.size++] = 0xb0;
  ev.data[ev.size++] = 120;
  ev.data[ev.size++] = 0;

  for (ev.channel = 0; ev.channel < 16; ev.channel++)
    midiCallback(&ev);
}

long bpm = 120;
long tempo = 1000/(bpm/60);

long prevmillis = 0;
long interval = tempo/24;    //interval is the number of milliseconds defined by tempo formula.



byte P = 0;  //program or patch number
byte M1 = 0;  //mute for part 1 off
byte M2 = 0;  //mute for part 2 off
byte M3 = 0;  //mute for part 3 off
byte M4 = 0;  //mute for part 4 off
byte M5 = 0;  //mute for part 5 off
byte M6a = 0;  //mute for part 6a off
byte M6b = 0;  //mute for part 6b off
byte M7a = 0;  //mute for part 7a off
byte M7b = 0;  //mute for part 7b off
byte S = 0;   //stop/start bit



Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

part 2 of the code:

Code:
void HandleNoteOn (byte channel, byte note, byte velocity){
  //if note X is sent, send program change control to go up
  //todo:

  if (velocity>0){ //needs to be greater than zero, not equal to,
    switch (note) {
    case 58: //note value of A#3 or 0x3a
      if (S == 0){
        S++;
        MIDI.sendRealTime(Start);
      }
      else
      {
        S--;
        MIDI.sendRealTime(Stop);
        //        MIDI.sendSongPosition(0);
      }
      break;
    case 60: //note value of C4  or 0x3C, handles program change 'up'
      if (P == 127){    //this notes that the program is at the highest of 127 and needs reset to 0
        P=0;
        MIDI.sendControlChange(99,05,10);
        MIDI.sendControlChange(98,107,10);
        MIDI.sendControlChange(06,0,10);
        MIDI.sendControlChange(99,05,10);
        MIDI.sendControlChange(98,108,10);
        MIDI.sendControlChange(06,0,10);
        MIDI.sendProgramChange(P,10);
      }
      else
      {
        P++;
        MIDI.sendControlChange(99,05,10);
        MIDI.sendControlChange(98,107,10);
        MIDI.sendControlChange(06,0,10);
        MIDI.sendControlChange(99,05,10);
        MIDI.sendControlChange(98,108,10);
        MIDI.sendControlChange(06,0,10);
        MIDI.sendProgramChange(P,10);

      }
      break;
    case 62: // note value of D4  or 0x3E, handles program change 'down'.
      if (P== 0){
        P=127;
        MIDI.sendControlChange(99,05,10);
        MIDI.sendControlChange(98,107,10);
        MIDI.sendControlChange(06,0,10);
        MIDI.sendControlChange(99,05,10);
        MIDI.sendControlChange(98,108,10);
        MIDI.sendControlChange(06,0,10);
        MIDI.sendProgramChange(P,10);
      }
      else
      {
        P--;
        MIDI.sendControlChange(99,05,10);
        MIDI.sendControlChange(98,107,10);
        MIDI.sendControlChange(06,0,10);
        MIDI.sendControlChange(99,05,10);
        MIDI.sendControlChange(98,108,10);
        MIDI.sendControlChange(06,0,10);
        MIDI.sendProgramChange(P,10);

      }
      break;
    case 64: //part 1 mute, M variable tells us if it's muted already;  all these mutes actually set the volume to zero, uses the volume NRPNs!
      if (M1==0) {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,1,10);
        MIDI.sendControlChange(06,0,10);  //sets level to zero, for part 1
        //need to toggle it
        M1=1;
      }
      else {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,1,10);
        MIDI.sendControlChange(06,120,10);
        M1=0;
      }

      break;
    case 66:
      if (M2==0) {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,9,10);
        MIDI.sendControlChange(06,0,10);  //sets level to zero, for part 2
        //need to toggle it
        M2=1;
      }
      else {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,9,10);
        MIDI.sendControlChange(06,120,10);
        M2=0;
      }
      break;
    case 68:   //mute for part 3
      if (M3==0) {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,17,10);
        MIDI.sendControlChange(06,0,10);  //sets level to zero, for part 3
        //need to toggle it
        M3=1;
      }
      else {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,17,10);
        MIDI.sendControlChange(06,120,10);
        M3=0;
      }
      break;
    case 70:
      if (M4==0) {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,25,10);
        MIDI.sendControlChange(06,0,10);  //sets level to zero, for part 1
        //need to toggle it
        M4=1;
      }
      else {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,25,10);
        MIDI.sendControlChange(06,120,10);
        M4=0;
      }
      break;
    case 72:
      if (M5==0) {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,33,10);
        MIDI.sendControlChange(06,0,10);  //sets level to zero, for part 1
        //need to toggle it
        M5=1;
      }
      else {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,33,10);
        MIDI.sendControlChange(06,120,10);
        M5=0;
      }
      break;
    case 74:
      if (M6a==0) {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,41,10);
        MIDI.sendControlChange(06,0,10);  //sets level to zero, for part 1
        //need to toggle it
        M6a=1;
      }
      else {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,41,10);
        MIDI.sendControlChange(06,120,10);
        M6a=0;
      }
      break;
    case 76:
      if (M6b==0) {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,49,10);
        MIDI.sendControlChange(06,0,10);  //sets level to zero, for part 1
        //need to toggle it
        M6b=1;
      }
      else {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,49,10);
        MIDI.sendControlChange(06,120,10);
        M6b=0;
      }
      break;
    case 78:
      if (M7a==0) {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,57,10);
        MIDI.sendControlChange(06,0,10);  //sets level to zero, for part 1
        //need to toggle it
        M7a=1;
      }
      else {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,57,10);
        MIDI.sendControlChange(06,120,10);
        M7a=0;
      }
      break;
    case 80:
      if (M7b==0) {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,65,10);
        MIDI.sendControlChange(06,0,10);  //sets level to zero, for part 1
        //need to toggle it
        M7b=1;
      }
      else {
        MIDI.sendControlChange(99,5,10);
        MIDI.sendControlChange(98,65,10);
        MIDI.sendControlChange(06,120,10);
        M7b=0;
      }
      break;


    } //end of switch (note) 



  }

  else{
    switch(note){
    case 58:
      MIDI.sendNoteOff(58,0,10);
      break;
    case 60:
      MIDI.sendNoteOff(60,0,10);
      break;
    case 62:
      MIDI.sendNoteOff(62,0, 10);
      break;   
    case 64:
      MIDI.sendNoteOff(64,0, 10);
      break;     
    case 66:
      MIDI.sendNoteOff(66,0, 10);
      break;     
    case 68:
      MIDI.sendNoteOff(68,0, 10);
      break;     
    case 70:
      MIDI.sendNoteOff(70,0, 10);
      break;     
    case 72:
      MIDI.sendNoteOff(72,0, 10);
      break;     
    case 74:
      MIDI.sendNoteOff(74,0, 10);
      break;   
    case 76:
      MIDI.sendNoteOff(76,0, 10);
      break;     
    case 78:
      MIDI.sendNoteOff(78,0, 10);
      break;     
    case 80:
      MIDI.sendNoteOff(80,0, 10);
      break;           

    }
  }

}

Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

part 3 of my code

Code:
[code]

void setup() {
 

  MIDI.begin(MIDI_CHANNEL_OMNI);
  //Serial.begin(31250);
   pinMode(MIDI_ENABLE, OUTPUT);      // sets the digital pin as output
  digitalWrite(MIDI_ENABLE, HIGH);
  //  MIDI.turnThruOff();

 MIDI.setHandleNoteOn(HandleNoteOn);
  MIDI.sendProgramChange(P,10);
  //   MIDI.setHandleClock ( HandleClock );


  //MIDIfile & SD card setups
  // Initialise SD
  if (!SD.begin(chipSelect, SPI_HALF_SPEED))SD.initErrorHalt();
   Serial.println("begin success");
 
  SD.begin(chipSelect, SPI_HALF_SPEED);

  // Initialise MIDIFile
  SMF.begin(&SD);
  SMF.setMidiHandler(midiCallback);
  SMF.setSysexHandler(sysexCallback);


}

void Sync(){

  unsigned long currentMillis = millis();
  if(currentMillis - prevmillis > interval) {
    // save the last time.
    prevmillis = currentMillis;
    MIDI.sendRealTime(Clock);   
  }

}


void loop () {
  int err = -1;


  SMF.setFilename("SCALE.MID");
SMF.load(); //lost sync at this point.
  err=SMF.load();  //try to print the load result
  SMF.setTempo(bpm);

  if (err != -1)
  {
    DEBUG("\nSMF load Error ");
    DEBUG(err);
    //digitalWrite(SMF_ERROR_LED, HIGH);
//    delay(WAIT_DELAY);
  }
  else
  {
    while (!SMF.isEOF())
    {
      if (SMF.getNextEvent());
    }
    // done with this one
    SMF.close();

  }
 MIDI.read();
   Sync();
}






[/code]
Logged

Sydney, Australia
Offline Offline
Edison Member
*
Karma: 33
Posts: 1285
Big things come in large packages
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

If the MIDI library uses the serial port, then you cannot have the MIDIFile library opening up the same serial port. I am not sure what the effect would be, but it is likely that the last 'open' is the one that works and the other is lost.

What you should probably be doing is calling the generic MIDI 'send a message' library call using the parameters from the MIDIFile callback rather than calling the serial routines (ie, all your MIDI messages are sent through the MIDI library).
Logged

Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

coincidentally, I've been thinking the same thing.  I'm looking at the MIDI library right now to see if there is a generic "send" method...


void MIDI_Class::send   (   kMIDIType    type,
byte    data1,
byte    data2,
byte    channel
)   


The next issue would be that I'd have to find a way to determine the type of message in the data, which can be handled by MIDI.gettype.   
« Last Edit: December 09, 2013, 11:04:54 pm by deseipel » Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

this feels like a dumb question:  I'm using the Ruggeduino MIDI shield, which allows you to change the MIDI IN/OUT to a variety of Digital in/outs.  would that help me here?  I think the MIDI library only supports Serial ... ???
Logged

Sydney, Australia
Offline Offline
Edison Member
*
Karma: 33
Posts: 1285
Big things come in large packages
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

In the MIDI library header file there is this
Code:
#define USE_SERIAL_PORT         Serial      // Change the number (to Serial1 for example) if you want
                                            // to use a different serial port for MIDI I/O.

which means that you can change the serial port used to something else if you want to.

The MIDI library has a generic function to send messages
Code:
void send(kMIDIType type, byte param1, byte param2, byte channel);

which you can invoke in the MIDIfile callback function, which receives as its parameter a pointer to this structure (defined in the MIDIfile header file)
Code:
typedef struct
{
uint8_t track; // the track this was on
uint8_t channel; // the midi channel
uint8_t size; // the number of data bytes
uint8_t data[4]; // the data. Only 'size' bytes are valid
} midi_event;

- The track you can ignore, as it is the track in the SMF being processed.
- The channel should be mapped to the channel in the send() function above. Need to take care that whether the channel is zero or one based in MIDI. MIDIfile is zero based.
- The rest of the data[] are KMIType, param1 and param2.

If you look at MIDI  header file KMIType is
Code:
enum kMIDIType {
NoteOff               = 0x80, ///< Note Off
NoteOn                = 0x90, ///< Note On
AfterTouchPoly        = 0xA0, ///< Polyphonic AfterTouch
ControlChange         = 0xB0, ///< Control Change / Channel Mode
ProgramChange         = 0xC0, ///< Program Change
AfterTouchChannel     = 0xD0, ///< Channel (monophonic) AfterTouch
PitchBend             = 0xE0, ///< Pitch Bend
SystemExclusive       = 0xF0, ///< System Exclusive
TimeCodeQuarterFrame  = 0xF1, ///< System Common - MIDI Time Code Quarter Frame
SongPosition          = 0xF2, ///< System Common - Song Position Pointer
SongSelect            = 0xF3, ///< System Common - Song Select
TuneRequest           = 0xF6, ///< System Common - Tune Request
Clock                 = 0xF8, ///< System Real Time - Timing Clock
Start                 = 0xFA, ///< System Real Time - Start
Continue              = 0xFB, ///< System Real Time - Continue
Stop                  = 0xFC, ///< System Real Time - Stop
ActiveSensing         = 0xFE, ///< System Real Time - Active Sensing
SystemReset           = 0xFF, ///< System Real Time - System Reset
InvalidType           = 0x00    ///< For notifying errors
};

which are basically the MIDI codes for those functions. These should correspond to the first byte in data[].
« Last Edit: December 10, 2013, 03:58:29 am by marco_c » Logged

Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There's also the kMIDIType.gettype()  function in the MIDI library that I might be able to use to get the type and then pass into the MIDI.send(). 


Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm now getting this odd pattern (via MIDI OX)

0033917D   9  --     E0    65    67    1  ---  Pitch Bend
0033917D   9  --     E0    69    6E    1  ---  Pitch Bend
0033917E   9  --     E0    20    73    1  ---  Pitch Bend
0033917F   9  --     E0    75    63    1  ---  Pitch Bend
00339180   9  --     E0    63    65    1  ---  Pitch Bend
00339181   9  --     E0    73    73    1  ---  Pitch Bend
00339181   9  --     E0    0D    0A    1  ---  Pitch Bend
003391B9   9  --     E0    62    65    1  ---  Pitch Bend
003391BA   9  --     E0    67    69    1  ---  Pitch Bend
003391BB   9  --     E0    6E    20    1  ---  Pitch Bend
003391BC   9  --     E0    73    75    1  ---  Pitch Bend
003391BD   9  --     E0    63    63    1  ---  Pitch Bend
003391BE   9  --     E0    65    73    1  ---  Pitch Bend
003391BF   9  --     E0    73    0D    1  ---  Pitch Bend
003391F7   9  --     E0    0A    62    1  ---  Pitch Bend
003391F8   9  --     E0    65    67    1  ---  Pitch Bend
Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

if I take the example MIDIfile sketch and simply add the MIDI.h reference, that sketch ceases to work.  This somewhat confirms a conflict.  The MIDI library is limited to using Serial as a comm method and changing the header file doesn't appear to work, but I'm not sure I'm doing it right either. 
Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There is a MIDI library 4.0 that's in beta and it supports Software Serial.  But I'm at a loss as to how that might help me.....



https://github.com/FortySevenEffects/arduino_midi_library
Logged

Sydney, Australia
Offline Offline
Edison Member
*
Karma: 33
Posts: 1285
Big things come in large packages
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Take all references to serial out of the MIDIfile example sketch that you are using. This was covered in my longer post before.

The MIDIfile library DOES NOT use serial, only the example sketch and it is just an example. You should be handling the callback differently in your code. Again as per my long post above.
Logged

Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

here's an example of how I think the callback should look

Code:
void midiCallback(midi_event *pev)
// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
//  0x80 is a status btye 0x8n (n=channel).  0xe0
{
  if ((pev->data[0] >= 0x80) && (pev->data[0] <= 0xe0))
  {
   // Serial.write(pev->data[0] | pev->channel);
   // Serial.write(&pev->data[1], pev->size-1);

    MIDI.send(MIDI.getType(), (pev->data[1]), (pev->data[2]), (pev->channel));

  }
  else
   // Serial.write(pev->data, pev->size);
  { MIDI.send(MIDI.getType(), (pev->data[1]), (pev->data[2]), (pev->channel));}
 
}


is that what you meant?  I will test this out when I have more free time today...
Logged

Sydney, Australia
Offline Offline
Edison Member
*
Karma: 33
Posts: 1285
Big things come in large packages
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Close. I am not sure that the get type is what you want but try it.
Logged

Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

Offline Offline
Full Member
***
Karma: 0
Posts: 120
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I tried this and I'm getting a weird error during compile:

Code:
void midiCallback(midi_event *pev)
// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
{
  if ((pev->data[0] >= 0x80) && (pev->data[0] <= 0xe0))
{
//SSerial.write(pev->data[0] | pev->channel);
//SSerial.write(&pev->data[1], pev->size-1);

  MIDI.send((pev->data[0]), (pev->data[1]), (pev->data[2]), (pev->channel));
 

}
//else
//SSerial.write(pev->data, pev->size);

 
}



MIDIFile_Play.ino: In function 'void midiCallback(midi_event*)':
MIDIFile_Play:133: error: invalid conversion from 'uint8_t' to 'midi::MidiType'
MIDIFile_Play:133: error: initializing argument 1 of 'void midi::MidiInterface<SerialPort>::send(midi::MidiType, midi::DataByte, midi::DataByte, midi::Channel) [with SerialPort = HardwareSerial]'
Logged

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