dmusker
Published

Reading Farfisa Syntaccordion to generate Midi using Arduino

You have to physically hack most accordions to generate midi. The Syntaccordion can do it with a simple, cheap circuit.

IntermediateFull instructions provided261
Reading Farfisa Syntaccordion to generate Midi using Arduino

Things used in this project

Hardware components

UNO R4 Minima
Arduino UNO R4 Minima
×1
Through Hole Resistor, 3.9 kohm
Through Hole Resistor, 3.9 kohm
×3
Through Hole Resistor, 8.2 kohm
Through Hole Resistor, 8.2 kohm
×3
SparkFun MIDI Shield
SparkFun MIDI Shield
×1
Centronics 24 pin socket
×1
DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
TaydaElectronics DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
×1
12V DC PSU
×1
Toggle Switch, Toggle
Toggle Switch, Toggle
×1
3.5mm panel socket
×2
LED (generic)
LED (generic)
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

MSA 3 X179 User Manual

Schematic for Syntaccordion to Midi Interface - add Midi Shield

Should work with +12V PSU and Midi Shield.

Code

Farfisa Syntaccordion Reader

Arduino
This lets you read the output data from a Farfisa Syntaccordion and output Midi notes, and Gate/CV.
//This is a program intended to read a Farfisa Syntaccordion and generate MIDI.
//This version is for Arduino Uno R4 Minima
//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();
// -----------------------------------------------------------------------------
#include <OPAMP.h>
//#include <mtof.h>
//#include "pwm.h"
//PwmOut AudioOut(D4);

//Port allocations
const uint8_t Midi_Out_Pin = 1; //allocate serial TX pin for MIDI serial port
const uint8_t CLOCK_Pin = 2; //allocate pin for the NOT CLOCK signal interrupts
const uint8_t TE_Pin = 3; //allocate pin for the TRANSFER ENABLE signal interrupts
const uint8_t DATA_Pin = 4; //allocate pin for the data in
const uint8_t Priority_Select_Pin = 5;//put a switch to select highest or lowest note priority on mono channel
const uint8_t Gate_Pin = 13; //output when a note is on, also light LED
const uint8_t CV_Pin = A0;
const int max_DAC = 4065; //DAC input at exactly +5V DAC output
const int multiple = int((float(max_DAC)/60.0)+0.5); //DAC increment per Midi note (semitone) to get 60 notes over 5 volts - 67.75.

//Midi Channel allocations
const uint8_t Midi_Channel = 0; //MIDI channel to use for treble notes
const uint8_t Bass_Channel = Midi_Channel + 1; //MIDI channel for bass notes and chords
const int Chord_Channel = Midi_Channel + 2; //MIDI channel for bass notes and chords
const int Mono_Channel = Midi_Channel + 3; //MIDI channel for mono notes
const int Strings_Channel = Midi_Channel + 4; //MIDI channel for string machine
const int Brass_Channel = Midi_Channel + 5; //MIDI channel for brass machine

//Other constants
const uint8_t noteON_Base = 144; 
const uint8_t noteOFF_Base = 128;
const uint8_t CC_Base = 176;
const uint8_t PC_Base = 192;
const uint8_t NumberBits = 132;

//variables
volatile uint8_t input_index = 0;
volatile bool input_values[NumberBits] = {false}; //read note and switch data into this array, start at all off
bool last_values[NumberBits] = {false}; //last recorded note and switch data values, start at all off
bool Any_Notes_On = false;
bool Any_Notes_Changed = false;

uint8_t CCNums[NumberBits] = {0}; //array of Midi Control Change numbers to map to switches
uint8_t PCNums[NumberBits] = {0}; //array of Midi Program Change numbers to map to switches
uint8_t ChannelNums[NumberBits] = {0}; //array of Midi Channels for the PC and CC values

