dmusker
Published

Converting Farfisa Syntaccordion GSA box to accept Midi

Farfisa's most advanced accordion. Most were converted to Midi, making their GSA boxes redundant. This lets you cheaply repurpose them.

IntermediateFull instructions provided36
Converting Farfisa Syntaccordion GSA box to accept Midi

Things used in this project

Hardware components

Buck Converter 12V-5V
×1
SparkFun Logic Level Converter - Bi-Directional
SparkFun Logic Level Converter - Bi-Directional
×1
4049 DIL hex inverting buffer
×1
24-way panel-mount socket
×1
Arduino Leonardo
×1
10K Linear Potentiometer
×8
1K Linear Potentiometer
×1
22K Linear Potentiometer
×1
6K8 Resistor
×1
1K8 Resistor
×1
Through Hole Resistor, 470 ohm
Through Hole Resistor, 470 ohm
×1
Resistor 100 ohm
Resistor 100 ohm
×1
SparkFun MIDI Shield
SparkFun MIDI Shield
×1
Resistor 220 ohm
Resistor 220 ohm
×1
3.5mm audio jack panel socket
×1
5 mm LED: Red
5 mm LED: Red
×1

Software apps and online services

Arduino IDE
Arduino IDE
Processing
The Processing Foundation Processing
Windows 10
Microsoft Windows 10

Story

Read more

Schematics

Syntaccordion logo

This is the image file for use with the Processing UI program

Farfisa GSA-1 Interface Schematic

Schematic for interface box. Arduino omitted as it just plugs onto the shield.

Farfisa GSA-1 Interface Schematic With Gate Output

Code

Farfisa GSA-1 Interface Program for Arduino

Arduino
This program accepts MIDI control messages and note messages, and generates the necessary waveforms to send equivalent serial data to the GSA-1 box, enabling emulation of a Farfisa Syntaccordion.
//This is a program intended to drive a Farfisa GSA box using MIDI.
//Copyright David Musker 2024.
//It can be freely used provided the author is acknowledged in the code.

#include <MIDI.h> //maybe add USB midi later, maybe serial too

MIDI_CREATE_DEFAULT_INSTANCE();
// -----------------------------------------------------------------------------


//Port allocations
const uint8_t TE_Pin = 3; //allocate pin for the TRANSFER ENABLE signal
const uint8_t DATA_Pin = 7; //allocate pin for the data out
const uint8_t CLOCK_Pin = 5; //allocate pin for the NOT CLOCK signal

const uint8_t Midi_In_Pin = 0; //allocate pin for MIDI serial port
const int Farfisa_Channel = 16; //MIDI channel carrying commands for the Farfisa GSA.  I believe this is the channel which is numerically 15.
//const int Bass_Channel = 15; //MIDI channel for bass notes and chords, for future use
//const int Mono_Channel = 14; //MIDI channel for controlling mono effects, for future use
//uint8_t PCArray[90] = {128}; //Midi Program Change array - this will map program change  messages to Farfisa bits - those at 255 will do nothing.  Set other values later.  Not interested in sounds above GM90
//uint8_t CCArray[96] = {128}; //Midi Control Change array - this will map control channel messages to Farfisa bits - those at 255 will do nothing.  Set other values later.  Not expecting anything higher than cc95

const uint8_t NumberBits = 132; //the total number of data bits to send per cycle - note that these go from [0] to [131] - equal to size of GSA latches
bool OutArray[(NumberBits)] = {false}; //byte array big enough for all the bits to be transmitted, rounded down - this holds the current state of the virtual Syntaccordion including the 7 meaningless elements sent at the end

