Friday, February 12, 2016

Midi(USB) with a CASIO SA-39 and Arduino (Clone)

Preface

There are a few posts on how to  make a MIDI keyboard out of a plain/toy keyboard, here I have my notes with a Casio SA-39. I made it with the plan of using it with a Mac (GarageBand) and to play with it in my band,  skipped the prehistoric MIDI interface and went straight to USB with the Arduino UNO as the little engine for this exercise. Got a set of the synth/organ sounds and it worked very good for keyboard sections when jamming/recording and didn't have to buy a MIDI keyboard. It also worked with my bro's hackintosh.

Required

   - Arduino Uno CH340G
   - MacBook 10.9.5 or higher
   - 74HC595 shift register
   - Basic Lab equipment(Multimeter, cables,etc)

The keyboard matrix

This was a little pain but got the mapping as below

and the actual layout of the keys and the ribbon cable. So an 8x4 keyboard
we'll read the 4 groups and find the key by outputting a mask.





   The revised  shift register interface for the casio, that is all is needed the Arduino UNO has the AVR chipset as USB interface which can be flashed to behave as a HIDMIDI from there the USB gets the serial MIDI and plugged to our Mac is good to go.

Actually I ended up going the much easier way using the free hairless-midi software and the cheap Arduino clone, read below.




Got the power(5V) from the USB and it works so it is truely just plug the USB keyboard and jam.






The Arduino interface


   - Borrowed an oldie Leonardo Uno to get started, as my el cheapo chinese Arduino Uno CH340G would get to me from Ebay, the Leonardo does not have the second AVR chipset but it was good start to double check the keymaps ... on the cheap Arduino needed to tweak the USB interface to make it work with their driver but it worked on the Mac
For the Casio matrix it required inverted masks and pull resistors to Vcc, the basic idea remains e.g. send an 8-bit mask and read the 4-pin group  for a LOW value to sense a key pressed.

  The code

My modded code is listed after a few debugging session this is the code




// Midi standard at 41 is the map to the casio-39 

/*
 * 
 *   Send out a mask and with the keybpard matrix check if that mask produces a key pressed loop thru the set and good 2 go
 * 
 *  Optimized the code to KISS it mode --  Becker Cuéllar 
 * 
 *    Send  out a byte(mask) and read IN 4 pins and mapp properly, the 74595 allows so only use 3 pins for the 3-octaves
 */

const int LED_pin =13;

// The 74HC595 uses a serial communication 
// link which has three pins
const int clock_pin = 9;
const int latch_pin = 10;
const int data_pin = 11;

// Pin Definitions

const int row0 = 8;
const int row1 = 7;
const int row2 = 6;
const int row3 = 5;

    int groupValue0 = 1;
    int groupValue1 = 1;
    int groupValue2 = 1;
    int groupValue3 = 1;



const int BASE_MIDI=41;   // (F on octave-0)

boolean keyPressed[32];
boolean keyPressedPrev[32];
int noteVelocity = 127;
int curKeyCmd, curMIDIKey;

// proper mask for my board
int cur_byte_mask[] = { B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111 };




