MIDI CC messages: how to read?

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:

//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.

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

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!)

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)

Thank you Mike! Issue solved!

This approach is vulnerable to framing errors.
Try the following:

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

This approach is vulnerable to framing errors.

While this is true, I have never found it to be a problem in practics

Grumpy_Mike:
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

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.