/*Here are the meanings of the OutArray bits
 * 0 Top Treble Note (B5) Midi 83
...
 * 47 Bottom Treble Note (C2) Midi 36

 * 48 Top Bass Note (C1) Midi 24
 ...
 * 59 Bottom Bass Note (C#0) Midi 13

BASS REGISTERS
 * 60 Bass Register 3rd
 * 61 Bass Register 2nd
 * 62 Bass Register 1st

POLY PRESET VOICE
 * 63 Marimba

 * 64 Bottom Chord Switch
...
 * 75 Top Chord Switch
 
 POLY PRESET VOICES
 * 76 Xylophone
 * 77 Organ 3
 * 78 Organ 2
 * 79 Organ 1
 * 80 Strings 16
 * 81 Strings 8
 * 82 Brass
 * 83 Piano
 * 84 Harpsichord

 BELLOWS EFFECTS
 * 85 Strings on Bass
 * 86 Strings on Chords
 * 87 Brass on Chords

 BASS
 * 88 Bass Guitar
 * 89 Bass
 * 90 Bass Sustain

 CHORDS
 * 91 Chords
 * 92 Chords Sustain

 POLY VIBRATO
 * 93 Vibrato
 * 94 Vibrato Fast/slow
 * 95 Vibrato Delay

 FLUTES
 * 96 Perc Long
 * 97 Perc 4
 * 98 Perc 2 2/3
 * 99 Perc 2
 * 100 Perc 1 1/3
 * 101 Flute.16
 * 102 Flute.8
 * 103 Flute.5 1/3
 * 104 Flute.4
 * 105 Flute.2 2/3
 * 106 Flute.2
 * 107 Flute.1 3/5
 * 108 Flute.1 1/3
 * 109 Flute.1

 EFFECTS
 * 110 Auto Sustain
 * 111 Repeat

 MONO VOICES
 * 112 Tuba
 * 113 Trombone
 * 114 Trumpet
 * 115 Sax
 * 116 Clarinet
 * 117 Oboe
 * 118 Violin

 MONO EFFECTS
 * 119 Wah Wah
 * 120 Decay
 * 121 Vib Mono
 * 122 L H
 * 123 M A R
 * 124 Glide
 * 125 --
 * 126 --
 * 127 --
 * 128 --
 * 129 --
 * 130 --
 * 131 --
 */
// -----------------------------------------------------------------------------

//END OF DECLARATIONS ETC
// -----------------------------------------------------------------------------

void setup() {

//Port Setup
pinMode(TE_Pin, OUTPUT);
pinMode(DATA_Pin, OUTPUT);
pinMode(CLOCK_Pin, OUTPUT);
pinMode(Midi_In_Pin, INPUT);
// -----------------------------------------------------------------------------

//Initial Port State.  Note we are using an inverting buffer chip so need to output the opposite of the TE, Clock and Data values we will transmit.  If the buffer is omitted, change the state of these settings
digitalWrite(TE_Pin, LOW); //Initially inhibit transfer at GSA
digitalWrite(CLOCK_Pin, LOW); //Initially set clock ready to run - GSA is looking for falling transitions.  Change if it doesn't work.
digitalWrite(DATA_Pin, HIGH); // Put a zero on data lines initially
// -----------------------------------------------------------------------------

//initially reset all bits OFF at GSA
for (int i=0; i<NumberBits; i++) {
   OutArray[i] = false; 
 }
SendArray(); 
// -----------------------------------------------------------------------------

//Midi setup

MIDI.setHandleNoteOn(handleNoteOn); 

MIDI.setHandleNoteOff(handleNoteOff);
    
MIDI.setHandleControlChange(handleCC);

MIDI.setHandleProgramChange(handlePC);

MIDI.setHandleSystemReset(handleReset);

MIDI.begin(MIDI_CHANNEL_OMNI); // Initiate MIDI communications, listen to all channels
// -----------------------------------------------------------------------------

} //END OF SETUP
// -----------------------------------------------------------------------------//

void loop() {

MIDI.read();

SendArray();

} //END OF LOOP
// -----------------------------------------------------------------------------


void SendArray() { //this is to send the bits

//Sending array
digitalWrite(CLOCK_Pin, LOW);
delayMicroseconds(4);
  for (int i = 0; i<NumberBits; i++) { //rest of the bits now
    bool outbyte = !OutArray[i]; //because we have an inverting buffer
    digitalWrite(DATA_Pin, outbyte);
    digitalWrite(CLOCK_Pin, HIGH); //generate falling clock edge to read data
    delayMicroseconds(6);  //takes account of 2xdigitalWrite delay to make a 10uS low
    digitalWrite(CLOCK_Pin, LOW); 
    delayMicroseconds(7); //rest of high clock cycle
  }
//===================================================================================

// Writing Transfer Enable after sending
    digitalWrite(TE_Pin, HIGH);// Start a TE pulse after data to allow transfer out
    delayMicroseconds(7);  //takes account of digitalWrite delay to make a 10uS low part of clock pulse
    digitalWrite(TE_Pin, LOW);// end of TE pulse
    delayMicroseconds(7);  //takes account of digitalWrite delay to make a 10uS low part of clock pulse
//===================================================================================
}