void setup() {
 
  // set up diode for visual output
  pinMode(LED_pin, OUTPUT);

 // set pins for the move
  pinMode(data_pin, OUTPUT);
  pinMode(clock_pin, OUTPUT);
  pinMode(latch_pin, OUTPUT);

  pinMode(row0, INPUT);
  pinMode(row1, INPUT);
  pinMode(row2, INPUT);
  pinMode(row3, INPUT);

  for (int j=0; j<32;j++)
  {
     keyPressed[j]=false;
     keyPressedPrev[j]=false;
  }
      
  Serial.begin(31250);   // MIDI standard needs this rate
  Serial.begin(115200);   //best to cover older macOS as they have issues for lower rates 
delay(1000); // time to cool down at startup } // --- end of setup --- void loop() { for (int col = 0; col < 8; col++) { // shift scan mask to following column scanColumn(cur_byte_mask[col]); // check if any keys were pressed - rows will have HIGH output when nothing pressed groupValue0 = digitalRead(row0); groupValue1 = digitalRead(row1); groupValue2 = digitalRead(row2); groupValue3 = digitalRead(row3); // since it is an inverted mask is with a '0' scanning a '1' is keyPressed[col]= ( (groupValue0==0) && ( (two_exp_n(col)&cur_byte_mask[col]) == 0) ); handleSingleKey(col); keyPressed[col+8]= ( (groupValue1==0) && ( (two_exp_n(col)&cur_byte_mask[col]) == 0) ); handleSingleKey(col+8); keyPressed[col+16]= ( (groupValue2==0) && ( (two_exp_n(col)&cur_byte_mask[col]) == 0) ); handleSingleKey(col+16); keyPressed[col+24]= ( (groupValue3==0) && ( (two_exp_n(col)&cur_byte_mask[col]) == 0) ); handleSingleKey(col+24); }// for } // -- end of loop --- //find which bit has a '0' in this mask - arduino exp didn't work 4 me int two_exp_n(int this_n_exp) { int ret = 1; int i=0; for(int k=0; k<this_n_exp; k++) ret = ret*2; return(ret); } // --- end of bit_pos --- // status byte 0x1001 CCCC -) status byte(1st bit=1) note=ON on channel CCCC // followd by optional data bytes 0PPP PPPP 0VVV VVVV pitch(actual music keu lije a C) and velocity // // 0x90 Note ON on channel 1 // 0x80 Note OFF on channel 1 // void handleSingleKey(int thisIdx) { if( keyPressed[thisIdx] != keyPressedPrev[thisIdx]) { curKeyCmd = keyPressed[thisIdx] ? 0x90 : 0x80; noteOn(curKeyCmd, BASE_MIDI + thisIdx , 127); keyPressedPrev[thisIdx] = keyPressed[thisIdx]; } } // --- end of handleSingleKey --- // ---- my routines to handle the stuff // 74HC595 shift to next column void scanColumn(int value) { digitalWrite(latch_pin, LOW); //Pulls the chips latch_pin low shiftOut(data_pin, clock_pin, MSBFIRST, value); //Shifts out the 8 bits to the shift register digitalWrite(latch_pin, HIGH); //Pulls the latch_pin high displaying the data_pin } // // Midi msg: [cmd] [data1] [data2] // // basic midi: cmd = 100K cccc (cccc is the channel 0-based) , K=1 note ON , K=0 note OFF // data1 = 0ppp pppp (the pitch 0-127) , middle C is 60 // data2 = 0vvv vvvv (velocity 0-127 or haw hard is pressed) // void noteOn(int cmd, int pitch, int velocity) // make MID message , velocity specifies haw hard was the key pressed(0-127) { Serial.write(cmd); Serial.write(pitch); Serial.write(velocity); } }



 Flashing Arduino to HID MIDI


The cheap Chinese Arduino clones are not flashable so for this a real one is needed. Also it is painful when you need to touch up the Arduino software when you want to add a new feature or correct a bug the whole process has to be rolled back and debugging is painful.


Using a MIDI-USB adaptor 

Next try was just use one of those MIDI to USB interfaces, quite straightforward pull the Arduino Tx line  with a 220-Ohm resistor to the MIDI IN, got a set from Ebay but they are temperamental, they do work but after you unplug it it gets a bit cumbersome to get it back and also is more wiring/bulk.

 Best solution! with software no need for flashing AVR and can use the clones.


 I'm doing this for the Mac(GarageBand, Logic Pro, etc.) 


The best solution and I'm kinda of staying there is using  hairless-midi software (free) which maps the virtual serial port of your Arduino to the MIDI via software on the mac and works flawlessly with the Arduino clone so it is the cheapest solution,  I can pull it out put it back and just restarting the hairless software gets me back to MIDI and ready to jam. Just need to make sure you close it before loading a new software version with the Arduino loader as obviously they use the same virtual serial port and only one can have it.



.


When you start probably you do not have an IAC driver listed on the MIDI out dropdown so setup one this is all well known but just in case:
    - Open Audio MIDI SetpUp  app
    - Click Window>Show MIDI window
    - Highlight IAC and Show  Info,  here check device is on line and ports are defined

Only issue I have found is the driver for the Arduino clone has to be right to get it to work nicely(CH340 dated 12/25/13 worked good for mine)also the rate that works better on older versions of the macOS(tried snow Leopard thru Mavericks) is 115200 on the Arduino code