Help adding MIDI output to old keyboard

Hi everyone, this is my first time using an arduino, and I have some basic coding knowledge (enough to understand most parts of code, and maybe editing it).

I have a really old 61-key keyboard, which I want to use for music creation on Logic Pro X. I followed a guide from someone who did the same thing, with a slightly different scan matrix pattern to mine. (His keyboard had 11 groups of 6 keys, where mine had 6 groups of 12 keys).

I used 2 serial to parallel shift registers to connect my keyboard to the arduino, and used the code from the person, but with my keyboard's wiring, the keys end up playing the wrong notes, and I have no idea how to fix it.

I've read the code I'm using back to front, but I can't find a single place where he assigned the individual notes to specific MIDI values.

I thought that a possible solution is to map every combination to a specific MIDI note value using an array or a table, but I am not sure how to do it.

Here's the code I'm using, could someone please help me implement a solution?

#include <MIDI.h>

#define NUM_ROWS 6
#define NUM_COLS 12

#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define NOTE_VELOCITY 127

//MIDI baud rate
#define SERIAL_RATE 9600

// Pin Definitions

// Row input pins
const int row1Pin = 7;
const int row2Pin = 6;
const int row3Pin = 5;
const int row4Pin = 4;
const int row5Pin = 3;
const int row6Pin = 2;

// 74HC595 pins
const int dataPin = 8;
const int latchPin = 9;
const int clockPin = 10;

boolean keyPressed[NUM_ROWS][NUM_COLS];
uint8_t keyToMidiMap[NUM_ROWS][NUM_COLS];

// bitmasks for scanning columns
int bits[] =
{ 
  B00000001,
  B00000010,
  B00000100,
  B00001000,
  B00010000,
  B00100000,
  B01000000,
  B10000000
};

void setup()
{
  int note = 31;

  for(int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
  {
    for(int rowCtr = 0; rowCtr < NUM_ROWS; ++rowCtr)
    {
      keyPressed[rowCtr][colCtr] = false;
      keyToMidiMap[rowCtr][colCtr] = note;
      note++;
    }
  }

  // setup pins output/input mode
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);

  pinMode(row1Pin, INPUT);
  pinMode(row2Pin, INPUT);
  pinMode(row3Pin, INPUT);
  pinMode(row4Pin, INPUT);
  pinMode(row5Pin, INPUT);
  pinMode(row6Pin, INPUT);

  Serial.begin(SERIAL_RATE);
}

void loop()
{
  for (int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
  {
    //scan next column
    scanColumn(colCtr);

    //get row values at this column
    int rowValue[NUM_ROWS];
    rowValue[0] = digitalRead(row1Pin);
    rowValue[1] = digitalRead(row2Pin);
    rowValue[2] = digitalRead(row3Pin);
    rowValue[3] = digitalRead(row4Pin);
    rowValue[4] = digitalRead(row5Pin);
    rowValue[5] = digitalRead(row6Pin);

    // process keys pressed
    for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
    {
      if(rowValue[rowCtr] != 0 && !keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = true;
        noteOn(rowCtr,colCtr);
      }
    }

    // process keys released
    for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
    {
      if(rowValue[rowCtr] == 0 && keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = false;
        noteOff(rowCtr,colCtr);
      }
    }
  }
}

void scanColumn(int colNum)
{
  digitalWrite(latchPin, LOW);

  if(0 <= colNum && colNum <= 7)
  {
    shiftOut(dataPin, clockPin, MSBFIRST, B00000000); //right sr
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum]); //left sr
  }
  else
  {
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum-8]); //right sr
    shiftOut(dataPin, clockPin, MSBFIRST, B00000000); //left sr
  }
  digitalWrite(latchPin, HIGH);
}

void noteOn(int row, int col)
{
  Serial.write(NOTE_ON_CMD);
  Serial.write(keyToMidiMap[row][col]);
  Serial.write(NOTE_VELOCITY);
}

void noteOff(int row, int col)
{
  Serial.write(NOTE_OFF_CMD);
  Serial.write(keyToMidiMap[row][col]);
  Serial.write(NOTE_VELOCITY);
}

Moderator edit: code tags

but I can't find a single place where he assigned the individual notes to specific MIDI values.

Here.

Serial.write(keyToMidiMap[row][col]);

The contents of the array keyToMidiMap define the note for a given value of row and col

This array is defined in the setup function in two nested "for" loops. The value note is initially set at 31 and is incremented in the inner loop, so that each note defined is one more than the last.

If you want to make it more clear then you can define the initial values in this array when it is declared and miss out the section in setup where it is done.

I must say that this is a bit of a silly way of doing it but it will work. I would just use an arithmetic expression to calculate the note number from the values of row and col.

note = row + (col * NUM_ROWS) + 31

Thanks for your reply

This array is defined in the setup function in two nested "for" loops. The value note is initially set at 31 and is incremented in the inner loop, so that each note defined is one more than the last.

This paragraph actually gave me an idea that I could use instead of defining every value with an array or using an arithmetic expression.

You mentioned that the inner for loop increments the note value. I knew it previously, but didn't actually account for its significance until you brought it up. I realised that the code is scanning each column and then incrementing the note based on which row the button is within the specific column. The issue I've been having coincidentally had the wrong notes in a repeating pattern, and realised that if I were to swop the functions of the inner and outer loops, it might work.

So instead of having this:

for(int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
  {
    for(int rowCtr = 0; rowCtr < NUM_ROWS; ++rowCtr)
    {
      keyPressed[rowCtr][colCtr] = false;
      keyToMidiMap[rowCtr][colCtr] = note;
      note++;
    }
  }

I changed it to this:

for(int rowCtr = 0; rowCtr < NUM_ROWS; ++rowCtr)
  {
    for(int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
    {
      keyPressed[rowCtr][colCtr] = false;
      keyToMidiMap[rowCtr][colCtr] = note;
      note++;
    }
  }

That got it to work. Thanks for the help :smiley:

Hi. New member and just diving in...

Is it possible to reverse this so that you can control the data and select lines by MIDI? I.e. to make the keyboard receive MIDI rather than transmit it?

Is it possible to reverse this so that you can control the data and select lines by MIDI?

Yes.

. to make the keyboard receive MIDI rather than transmit it?

No - that would be silly, what would you do with it?

Please read this:-
How to use this forum and don't add anymore posts to this thread unless it is relevant. If you have a question like this then please start your own thread.

Thank you!

For what? Read the how to use the forum sticky post and don't highjack other people's threads, start your own.