uint8_t Current_High_Note = 0;
uint8_t Last_High_Note = 0;
uint8_t Current_Low_Note = 0;
uint8_t Last_Low_Note = 0;
uint8_t Current_Note = 0;
uint8_t Bass_Note = 0;
uint8_t Bass_Offset = 0;//multiple of 12 to shift bass up and down
uint8_t Chord_Note = 0;
uint8_t notevel = 0;
uint8_t j = 0; //index counter

int CV_Out = 0; // for the 12 bit analog output

/*Here are the meanings of the input 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: Farfisa-like, send CC 80 data 64; GM PC 18
 * 78 Organ 2: Vox-like, send CC 80 data 32; GM PC 16
 * 79 Organ 1: Hammond-like, send CC 80 data 0;  GM PC 17
 * 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 [seems to be preset cancel]
 * 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 --
 */
// -----------------------------------------------------------------------------

void setup() {

//Assign non-zero Midi Channel, CC and PC numbers to switches: 255 indicates it is to be used internally not transmitted
ChannelNums[60] = 255;
ChannelNums[61] = 255;
ChannelNums[62] = 255;
ChannelNums[63] = Midi_Channel;
ChannelNums[76] = Midi_Channel;
ChannelNums[77] = Midi_Channel;
ChannelNums[78] = Midi_Channel;
ChannelNums[79] = Midi_Channel;
ChannelNums[80] = Strings_Channel;
ChannelNums[81] = Strings_Channel;
ChannelNums[82] = Brass_Channel;
ChannelNums[83] = Midi_Channel;
ChannelNums[84] = Midi_Channel;
ChannelNums[85] = Brass_Channel;
ChannelNums[86] = Strings_Channel;
ChannelNums[87] = Brass_Channel;
ChannelNums[88] = Bass_Channel;
ChannelNums[89] = Bass_Channel;
ChannelNums[90] = Bass_Channel;
ChannelNums[91] = 255;
ChannelNums[92] = Chord_Channel;
ChannelNums[93] = Bass_Channel;
ChannelNums[94] = Bass_Channel;
ChannelNums[95] = Bass_Channel;
ChannelNums[95] = Mono_Channel;
ChannelNums[96] = Midi_Channel;
ChannelNums[97] = Midi_Channel;
ChannelNums[98] = Midi_Channel;
ChannelNums[99] = Midi_Channel;
ChannelNums[100] = Midi_Channel;
ChannelNums[101] = Midi_Channel;
ChannelNums[102] = Midi_Channel;
ChannelNums[103] = Midi_Channel;
ChannelNums[104] = Midi_Channel;
ChannelNums[105] = Midi_Channel;
ChannelNums[106] = Midi_Channel;
ChannelNums[107] = Midi_Channel;
ChannelNums[108] = Midi_Channel;
ChannelNums[109] = Midi_Channel;
ChannelNums[110] = Midi_Channel;
ChannelNums[111] = Midi_Channel;
ChannelNums[112] = Mono_Channel;
ChannelNums[113] = Mono_Channel;
ChannelNums[114] = Mono_Channel;
ChannelNums[115] = Mono_Channel;
ChannelNums[116] = Mono_Channel;
ChannelNums[117] = Mono_Channel;
ChannelNums[118] = Mono_Channel;
ChannelNums[119] = Mono_Channel;
ChannelNums[120] = Mono_Channel;
ChannelNums[121] = Mono_Channel;
ChannelNums[124] = Mono_Channel;

//Assign non-zero CC numbers
CCNums[90] = 64;
CCNums[92] = 64;
CCNums[93] = 77;
CCNums[94] = 76;
CCNums[95] = 78;

//the following are percussion - no particular CCs to map them to
CCNums[96] = 113;
CCNums[97] = 111;
CCNums[98] = 112;
CCNums[99] = 114;
CCNums[100] = 115;

//the following are Yamaha Reface footings
CCNums[101] = 102;
CCNums[102] = 103;
CCNums[103] = 104;
CCNums[104] = 105;
CCNums[105] = 106;
CCNums[106] = 107;
CCNums[107] = 108;
CCNums[108] = 109;
CCNums[109] = 110;
//======================================

CCNums[110] = 64; //repeat
CCNums[111] = 111;
CCNums[119] = 70;
CCNums[120] = 80;
CCNums[121] = 1;
CCNums[124] = 65;

//Assign non-zero voices
PCNums[63] = 12;

PCNums[76] = 11;
PCNums[77] = 18;//Midi organ Farfisa-like
PCNums[78] = 16;//Midi organ Vox-like
PCNums[79] = 17;//Midi organ Hammond-like
PCNums[80] = 50;
PCNums[81] = 50;
PCNums[82] = 62;
PCNums[83] = 4;
PCNums[84] = 6;
PCNums[85] = 50;
PCNums[86] = 50;
PCNums[87] = 62;
PCNums[88] = 34;
PCNums[89] = 62;
PCNums[112] = 58;
PCNums[113] = 57;
PCNums[114] = 56;
PCNums[115] = 65;
PCNums[116] = 71;
PCNums[117] = 68;
PCNums[118] = 40;

//Set up input and output pins
pinMode(TE_Pin, INPUT); //check if it should be INPUT_PULLUP
pinMode(DATA_Pin, INPUT); //check if it should be INPUT_PULLUP
pinMode(CLOCK_Pin, INPUT); //check if it should be INPUT_PULLUP
pinMode(Priority_Select_Pin, INPUT_PULLUP);//switch to select highest or lowest note priority normally HIGH
pinMode(Gate_Pin, OUTPUT); // for a 5V non-retriggering gate output
analogWriteResolution(12); // for the 1V per octave CV analog output
OPAMP.begin(OPAMP_SPEED_LOWSPEED);


// declare any output pins
//ditto any analogue inputs

attachInterrupt(digitalPinToInterrupt(CLOCK_Pin), CLK_ISR, FALLING); 
attachInterrupt(digitalPinToInterrupt(TE_Pin), TE_ISR, FALLING); 

Serial1.begin(31250); //Midi rate.  NOTE: all commands are for Serial1 which is the hardware port on the R4 Minima.  Would be Serial for other versions of Uno.

sendPC(Midi_Channel, 16); //start with Vox voice
sendPC(Strings_Channel, 50); //set up synth voice on brass channel
sendPC(Brass_Channel, 62); //set up brass voice on brass channel
sendPC(Bass_Channel, 38); //set up bass voice on bass channel
sendPC(Mono_Channel, 62); //something on the mono channel for now
}