void handleNoteOn(byte channel, byte pitch, byte velocity) {
    if ((pitch<=83) && (pitch>=36)) { //was 36 for just the 48 treble notes - 36 takes in the 12 bass notes too
      OutArray[83-pitch] = true;}//

    else if ((pitch<=24) && (pitch> 12)) {
      OutArray[72-pitch] = true;}//bass notes 
}
// -----------------------------------------------------------------------------

void handleNoteOff(byte channel, byte pitch, byte velocity) {
    if ((pitch<=83) && (pitch>=36)) { //ONLY react to notes within scale
      OutArray[83-pitch] = false;}

    else if ((pitch<=24) && (pitch> 12)) {//react to MIDI notes 13 to 24
      OutArray[72-pitch] = false;}//bass notes - bit pos 48 to 59
}
// -----------------------------------------------------------------------------

void handleCC(byte channel, byte CC, byte val) {
  if(channel == Farfisa_Channel) { //specific to Farfisa
    OutArray[CC+60] = val;} //translate CC to bit pos 60-124
  
  if(CC == 123) { //All notes off
  for (int i=0; i<60; i++) { //reset treble and bass notes
   OutArray[i] = false;}
  for (int i = 64; i<76; i++) {
   OutArray[i] = false;} //reset chords
   }

  if(CC == 120) { //All sounds off
  for (int i=60; i<63; i++) { //reset voices and effects
   OutArray[i] = false;}
  for (int i = 76; i<NumberBits; i++) {
   OutArray[i] = false;} //reset the rest
   } 
}
// -----------------------------------------------------------------------------

void handlePC(byte channel, byte PC) {

 }
// -----------------------------------------------------------------------------

void handleReset(byte channel, byte pitch, byte velocity) {
 for (int i=0; i<NumberBits; i++) {
   OutArray[i] = false; //reset all bits
 }

}
// -----------------------------------------------------------------------------

Farfisa Syntaccordion Control Panel UI - to run on PC or other platform

Processing
This program (a) generates a UI emulating the Farfisa Syntaccordion, (b) sends MIDI control messages based on the UI selections, and (c) relays MIDI messages from a keyboard, to an Arduino driving the GSA-1 box. Used on a Windows 10 laptop but should compile to other platforms - display elements may need resizing for different screen sizes. Use with image file provided.
//This program is to emulate the controls available on a Farfisa Syntaccordion, to interface with a Farfisa GSA sound generator box via MIDI.
//Copyright David Musker 2024
//It is free for use provided the author is acknowledged in the code.

import themidibus.*;


MidiBus myBus; // The MidiBus


StringList labels1;
PFont myFont;
PFont myFont1;
PImage img;
int ArrayMax = 61; //number of buttons displayed
int[] PosX = new int[ArrayMax]; //Button X coords
int[] PosY = new int[ArrayMax]; //Button Y coords
int RectColour[] = new int[ArrayMax]; //Button colours default
int AltRectColour[] = new int[ArrayMax]; //Button colours when pressed
int[] BitPos = new int[127]; //Output Bit location in stream, add 60 to get actual bit position in the data stream
boolean[] BitValue = new boolean[127]; //Output Bit state
int MidiChannel = 15; // the dedicated channel for the Farfisa GSA - this is the 16th channel

int OnCol = #82E0AA;
int YellowCol = #F7DC6F;
int RedCol = #943126;
int BlueCol = #2874A6;
int GreyCol = #BFC9CA;
int BlacKCol = #616A6B;
int WhiteCol = #FDFEFE;
int RecWidth = 96;
int RecHeight = 100;

