Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
Hackster is hosting Impact Spotlights: Motorized movement. Watch the stream live on Thursday!Hackster is hosting Impact Spotlights: Motorized movement. Stream on Thursday!
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 provided315
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 • 4 followers
Contact

Comments

Please log in or sign up to comment.