Go Down

Topic: MIDI CC messages: how to read? (Read 145 times) previous topic - next topic

Barito

Hi, thanks for reading.

I am trying to translate MIDI Control Change messages to volts in order to control the filter cutoff frequency of a synthesizer of mine (Korg MS10).
In particular, I am trying to listen to CC 19 and map it to a voltage.

Looking at this reference...

 *  MIDI messages table:
 *    Message                      Status    Data 1                  Data 2
 *    Note Off                      8n        Note Number          Velocity
 *    Note On                      9n        Note Number           Velocity
 *    Polyphonic Aftertouch    An        Note Number          Pressure
 *    Control Change             Bn        Controller Number   Data
 *    Program Change           Cn        Program Number     Unused
 *    Channel Aftertouch        Dn        Pressure                Unused
 *    Pitch Wheel                  En        LSB                       MSB   
 *   
 * Key
 * n is the MIDI Channel Number (0-F)
 * LSB is the Least Significant Byte
 * MSB is the Least Significant Byte
 * There are several different types of controller messages.

and in particula at his line ...

*    Message                      Status    Data 1                     Data 2
*    Control Change             Bn        Controller Number      Data
 
... I was almost sure that the "Controller Number" was my "19". Looks like it's not the case.

Here is the sketch:

Code: [Select]


//set at your will ...
#define MIDI_CHANNEL 0 //the MIDI channel you want your box to listen to (0 = 1, 1 = 2 etc.)
#define CC_NUMBER 19 //MIDI CC number - Filter Cutoff

byte CCPin = 6; // 978Hz (pin D5 and D6 in ATmega 8, 168 or 328)

byte messageByte;
byte data1;
byte data2;

byte CCByte;
byte CCValue;
byte midi_CC = 0xB0 + MIDI_CHANNEL;// control change (in general 0xBn, with n = 0-F for channels 1-16)
int CCOut;

void setup() {
pinMode(CCPin, OUTPUT);
Serial.begin(31250);//baud rate for MIDI
}

void loop() {
  do{
    if (Serial.available()){
      MIDIread();   
      if (messageByte == midi_CC){
        CCByte = data1;
        CCValue = data2;
        if(CCByte == CC_NUMBER){
          CCOut = constrain(map(CCValue, 0, 127, 0, 255), 0, 255);
          analogWrite(CCPin, CCOut);
        }       
      }     
     }
    }
  while (Serial.available() > 2);//when at least three bytes available
}

void MIDIread(){
messageByte = Serial.read();//read first byte
data1 = Serial.read();//read next byte
data2 = Serial.read();//read final byte 
}



My tests showed that this condition here:

 if(CCByte == CC_NUMBER)

... is never satisfied. If i remove that condition, every single CC makes Arduino output a (RC D/A) voltage out of pin D6, so the circuit is ok.

any thought?

I am almost sure I am misleading that "Controller Number " I see there: it must be referred to something else, not the CC i set.

Grumpy_Mike

The logic of that code says, if one byte has been received then read three bytes. This is not what you want to do.

Barito

thanks Grumpy. Sorry for being noob, but am I losing a byte by checking for Serial.available() or where? I am not a coder so some easy task could be not that easy for me

(thanks a lot!)

Grumpy_Mike

Quote
but am I losing a byte by checking for Serial.available()
No.

That code looks to see if their is one or more bytes in the serial buffer. Let's say their is one. Then it calls the MIDIread function that proceeds to read three bytes irrespective of whether their are three bytes to read. I think you can see that is not want you want to do.

Remove the do .... while structure it is not doing what you think it is doing, and replace the if (Serial.available()) with if (Serial.available()>2)

Barito


PieterP

This approach is vulnerable to framing errors.
Try the following:
Code: [Select]
void setup() {
  Serial.begin(31250);
}

void loop() {
  static uint8_t header = 0;
  static uint8_t data1 = 0;
  static bool third_byte = false;

  if (Serial.available() >= 1) {
    uint8_t data = Serial.read();
    if (data & 0b10000000) { // if msb == 1 (header byte)
      header = data;
      third_byte = false;
    } else if ((header & 0xF0) == 0xB0) {
      if (!third_byte) {
        data1 = data;
        third_byte = true;
      } else {
        third_byte = false;
        uint8_t data2 = data;
        uint8_t channel = header & 0x0F;
        handleCC(channel, data1, data2);
      }
    }
  }
}

void handleCC(uint8_t channel, uint8_t data1, uint8_t data2) {
  // Serial.printf("Control Change event on channel %d: controller number 0x%02X, value 0x%02X\r\n", channel + 1, data1, data2);
}


Pieter

Grumpy_Mike

Quote
This approach is vulnerable to framing errors.
While this is true, I have never found it to be a problem in practics

PieterP

While this is true, I have never found it to be a problem in practics
It probably won't be a problem, until you start to send 2-byte MIDI events as well.

Pieter

Grumpy_Mike

Again true but you have to consider if the application is going to receive any. If it can't do anything with a two byte message then their is no need to send one nor make provisions for it appearing.

You don't always need a full MIDI message parsing system, to insist on one from a beginner just in case, I think makes things too complex to begin with and can frighten off learners. I would advise either matching your parsing code with what you expect or use a libiary with full parsing. However, in the Arduino world I believe libraries are used way too much and mainly stop people learning the fundamentals of the craft.

Go Up