void setup() {
  img = loadImage("Syntaccordion_Logo.jpg"); //pic of the logo, in a file named DATA in the same directory. 
//  background(#fa1414);
  size(1800, 900);
 // image(img, 0, 800);

 MidiBus.list(); // List all available Midi devices on STDOUT. This will show each device's index and name.

 myBus = new MidiBus(this, "reface YC-1", "Yamaha UX16-1"); // Create a new MidiBus with input and output devices - here, substitute the name or position of whatever devices are being used

 
   for (int i = 0; i < ArrayMax; i = i+1) { 
     BitValue[i] = false; //INITIALISE ALL SWITCHES AT OFF
   }
   
   for (int i = 0; i < 15; i = i+1) { //INITIALISE 1ST ROW COORDS
     PosX[i] = i*(RecWidth+4);
     PosY[i] = RecHeight/2;
   }
  
    for (int i = 15; i < 28; i = i+1) { //INITIALISE 2ND ROW COORDS
     PosX[i] = (i-15)*(RecWidth+4);
     PosY[i] = 4*RecHeight/2;
   } 
   
     for (int i = 28; i < 42; i = i+1) { //INITIALISE 3RD ROW COORDS
     PosX[i] = (i-28)*(RecWidth+4);
     PosY[i] = 7*RecHeight/2;
   } 
     
     for (int i = 42; i < 55; i = i+1) { //INITIALISE 4TH ROW COORDS
     PosX[i] = (i-42)*(RecWidth+4);
     PosY[i] = 10*RecHeight/2;
   } 
           
     for (int i = 55; i < 61; i = i+1) { //INITIALISE 5TH ROW COORDS
     PosX[i] = (i-55)*(RecWidth+4);
     PosY[i] = 13*RecHeight/2;
   } 
   
 //SET UP LABELS AND INITIALISE COLOURS; ASCRIBE BIT POSITIONS
  labels1 = new StringList();
  labels1.append("Strings on Bass");
  BitPos[0] = 25;
  RectColour[0] = YellowCol;
  labels1.append("Strings on Chords");
  BitPos[1] = 26;
  RectColour[1] = YellowCol;
  labels1.append("Brass on Chords");
  BitPos[2] = 27;
  RectColour[2] = RedCol; 
  labels1.append("Bass Guitar");
  BitPos[3] = 28;
  RectColour[3] = BlueCol;  
  labels1.append("Bass");
  BitPos[4] = 29;
  RectColour[4] = GreyCol; 
  labels1.append("Bass Sustain");
  BitPos[5] = 30;
  RectColour[5] = GreyCol;  
  labels1.append("Chords");
  BitPos[6] = 31;
  RectColour[6] = GreyCol; 
  labels1.append("Chords Sustain");
  BitPos[7] = 32;
  RectColour[7] = GreyCol; 
  labels1.append("Vibrato Off/On");
  BitPos[8] = 33;
  RectColour[8] = BlacKCol;  
  labels1.append("Vibrato Slow/Fast");
  BitPos[9] = 34;
  RectColour[9] = BlacKCol;   
  labels1.append("Vibrato Delay");
  BitPos[10] = 35;
  RectColour[10] = BlacKCol;  
  labels1.append("Auto Sustain");
  BitPos[11] = 50;
  RectColour[11] = WhiteCol;    
  labels1.append("Bass 1st Oct");
  BitPos[12] = 2;
  RectColour[12] = GreyCol;   
  labels1.append("Bass 2nd Oct");
  BitPos[13] = 1;
  RectColour[13] = GreyCol; 
  labels1.append("Bass 3rd Oct");
  BitPos[14] = 0;
  RectColour[14] = GreyCol; 
  
  labels1.append("Organ 1");//aka flute presets
  BitPos[15] = 19;
  RectColour[15] = WhiteCol; 
  labels1.append("Organ 2");//aka flute presets
  BitPos[16] = 18;
  RectColour[16] = WhiteCol; 
  labels1.append("Organ 3");//aka flute presets
  BitPos[17] = 17;
  RectColour[17] = WhiteCol; 
  labels1.append("Vibes"); //aka Xylophone
  BitPos[18] = 16;
  RectColour[18] = BlueCol;  
  labels1.append("Marimba");
  BitPos[19] = 3;
  RectColour[19] = BlueCol;
  labels1.append("Cancel Preset");
  BitPos[20] = 100; //fake value
  RectColour[20] = WhiteCol;
  labels1.append("Glide");
  BitPos[21] = 64;
  RectColour[21] = WhiteCol;  
  labels1.append("Perc Long");
  BitPos[22] = 36;
  RectColour[22] = BlueCol;
  labels1.append("Perc 4");
  BitPos[23] = 37;
  RectColour[23] = WhiteCol;
  labels1.append("Perc 2 2/3");
  BitPos[24] = 38;
  RectColour[24] = BlacKCol;
  labels1.append("Perc 2");
  BitPos[25] = 39;
  RectColour[25] = WhiteCol;
  labels1.append("Perc 1 1/3");
  BitPos[26] = 40;
  RectColour[26] = BlacKCol;
  labels1.append("Repeat");
  BitPos[27] = 51;
  RectColour[27] = BlueCol;   
  
  labels1.append("Strings 16");
  BitPos[28] = 20;
  RectColour[28] = YellowCol;  
  labels1.append("Strings 8");
  BitPos[29] = 21;
  RectColour[29] = YellowCol;  
  labels1.append("Brass");
  BitPos[30] = 22;
  RectColour[30] = RedCol;  
  labels1.append("Piano");
  BitPos[31] = 23;
  RectColour[31] = BlueCol;
  labels1.append("Harpsi chord");
  BitPos[32] = 24;
  RectColour[32] = BlueCol;
  labels1.append("Flute 16");
  BitPos[33] = 41;
  RectColour[33] = WhiteCol;
  labels1.append("Flute 8");
  BitPos[34] = 42;
  RectColour[34] = WhiteCol;
  labels1.append("Flute 5 1/3");
  BitPos[35] = 43;
  RectColour[35] = BlacKCol;
  labels1.append("Flute 4");
  BitPos[36] = 44;
  RectColour[36] = WhiteCol;
  labels1.append("Flute 2 2/3");
  BitPos[37] = 45;
  RectColour[37] = BlacKCol;
  labels1.append("Flute 2");
  BitPos[38] = 46;
  RectColour[38] = WhiteCol;
  labels1.append("Flute 1 3/5");
  BitPos[39] = 47;
  RectColour[39] = BlacKCol; 
  labels1.append("Flute 1 1/3");
  BitPos[40] = 48;
  RectColour[40] = BlacKCol; 
  labels1.append("Flute 1");
  BitPos[41] = 49;
  RectColour[41] = WhiteCol;
   
  labels1.append("L H");
  BitPos[42] = 62;
  RectColour[42] = GreyCol;
  labels1.append("Cancel Mono");
  BitPos[43] = 101;
  RectColour[43] = WhiteCol;
  labels1.append("Tuba");
  BitPos[44] = 52;
  RectColour[44] = RedCol;  
  labels1.append("Trombone");
  BitPos[45] = 53;
  RectColour[45] = RedCol;  
  labels1.append("Trumpet");
  BitPos[46] = 54;
  RectColour[46] = RedCol; 
  labels1.append("Sax");
  BitPos[47] = 55;
  RectColour[47] = GreyCol;
  labels1.append("Clarinet");
  BitPos[48] = 56;
  RectColour[48] = GreyCol;
  labels1.append("Oboe");
  BitPos[49] = 57;
  RectColour[49] = GreyCol;
  labels1.append("Violin");
  BitPos[50] = 58;
  RectColour[50] = YellowCol;
  labels1.append("M A R");
  BitPos[51] = 63;
  RectColour[51] = RedCol; 
  labels1.append("Vibrato Mono");
  BitPos[52] = 61;
  RectColour[52] = BlacKCol; 
  labels1.append("Decay");
  BitPos[53] = 60;
  RectColour[53] = BlueCol; 
  labels1.append("Wah Wah");
  BitPos[54] = 59;
  RectColour[54] = RedCol;
  
  labels1.append("Bellows Mono");
  BitPos[55] = 102;
  RectColour[55] = WhiteCol; 
  labels1.append("Bellows Str");
  BitPos[56] = 103;
  RectColour[56] = WhiteCol;
  labels1.append("Bellows BPM");
  BitPos[57] = 104;
  RectColour[57] = WhiteCol;
  labels1.append("Bellows Spring State");
  BitPos[58] = 105;
  RectColour[58] = WhiteCol;
  labels1.append("Panic All Off");
  BitPos[59] = 70;
  RectColour[59] = RedCol;
  labels1.append("Test Note");
  BitPos[60] = 106;
  RectColour[60] = WhiteCol;
  
}