void loop() {

//first do treble notes

//initialise before loop
Current_Note = 0;
Any_Notes_On = false;
Any_Notes_Changed = false;
Current_Low_Note = 0;
Current_High_Note = 0;


for(uint8_t i=0; i<48; i++) { //scanning all treble keys - high note to low

  //checking and replacing highest and lowest notes this cycle
  if (input_values[i] == true) {
    Any_Notes_On = true;
        if(Current_Low_Note == 0){ //Haven't found any note on before
          Current_High_Note = (83-i); //This note must be highest note currently played
        }
    Current_Low_Note = (83-i); //note encountered is lowest so far
  }
 
  //now look for changes to send on Midi (and Strings and Brass) channels
  if (input_values[i] != last_values[i]) { //change of state for datum i
    Any_Notes_Changed = true;
    Current_Note = 83 - i; //convert index to Midi note number
    if (input_values[i] == true) { //note turned ON
      notevel = 127;
    }
    else { //note turned OFF
      notevel = 0;
    }
    sendnote(Midi_Channel, Current_Note, notevel);

//if brass register is selected, send relevant footings on brass channel too
    if (input_values[82] == true) { //BRASS turned ON
      sendnote(Brass_Channel, Current_Note, notevel);
      }

//if one or both of the strings registers is selected, send relevant footings on strings channel too
    if (input_values[81] == true) { //STRINGS 8 turned ON
      sendnote(Strings_Channel, Current_Note, notevel);
      }
    if (input_values[80] == true) { //STRINGS 16 turned ON
      sendnote(Strings_Channel, (Current_Note - 12), notevel);
      }

 last_values[i] = input_values[i]; //write the sent values to the past value register ready for next cycle

}

}
//At end of read cycle, send mono note (highest or lowest) or switch all off

if (Any_Notes_Changed == true) { //only act if something changed
  if (Any_Notes_On == false) { //changed to switch off last note
    sendCC(Midi_Channel, 123, 0); //all notes off
    sendCC(Mono_Channel, 123, 0); //all notes off
    sendCC(Brass_Channel, 123, 0); //all notes off
    sendCC(Strings_Channel, 123, 0); //all notes off
    digitalWrite(Gate_Pin, LOW); //gate OFF
    }
  
  else if (Any_Notes_On == true) { //something changed, it is a note change
    digitalWrite(Gate_Pin, HIGH); //gate ON
    if (digitalRead(Priority_Select_Pin) == HIGH) { //Highest note priority selected - default
      if (Current_High_Note != Last_High_Note) { //change of high note
        sendnote(Mono_Channel, Last_High_Note, 0); //switch off last high note
        sendnote(Mono_Channel, Current_High_Note, 127);
        CV_Out = (Current_High_Note - 24)*multiple;
        analogWrite(CV_Pin, CV_Out); //set new CV
        Last_High_Note = Current_High_Note;
      }
    }
    else if(digitalRead(Priority_Select_Pin) == LOW) { //Lowest note priority selected - pin earthed
      if (Current_Low_Note != Last_Low_Note) { //change of low note
         sendnote(Mono_Channel, Last_Low_Note, 0); //switch off last low note
         sendnote(Mono_Channel, Current_Low_Note, 127);
         Last_Low_Note = Current_Low_Note;
         CV_Out = (Current_Low_Note - 24)*multiple;
         analogWrite(CV_Pin, CV_Out); //set new CV
         Current_Low_Note = 0;
      }
    }
   /*
  //output some analogue signal using PWM
  //AudioOut.begin(float(mtof.toFrequency(Current_Note)), 50.0f); // ought to generate a square wave at lowest note frequency
}

*/
   
  }
}


//then do bass notes

if(input_values[89] == true) {//First check if bass switch 89 is engaged
for(uint8_t i=48; i<60; i++) {
  if (input_values[i] != last_values[i]) { //change of state
    if (input_values[62] == true) {
      Bass_Offset = 12;
  }
  else if (input_values[61] == true) {
      Bass_Offset = 24;
  }
  else if (input_values[60] == true) {
      Bass_Offset = 36;
  } 
    Bass_Note = 59 + Bass_Offset - i; 

    if (input_values[i] == true) { //note turned ON
      notevel = 127;
    }
    else { //note turned OFF
      notevel = 0;
    }
    sendnote(Bass_Channel, Bass_Note, notevel);
    if (input_values[85] == true) { //strings on bass
      sendnote(Strings_Channel, (Bass_Note + 36), notevel);
    }
    last_values[i] = input_values[i]; //write the sent values to the past value register ready for next cycle
  }
}
}

//then do chords
if(input_values[91] = true) {//First check if CHORDS switch 91 is engaged
for(uint8_t i=64; i<76; i++) {
  if (input_values[i] != last_values[i]) { //change of state
    Chord_Note = 72 + 48 - i;
    if (input_values[i] == true) { //note turned ON
      notevel = 127;
    }
    else { //note turned OFF
      notevel = 0;
    }
   sendnote(Chord_Channel, Chord_Note, notevel); // the root note
   if (input_values[86] == true) { //strings on chords
     sendnote(Strings_Channel, Chord_Note, notevel);
    }
   if (input_values[87] == true) { //brass on chords
     sendnote(Brass_Channel, Chord_Note, notevel);
    }

   
   last_values[i] = input_values[i]; //write the sent values to the past value register ready for next cycle 
  }

}
}

//Handle switches
//cc messages first
//isolated ones first
j=90;
    if (input_values[j] != last_values[j]) { //change of state
        sendCC(ChannelNums[j], CCNums[j], (input_values[j]*127));
        last_values[j] = input_values[j]; //write the sent values to the past value register ready for next cycle
    } 

j=124;
    if (input_values[j] != last_values[j]) { //change of state
        sendCC(ChannelNums[j], CCNums[j], (input_values[j]*127));
        last_values[j] = input_values[j]; //write the sent values to the past value register ready for next cycle
    }

//then consecutive runs
for(uint8_t i=92; i<112; i++) {
    if (input_values[i] != last_values[i]) { //change of state
        sendCC(ChannelNums[i], CCNums[i], (input_values[i]*127));
        last_values[i] = input_values[i]; //write the sent values to the past value register ready for next cycle
    } 
}

for(uint8_t i=119; i<122; i++) {
    if (input_values[i] != last_values[i]) { //change of state
        sendCC(ChannelNums[i], CCNums[i], (input_values[i]*127));
        last_values[i] = input_values[i]; //write the sent values to the past value register ready for next cycle
    } 
}

//then pc messages
//isolated one first
j=63;
    if (input_values[j] != last_values[j]) { //change of state
      if (input_values[j] == true) {
        sendPC(ChannelNums[j], PCNums[j]);
        }
      last_values[j] = input_values[j]; //write the sent values to the past value register ready for next cycle
    }

//then consecutive run
for(uint8_t i=76; i<90; i++) {  //change of state
    if (input_values[i] != last_values[i]) {
      if (input_values[i] == true) {
        sendPC(ChannelNums[i], PCNums[i]);
        }
      last_values[i] = input_values[i]; //write the sent values to the past value register ready for next cycle
    }
}

}