void draw() {
  
    background(#fa1414);
    image(img, 0, 800);
  
  fill (0);
  myFont1 = createFont("Arial", 20);
  textFont(myFont1);
    textAlign(LEFT, CENTER);
    text("_______________________________LEFT HAND______________________________", 0, 30);

    text("___________________FLUTE PRESETS____________________", 0, 180);
    text("_____________________PERCUSSION____________________", 700, 180);
    text("_________________ORCHESTRA________________", 0, 330);
    text("____________________________________FLUTES_____________________________________", 505, 330);
    text("_______________________________________________________MONO________________________________________________________", 0, 480);
    text("_______________OTHER______________", 0, 630);
    text("__MIDI__", 400, 630);

  myFont = createFont("Arial Italic", 20);
  textFont(myFont);
  noLoop();
  fill(0);
  textAlign(CENTER, CENTER);
  
  //println(frameCount);
  //BitValue[0] = true; //just to test 

  for (int i = 0; i < ArrayMax; i = i+1) {
        if (BitValue[i] == true) {
           fill(OnCol);
         }
    else { fill(RectColour[i]); 
  }
    rect(PosX[i], PosY[i], RecWidth, RecHeight, 10);
    fill (0);
    String item = labels1.get(i);
    text(item, PosX[i], PosY[i], RecWidth, RecHeight);

  }

}

void mouseClicked() {

  for (int i = 0; i < ArrayMax; i = i+1) {//finds which button is clicked in
     if ((mouseX > PosX[i]) && (mouseX < (PosX[i]+RecWidth)) && (mouseY > PosY[i]) && (mouseY < (PosY[i]+RecHeight))){
       //detect 0 and 1 bit positions and execute radio button clears
       //NOTE: I have not implemented the "radio button" functionality or the CANCEL buttons
       
       BitValue[i] = !BitValue[i];//changes state of value
       //at this stage send the Bit Position(s) and the Bit Value(s) to the Arduino
       
       if (BitPos[i]<70) { //do not send other out-of-band buttons
         myBus.sendControllerChange(MidiChannel, BitPos[i], int(BitValue[i]));  }

       else if (BitPos[i] == 70) { //PANIC button
         myBus.sendControllerChange(1, 123, 0); }
       
       else if (BitPos[i] == 106) { //Test Note button
           if (BitValue[i] == true) {//Note on
             myBus.sendNoteOn(1, 60, 1);} 
           else {//Note off
             myBus.sendNoteOff(1, 60, 1);}
             
       }
       
       }
            
       } 


       //background(#fa1414);
           redraw();
   }
 
  


void noteOn(int channel, int pitch, int velocity) {
  // Receive a note On
  myBus.sendNoteOn(channel, pitch, velocity); // Send a Midi note On

}

void noteOff(int channel, int pitch, int velocity) {
  // Receive a noteOff
  myBus.sendNoteOff(channel, pitch, velocity); // Send a Midi note off

}

Farfisa GSA-1 Interface Program for Arduino - With Gate

Arduino
//This is a program intended to drive a Farfisa GSA box using MIDI.
//Copyright David Musker 2024.
//It can be freely used provided the author is acknowledged in the code.

#include <MIDI.h> //maybe add USB midi later, maybe serial too

MIDI_CREATE_DEFAULT_INSTANCE();
// -----------------------------------------------------------------------------


//Port allocations
const uint8_t TE_Pin = 3; //allocate pin for the TRANSFER ENABLE signal
const uint8_t DATA_Pin = 7; //allocate pin for the data out
const uint8_t CLOCK_Pin = 5; //allocate pin for the NOT CLOCK signal

const uint8_t GATE_Pin = 9; //for Gate output

const uint8_t Midi_In_Pin = 0; //allocate pin for MIDI serial port
const int Farfisa_Channel = 16; //MIDI channel carrying commands for the Farfisa GSA.  I believe this is the channel which is numerically 15.
//const int Bass_Channel = 15; //MIDI channel for bass notes and chords, for future use
//const int Mono_Channel = 14; //MIDI channel for controlling mono effects, for future use
//uint8_t PCArray[90] = {128}; //Midi Program Change array - this will map program change messages to Farfisa bits - those at 255 will do nothing.  Set other values later.  Not interested in sounds above GM90
//uint8_t CCArray[96] = {128}; //Midi Control Change array - this will map control channel messages to Farfisa bits - those at 255 will do nothing.  Set other values later.  Not expecting anything higher than cc95

const uint8_t NumberBits = 132; //the total number of data bits to send per cycle - note that these go from [0] to [131] - equal to size of GSA latches

uint8_t NotesOn = 0;
bool OutArray[(NumberBits)] = {false}; //byte array big enough for all the bits to be transmitted, rounded down - this holds the current state of the virtual Syntaccordion including the 7 meaningless elements sent at the end

/*Here are the meanings of the OutArray bits
 * 0 Top Treble Note (B5) Midi 83
...
 * 47 Bottom Treble Note (C2) Midi 36

 * 48 Top Bass Note (C1) Midi 24
 ...
 * 59 Bottom Bass Note (C#0) Midi 13

BASS REGISTERS
 * 60 Bass Register 3rd
 * 61 Bass Register 2nd
 * 62 Bass Register 1st

POLY PRESET VOICE
 * 63 Marimba

 * 64 Bottom Chord Switch
...
 * 75 Top Chord Switch
 
 POLY PRESET VOICES
 * 76 Xylophone
 * 77 Organ 3
 * 78 Organ 2
 * 79 Organ 1
 * 80 Strings 16
 * 81 Strings 8
 * 82 Brass
 * 83 Piano
 * 84 Harpsichord

 BELLOWS EFFECTS
 * 85 Strings on Bass
 * 86 Strings on Chords
 * 87 Brass on Chords

 BASS
 * 88 Bass Guitar
 * 89 Bass
 * 90 Bass Sustain

 CHORDS
 * 91 Chords
 * 92 Chords Sustain

 POLY VIBRATO
 * 93 Vibrato
 * 94 Vibrato Fast/slow
 * 95 Vibrato Delay

 FLUTES
 * 96 Perc Long
 * 97 Perc 4
 * 98 Perc 2 2/3
 * 99 Perc 2
 * 100 Perc 1 1/3
 * 101 Flute.16
 * 102 Flute.8
 * 103 Flute.5 1/3
 * 104 Flute.4
 * 105 Flute.2 2/3
 * 106 Flute.2
 * 107 Flute.1 3/5
 * 108 Flute.1 1/3
 * 109 Flute.1

 EFFECTS
 * 110 Auto Sustain
 * 111 Repeat

 MONO VOICES
 * 112 Tuba
 * 113 Trombone
 * 114 Trumpet
 * 115 Sax
 * 116 Clarinet
 * 117 Oboe
 * 118 Violin

 MONO EFFECTS
 * 119 Wah Wah
 * 120 Decay
 * 121 Vib Mono
 * 122 L H
 * 123 M A R
 * 124 Glide
 * 125 --
 * 126 --
 * 127 --
 * 128 --
 * 129 --
 * 130 --
 * 131 --
 */
// -----------------------------------------------------------------------------

//END OF DECLARATIONS ETC
// -----------------------------------------------------------------------------

void setup() {

//Port Setup
pinMode(TE_Pin, OUTPUT);
pinMode(DATA_Pin, OUTPUT);
pinMode(CLOCK_Pin, OUTPUT);
pinMode(Midi_In_Pin, INPUT);
pinMode(GATE_Pin, OUTPUT);
// -----------------------------------------------------------------------------

//Initial Port State.  Note we are using an inverting buffer chip so need to output the opposite of the TE, Clock and Data values we will transmit.  If the buffer is omitted, change the state of these settings
digitalWrite(TE_Pin, LOW); //Initially inhibit transfer at GSA
digitalWrite(CLOCK_Pin, LOW); //Initially set clock ready to run - GSA is looking for falling transitions.  Change if it doesn't work.
digitalWrite(DATA_Pin, HIGH); // Put a zero on data lines initially
// -----------------------------------------------------------------------------

//initially reset all bits OFF at GSA
for (int i=0; i<NumberBits; i++) {
   OutArray[i] = false; 
 }
SendArray(); 
// -----------------------------------------------------------------------------

//Midi setup

MIDI.setHandleNoteOn(handleNoteOn); 

MIDI.setHandleNoteOff(handleNoteOff);
    
MIDI.setHandleControlChange(handleCC);

MIDI.setHandleProgramChange(handlePC);

MIDI.setHandleSystemReset(handleReset);

MIDI.begin(MIDI_CHANNEL_OMNI); // Initiate MIDI communications, listen to all channels
// -----------------------------------------------------------------------------

} //END OF SETUP
// -----------------------------------------------------------------------------//

void loop() {

MIDI.read();

SendArray();

//Setting gate pin for mono mode - no retriggering
if (NotesOn > 0) {
digitalWrite(GATE_Pin, HIGH);
}
else{
digitalWrite(GATE_Pin, LOW);
}

} //END OF LOOP
// -----------------------------------------------------------------------------


void SendArray() { //this is to send the bits

//Sending array
digitalWrite(CLOCK_Pin, LOW);
delayMicroseconds(4);
  for (int i = 0; i<NumberBits; i++) { //rest of the bits now
    bool outbyte = !OutArray[i]; //because we have an inverting buffer
    digitalWrite(DATA_Pin, outbyte);
    digitalWrite(CLOCK_Pin, HIGH); //generate falling clock edge to read data
    delayMicroseconds(6);  //takes account of 2xdigitalWrite delay to make a 10uS low
    digitalWrite(CLOCK_Pin, LOW); 
    delayMicroseconds(7); //rest of high clock cycle
  }
//===================================================================================

// Writing Transfer Enable after sending
    digitalWrite(TE_Pin, HIGH);// Start a TE pulse after data to allow transfer out
    delayMicroseconds(7);  //takes account of digitalWrite delay to make a 10uS low part of clock pulse
    digitalWrite(TE_Pin, LOW);// end of TE pulse
    delayMicroseconds(7);  //takes account of digitalWrite delay to make a 10uS low part of clock pulse
//===================================================================================
}

void handleNoteOn(byte channel, byte pitch, byte velocity) {
  NotesOn++;
    if ((pitch<=83) && (pitch>=36)) { //was 36 for just the 48 treble notes - 36 takes in the 12 bass notes too
      OutArray[83-pitch] = true;}//

    else if ((pitch<=24) && (pitch> 12)) {
      OutArray[72-pitch] = true;}//bass notes 
}
// -----------------------------------------------------------------------------

void handleNoteOff(byte channel, byte pitch, byte velocity) {
  NotesOn--;
    if ((pitch<=83) && (pitch>=36)) { //ONLY react to notes within scale
      OutArray[83-pitch] = false;}

    else if ((pitch<=24) && (pitch> 12)) {//react to MIDI notes 13 to 24
      OutArray[72-pitch] = false;}//bass notes - bit pos 48 to 59
}
// -----------------------------------------------------------------------------

void handleCC(byte channel, byte CC, byte val) {
  if(channel == Farfisa_Channel) { //specific to Farfisa
    OutArray[CC+60] = val;} //translate CC to bit pos 60-124
  
  if(CC == 123) { //All notes off
  for (int i=0; i<60; i++) { //reset treble and bass notes
   OutArray[i] = false;}
  for (int i = 64; i<76; i++) {
   OutArray[i] = false;} //reset chords
   }

  if(CC == 120) { //All sounds off
  for (int i=60; i<63; i++) { //reset voices and effects
   OutArray[i] = false;}
  for (int i = 76; i<NumberBits; i++) {
   OutArray[i] = false;} //reset the rest
   } 
}
// -----------------------------------------------------------------------------

void handlePC(byte channel, byte PC) {

 }
// -----------------------------------------------------------------------------

void handleReset(byte channel, byte pitch, byte velocity) {
 for (int i=0; i<NumberBits; i++) {
   OutArray[i] = false; //reset all bits
 }

}
// -----------------------------------------------------------------------------

Credits

dmusker

dmusker

2 projects • 1 follower

Comments