//=======================SEND MIDI NOTE====================
void sendnote(uint8_t channel, uint8_t noteval, uint8_t notevel) {
  if(noteval != 0) {
  Serial1.write(noteON_Base + channel);
  Serial1.write(noteval);
  Serial1.write(notevel);
  }
}


//=======================SEND MIDI CC====================
void sendCC(uint8_t channel, uint8_t CCnum, uint8_t CCval) {
  if (CCnum != 0) {
  Serial1.write(CC_Base + channel);
  Serial1.write(CCnum);
  Serial1.write(CCval);
//=================adaptation for Yamaha Reface to set rotary - comment out if using a different Midi instrument
  if (CCnum == 77) { //vibrato on/off
    if(CCval == 0) {
        Serial1.write(CC_Base + channel);
        Serial1.write(19);
        Serial1.write(CCval);
    }
    else {
        Serial1.write(CC_Base + channel);
        Serial1.write(19);
        Serial1.write(85);
    } 
    }
  if (CCnum == 76) { //vibrato fast/slow
    if(CCval == 0) {
        Serial1.write(CC_Base + channel);
        Serial1.write(19);
        Serial1.write(85);
    }
    else {
        Serial1.write(CC_Base + channel);
        Serial1.write(19);
        Serial1.write(127);
    } 
    }

//=================
}
}

//=======================SEND MIDI PC====================
void sendPC(uint8_t channel, uint8_t PCval) {
  if (PCval != 0) {
  Serial1.write(PC_Base + channel);
  Serial1.write(PCval);
  //=================adaptation for Yamaha Reface to set voice - comment out if using a different Midi instrument
  if(PCval == 16) {
    sendCC(Midi_Channel, 80, 32);
  }
  else if(PCval == 17) {
    sendCC(Midi_Channel, 80, 0);
  }
  else if(PCval == 18) {
    sendCC(Midi_Channel, 80, 64);
  }
//=================
  }
}


//=======================INTERRUPT ROUTINES====================
void TE_ISR() {
  input_index = 0;
}

void CLK_ISR() {
  if (input_index < 125) { //test shouldn't be necessary as TE occurs first, but just in case.  Could reduce to 125
    input_values[input_index] = digitalRead(DATA_Pin);
    input_index++;
  }
}

Credits

dmusker
4 projects • 3 followers
Contact

Comments

Please log in or sign up to comment.