Hello everyone,

It's been a while since I worked on the manufacture of an analog synthesizer totally controlled at midday.

Thanks to your help and support, I realize this project a little more every day.
Hello everyone,

It's been a while since I worked on the manufacture of an analog synthesizer totally controlled at midday.

Thanks to your help and support, I realize this project a little more every day.

I just finished the Teensy code that drives the synthesizer. I want to share it with you for your opinion and your advice to improve it.

The code works perfectly!
The Teensy 4.1 receives MIDI data by USB, depending on the data received, it controls two MAX11300 (chip which has 20 DACs in 0 / 10v) in SPI on the same port with two slaveselect. For that I used the Pixi_demo library from wolfgangfriedrich (available on Github), however I had to modify the code of this one to be able to implement a second Max11300.



The MAX11300 are used to generate the 2 envelopes (VCF / VCA), the notes for the VC01 and the VC02, and more particularly, the CVs which in turn control the VCAs (VCAs which replace the analog potentiometers,


The 36 pins of the teensy are used to activate digital switches (DG333, DG419).

Sincerely ANOAT

Code:
#include <Pixi.h>
#include <SPI.h>
#include <math.h>

Pixi pixi;

const boolean toggled = true;

const byte VCF_VCA = 2; // Enveloppe for VCF and VCA
const byte ADSR = 4; // pot control
const byte SW_GTF = 3; // switch control
const byte MIDI_L[VCF_VCA]    = {  115,    116};
const byte HR_MIDI[VCF_VCA][ADSR] = {{ 44, 45, 46, 47}, { 48, 49, 50, 51}};
const byte MIDI_GTF [SW_GTF][VCF_VCA] = {{ 103,    108}, { 104,    109}, { 105,    110}};

const int channel = 1; // MIDI channel
const int D_PINS = 13; // number of Digital PINS for swith on/off
const int SW3_1 =  29;
const int SW3_2 =  31;
const int MIDI_SW3 =  102;
const int SW4_1 = 1;        // SW4_1/SW4_2 is logic control for VCF (12db/24db/hp/bp)
const int SW4_2 = 2;        //Switch on1/on2/on3/on4
const int MIDI_SW4 = 112;

const int DAC = 13;
const int PIXI = 2;
const int LFO_POT = 6;

const int SW2[D_PINS] =      {36, 34, 32, 30, 33, 35,  3, 17, 20, 7,   5,  23, 4};
const int MIDI_SW2[D_PINS] = {95, 94, 93, 92, 90, 89, 88, 87, 86, 85, 84, 83 , 82 };

const int MIDI_SW_LFO [PIXI][LFO_POT] = {
  { 97,  98,  96, 100, 99, 101 },//  CC control switch on/off LFO 
  {106, 111, 107,   0,  0,   0 }
};

const int SW_LFO_A[PIXI][LFO_POT] =     {
  { 39,  41,  16,  26,  24,  37},//  Switch LFO A (potentiometre panning, centre= fermé, gauche LFO A, droite LFO B)
  { 18,  19,  22,   0,  0,   0 }
};

const int SW_LFO_B[PIXI][LFO_POT] =     {
  { 25,  15,  19,  38,  14,  27},//  Switch LFO A (potentiometre panning, centre= fermé, gauche LFO A, droite LFO B)
  {  6,   8,  21,   0,  0,   0 } 
};

const int CC_MIDI[PIXI][DAC] = {
  {32, 34,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},//  DAC B
  {37, 76, 77, 74, 75, 43, 41, 40, 39, 42, 91, 36, 38} //  DAC A
};


const int CC_LFO[PIXI][LFO_POT] = {
  {67, 68, 66, 72, 71, 73},       ///DAC B
  {79, 81, 80,  0,  0,  0 }       ///DAC A
};

const int CC_WAVE [2] = {33, 35};               // CC des sélecteurs de formes d'onde

float         k_timer[VCF_VCA]         = {0, 0};  //

unsigned long TimerStartTop[VCF_VCA]   = {0, 0};  // instant en microsecondes du dernier evenement start ou stop
unsigned long TimeRefA[VCF_VCA]        = {0, 0};  // Duree en microsecondes de l'Attack
unsigned long TimeRefD[VCF_VCA]        = {0, 0};  // Duree en microsecondes du Decay
unsigned long TimeRefR[VCF_VCA]        = {0, 0};  // Duree en microsecondes du Release
unsigned long tempsreel[VCF_VCA];                 // Differentiel du Temps en microseconde entre l'evenement et le temps absolu
unsigned long  Offset[VCF_VCA]         = {0, 0};  // Amortissement des montees et descente du MCP

byte high_byte[17][33];
byte note1;
byte note2;
byte mode ;

byte          klog                     = 4;       // Courbure equivalente à celles des condensateurs
byte          Etat_module[VCF_VCA]     = {0, 0};  // 0=NOP      1=A       2=D     3=S     4=R
byte          Choix_forme[VCF_VCA]     = {0, 0};  // 0=LIN      1=LOG     2=EXP
byte          Choix_timer[VCF_VCA]     = {0, 0};  // 0=x1       1=x0.1    2=x10
byte          Choix_gate_in[VCF_VCA]   = {0, 0};  // 0=GATE_IN  1=TRIG_IN 2=LOOP
byte          DureeRef[3] = {5, 10, 60};
byte          Etat_SW [VCF_VCA][SW_GTF];

bool top = false;
bool bot = false;

int out_value;
int SINE[2];
int TRI[2];
int TRIZ[2];
int TRIY[2];
int SAW[2];
int SAWY[2];
int SAWZ[2];
int PULSE[2];
int PWM [2];

int LFOA[2][6];
int LFOB[2][6];
int LFO [2][6];

int LFO_state[PIXI][LFO_POT];
int LFO_level[PIXI][LFO_POT];

int POT[VCF_VCA][ADSR];
int AMT[VCF_VCA];
int LEVEL [VCF_VCA];
int OUT[VCF_VCA];
int  tension[VCF_VCA]                   = {0, 0};  // Tension courante en millivolts
int  tensionlin[VCF_VCA]                = {0, 0};  // Tension courante en millivolts
int  tensionlog[VCF_VCA]                = {0, 0};  // Tension courante en millivolts
int  memo_tension[VCF_VCA]              = {0, 0};  // Tension boucle-1 en millivolts
unsigned int  TensionRefS[VCF_VCA]      = {0, 0};  // Tension (mV) de reference du SUSTAIN
unsigned int  TensionStartTop[VCF_VCA]  = {0, 0};  // Tension (mV) du dernier evenement start ou stop

float bend;
float pitch_value;
float divider = 120;                    //diviseur 1V/octave



boolean       reset_ADSR  ;          // Redémarage des envellopes en mode DUO
boolean       Etat_gate_in    = 0;   // Copie de l'etat de l'entree gate  (1=on  0=off)
boolean       StartTop        = 0;   // Flag fugitif sur l'Etat_gate_in  (1=actif  0=deactive)
boolean state[D_PINS];

/////////////// Les DAC utilisé sont des MAX11300, il en a 2, PIXI A et PIXI B/////////////////////////

char CH_LFO[PIXI][LFO_POT]  = {
  { CHANNEL_2,  CHANNEL_3,  CHANNEL_8, CHANNEL_13, CHANNEL_15, CHANNEL_18 }, /////DAC B
  { CHANNEL_12, CHANNEL_13, CHANNEL_14,     0,         0,           0     }  /////DAC A
};

char CH_X[PIXI][DAC]  =  {
  {CHANNEL_0, CHANNEL_11,         0,         0,         0,          0,          0,          0,          0,         0,          0,          0,           0},/////DAC B
  {CHANNEL_19, CHANNEL_0, CHANNEL_1, CHANNEL_2,  CHANNEL_3,  CHANNEL_5,  CHANNEL_6,  CHANNEL_7, CHANNEL_8, CHANNEL_9, CHANNEL_16, CHANNEL_17,  CHANNEL_18} /////DAC A
};
                  /// VCO1 ------ VCO2 ///
char CH_Si[2]  = {CHANNEL_10, CHANNEL_19 };
char CH_Tr[2]  = {CHANNEL_9,  CHANNEL_17 };
char CH_Sa[2]  = {CHANNEL_7,  CHANNEL_16 };
char CH_Pu[2]  = {CHANNEL_5,  CHANNEL_14 };
char CH_Pw[2]  = {CHANNEL_6,  CHANNEL_4 };


void setup() {

  word Pixi_ID = 0;
  float Temp = 0;
  word test = 0;

  Pixi_ID = pixi.configA();

  if (Pixi_ID == 0x0424 ) {
    Serial.print("Found PIXI module ID: 0x");
    Serial.println(Pixi_ID, HEX);

    pixi.configChannelA ( CHANNEL_19, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_0, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_1, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_2, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_3, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );

    pixi.configChannelA ( CHANNEL_5, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_6, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_7, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_8, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_9, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );

    pixi.configChannelA ( CHANNEL_10, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_11, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_12, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_13, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_14, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );

    pixi.configChannelA ( CHANNEL_16, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_17, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelA ( CHANNEL_18, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );

  }

  Pixi_ID = pixi.configB();

  if (Pixi_ID == 0x0424 ) {
    Serial.print("Found PIXI module ID: 0x");
    Serial.println(Pixi_ID, HEX);

    pixi.configChannelB ( CHANNEL_0, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );


    pixi.configChannelB ( CHANNEL_1, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_2, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_3, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_4, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_5, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_6, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_7, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_8, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_9, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_10, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );

    pixi.configChannelB ( CHANNEL_11, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );

    pixi.configChannelB ( CHANNEL_12, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_13, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_14, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_15, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_16, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_17, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_18, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );
    pixi.configChannelB ( CHANNEL_19, CH_MODE_DAC, 5, CH_0_TO_10P, 0 );

  }

  for (int i = 0; i < D_PINS; i++) {

    pinMode(SW2[i], OUTPUT);
    digitalWrite(SW2[i], LOW); // Toutes les pins out ont pour valeur LOW
  }


  pinMode(SW3_1, OUTPUT);
  digitalWrite(SW3_1, LOW); // Toutes les pins out ont pour valeur LOW
  pinMode(SW3_2, OUTPUT);
  digitalWrite(SW3_2, LOW);

  pinMode(SW4_1, OUTPUT);
  pinMode(SW4_2, OUTPUT);
  digitalWrite(SW4_1, LOW);
  digitalWrite(SW4_2, LOW);

  for (int i = 0; i < PIXI; i++) {
    for ( int k = 0 ; k < LFO_POT ; k++ ) {

      pinMode(SW_LFO_A[i][k], OUTPUT);
      pinMode(SW_LFO_B[i][k], OUTPUT);
      digitalWrite(SW_LFO_A[i][k], LOW);
      digitalWrite(SW_LFO_B[i][k], LOW); // Toutes les pins out ont pour valeur LOW
    }
  }
  usbMIDI.setHandleNoteOn(OnNoteOn);
  usbMIDI.setHandleNoteOff(OnNoteOff);
  usbMIDI.setHandlePitchChange(OnPitchChange);
  usbMIDI.setHandleControlChange (OnControlChange);
}

void loop() {

  usbMIDI.read();
  AUTOMATE();           // GESTION DE L'ENCHAINEMENT DES TACHES
  CALCUL();             // DETERMINATION DE LA TENSION DE SORTIE
  ECRIREDAC();          // ENVOIE DE LA TENSION DE SORTIE SUR LE DAC
  IHM();                // GESTION DES INVERSEURS ET POTENTIOMETRES
}

void OnControlChange (byte channel, byte control, byte value) {
  if ( control == 116) {
    mode = map(value, 0, 127, 0, 1);      //monophonic ou duophonic
  }
  if ( control == 117 ) {
    reset_ADSR = map(value, 0, 127, 0, 1);      //Reset ADSR DUO-MODE
  }
  for (int i = 0; i < PIXI ; i++) {
    for ( int k = 0 ; k < DAC ; k++ ) {
      if (control >= 64 && control <= 127) {
        out_value =  map(value, 0, 127, 0, 4095) ;  //Gestion des CC en 7bits
      }
      if (control == MIDI_L[i])
      { LEVEL[i] =  map(value, 0, 127, 0, 4092) ;
      }
      if (control >= 0 && control <= 31) {
        high_byte[channel][control] = value;
      }
      else if (control >= 32 && control <= 63) {
        out_value = (high_byte[channel][control - 32] << 7) + value;        //Gestion des CC en 14bits
        out_value = map(out_value, 0, 16383, 0, 4095);
      }
      if (control == CC_MIDI[0][k]) {
        pixi.writeAnalogB ( CH_X[0][k], out_value);
      }
      if (control == CC_MIDI[1][k]) {
        pixi.writeAnalogA ( CH_X[1][k], out_value);
      }
      for ( int j = 0 ; j < ADSR ; j++ ) {
        if (control == HR_MIDI[i][j]) {                                 //copie de l'etat des CC pour gestion des envellopes
          POT[i][j] =  map(out_value, 0, 4095, 0, 1023) ;
        }
      }
    }
    for ( int j = 0 ; j < SW_GTF ; j++ ) {
      if (control == MIDI_GTF[i][j])
      { Etat_SW[i][j] =  map(value, 0, 127, 0, 2) ;
        Choix_gate_in[i] = Etat_SW[0][i];                           //Switch des envellopes
        Choix_timer[i] = Etat_SW[1][i];
        Choix_forme[i] = Etat_SW[2][i];
      }
    }
//----------------------------------------------
//GESTION DES FORMES  (un CC permet de choisir l'onde entre sinus/triangle/saw/pulse/PWM)
//----------------------------------------------     
 
 //le changement se fait à l'aide de CV qui contrôle des VCA, de tel sorte que l'on évolue petit à petit d'une forme à l'autre//   
 
    for (int j = 0; j < 2 ; j++) {
      if (control == CC_WAVE[j]) {

        SINE[j] = TRIZ[j] = constrain(out_value, 0, 1023);
        SINE[j] = map( SINE[j], 0, 1023, 4095, 0);

        TRIZ[j] = map(TRIZ[j], 0, 1023, 0, 4095);         
        TRIY[j] = SAWZ[j] = constrain(out_value, 1023, 2047);
        TRIY[j] = map(TRIY[j], 1023, 2047, 0, 4095);
        TRI[j] = TRIZ[j] - TRIY[j];

        SAWZ[j] = map(SAWZ[j], 1023, 2047, 0, 4095);
        SAWY[j] = PULSE[j] = constrain(out_value, 2047, 3071);
        SAWY[j] = map( SAWY[j], 2047, 3071, 0, 4095);
        SAW[j] =  SAWZ[j] - SAWY[j];

        PULSE[j] = map(PULSE[j], 2047, 3071, 0, 4095);
        PWM[j] = constrain(out_value, 3071, 4095);
        PWM[j] = map(PWM[j], 3071, 4095, 0, 4095);

        pixi.writeAnalogB ( CH_Si[j], SINE[j]);
        pixi.writeAnalogB ( CH_Tr[j], TRI[j]);
        pixi.writeAnalogB ( CH_Sa[j], SAW[j]);
        pixi.writeAnalogB ( CH_Pu[j], PULSE[j]);
        pixi.writeAnalogB ( CH_Pw[j], PWM[j]);
      }
    }
//----------------------------------------------
//GESTION DU PANNING DES LFO PAR CV
//---------------------------------------------- 
  
    for ( int k = 0 ; k < LFO_POT ; k++ ) {
      if (control == CC_LFO[i][k]) {

        LFOA[i][k] = constrain(value, 0, 63);
        LFOA[i][k] = map(  LFOA[i][k], 0, 63, 4095, 0);
        LFOB[i][k] = constrain(value, 64, 127);
        LFOB[i][k] = map(  LFOB[i][k], 64, 127, 0, -4095);
        LFO [i][k] = LFOA[i][k] - LFOB[i][k];                                                           

        if ( CH_LFO[i][k] == CH_LFO[0][k]) {
          pixi.writeAnalogB ( CH_LFO[i][k], LFO[i][k]);
        }
        if ( CH_LFO[i][k] == CH_LFO[1][k]) {
          pixi.writeAnalogA ( CH_LFO[i][k], LFO[i][k]);
        }
        if (MIDI_SW_LFO[i][k] == control )
        {
          LFO_state [i][k] = map(value, 0, 127, 0, 1);
        }
        if (CC_LFO[i][k] == control )
        {
          LFO_level [i][k] = value ;
        }

        if (LFO_level [i][k] <= 62 && LFO_state [i][k] == 1  )
        { digitalWrite( SW_LFO_A [i][k], HIGH);
          digitalWrite( SW_LFO_B [i][k], LOW);
        }
        else {
          digitalWrite( SW_LFO_A [i][k], LOW);
        }

        if (LFO_level [i][k] >= 65 && LFO_state [i][k]  == 0 )
        { digitalWrite( SW_LFO_B [i][k], HIGH);
          digitalWrite( SW_LFO_A [i][k], LOW);
        }
        else {
          digitalWrite( SW_LFO_B [i][k], LOW);
        }
      }
    }
  }

  //// GESTION DES SWITCH ON/OFF////
  for (int i = 0; i < D_PINS ; i++)
  { if (MIDI_SW2[i] == control)
    { if (value >= 64)
      { digitalWrite(SW2[i], HIGH);
        state[i] = true;
      }        else {
        digitalWrite(SW2[i], LOW);
        state[i] = false;
      }
    }
  }
//----------------------------------------------
//GESTION DU SWITCH GLIDE
//---------------------------------------------- 
    

  if (MIDI_SW3 == control)
  { if (value >= 84)
    { digitalWrite(SW3_1, HIGH);
      digitalWrite(SW3_2, LOW);
    }
    else if (value < 84 && value > 42)
    { digitalWrite(SW3_1, LOW);
      digitalWrite(SW3_2, LOW);
    }
    else if (value < 42)
    { digitalWrite(SW3_1, LOW);
      digitalWrite(SW3_2, HIGH);
    }
  }
//----------------------------------------------
// GESTION DU SWITCH SELECTEUR DE PENTE DU VCF
//---------------------------------------------- 
  
  { if (MIDI_SW4 == control)
    { if (value >= 96)
      { digitalWrite(SW4_1, HIGH);
        digitalWrite(SW4_2, LOW);
      }
      else if (value < 96 && value > 64)
      { digitalWrite(SW4_1, LOW);
        digitalWrite(SW4_2, LOW);
      }
      else if (value < 64 && value > 32)
      { digitalWrite(SW4_1, LOW);
        digitalWrite(SW4_2, HIGH);
      }
      else if (value < 32)
      { digitalWrite(SW4_1, HIGH);
        digitalWrite(SW4_2, HIGH);
      }
    }
  }
}

//----------------------------------------------
// CLAVIER EN MODE MONOPHONIC
//----------------------------------------------

void OnNoteOn(byte channel, byte pitch, byte velocity) {
  if (mode == 0 && channel == 1 && velocity > 0) {
    pitch_value = constrain(pitch, 0, 120);
    pixi.writeAnalogB ( CHANNEL_1, ((pitch_value + bend) / divider) * 4095.0);        
    pixi.writeAnalogB ( CHANNEL_12, ((pitch_value + bend) / divider) * 4095.0);
    Etat_gate_in = 1;
    StartTop = 1;
    AMT[0] = map(velocity, 0, 127, 0, LEVEL[0]);
    AMT[1] = map(velocity, 0, 127, 0, LEVEL[1]);
  }
  else {
    Etat_gate_in = 0;
  }

//----------------------------------------------
// CLAVIER EN MODE DUOPHONIC
//----------------------------------------------
  
  if  (mode == 1 && top == false && channel == 1 && velocity > 0) {
    pitch_value = constrain(pitch, 0, 120);
    pixi.writeAnalogB ( CHANNEL_1, ((pitch_value + bend) / divider) * 4095.0);
    top = true;
    note1 = pitch ;
    Etat_gate_in = 1;
    StartTop = 1;
    AMT[0] = map(velocity, 0, 127, 0, LEVEL[0]);
    AMT[1] = map(velocity, 0, 127, 0, LEVEL[1]);
  }
  else if (mode == 1 && top == true && bot == false && channel == 1 && velocity > 0) {
    pitch_value = constrain(pitch, 0, 120);
    pixi.writeAnalogB ( CHANNEL_12, ((pitch_value + bend) / divider) * 4095.0);
    bot = true;
    note2 = pitch ;
    if (reset_ADSR = 1) {
      Etat_gate_in = 1;
      StartTop = 1;
      AMT[0] = map(velocity, 0, 127, 0, LEVEL[0]);
      AMT[1] = map(velocity, 0, 127, 0, LEVEL[1]);
    }
  }
}

void OnNoteOff(byte channel, byte pitch, byte velocity) {
  if (pitch == note1 && channel == 1) {
    top = false;
    Etat_gate_in = 0;
  }
  else if (pitch == note2 && channel == 1) {
    bot = false;
    Etat_gate_in = 0;
  }
}

void OnPitchChange (byte channel, int pitch_change) {
  if (channel == 1) {
    bend = map(pitch_change, 8191 , -8192, 0, 24);
  }
}
//----------------------------------------------
// ENVOI DE LA TENSION DE SORTIE AU DAC
//----------------------------------------------

void ECRIREDAC() {
  for (int i = 0; i < VCF_VCA ; i++) {
    if (memo_tension[i] != tension[i]) {
      memo_tension[i] = tension[i];

      OUT[i] = map(tension[i], 0, 4095, 0, AMT[i]);

      pixi.writeAnalogA ( CHANNEL_10, OUT[0]);
      pixi.writeAnalogA ( CHANNEL_11, OUT[1]);
    }
  }
}

//----------------------------------------------
// CALCUL DE LA TENSION DE SORTIE (0->4095mV)
//----------------------------------------------
void CALCUL() {
  for (int i = 0; i < VCF_VCA ; i++)
    //----------------------------------------------------------------------ATTACK--
    if (Etat_module[i] == 1) {
      tempsreel[i] = (micros() - TimerStartTop[i]);
      tension[i] = 4095 - TensionStartTop[i];
      tension[i] *= float(tempsreel[i] * k_timer[i] / (TimeRefA[i] * (4095.00 - TensionStartTop[i]) / 4095.00));
      tension[i] += TensionStartTop[i];
      if      (Choix_forme[i] == 1) {
        tension[i] += 512;
        float k = (4095 - TensionStartTop[i]) / 3.61;
        tension[i] = (log10(tension[i] - TensionStartTop[i]) * k * klog) + (4095 - (log10(4095 - TensionStartTop[i]) * k * klog));
      }
      if      (Choix_forme[i] == 2) {
        tensionlin[i] = tension[i];
        tension[i] = 4095 - TensionStartTop[i];
        float k = tension[i] / 3.61;
        tension[i] *= float((TimeRefA[i] - (tempsreel[i] * k_timer[i])) / (TimeRefA[i] * (4095.00 - TensionStartTop[i]) / 4095.00));
        tensionlog[i] = (log10(tension[i] - TensionStartTop[i]) * k * klog) + (4095 - (log10(4095 - TensionStartTop[i]) * k * klog));
        tension[i] = tensionlin[i] - tensionlog[i] + tension[i];

      }
      tension[i] = constrain(tension[i], TensionStartTop[i], 4095);
    }

  //----------------------------------------------------------------------DECAY--
    else if (Etat_module[i] == 2) {
      tempsreel[i] = (micros() - TimerStartTop[i]);
      tension[i] = TensionStartTop[i] - ((TensionStartTop[i] - TensionRefS[i]) * (k_timer[i] * tempsreel[i] / TimeRefD[i] * 2));
      if      (Choix_forme[i] == 1) { //LOG => EXP en descente
        float k = log(TensionStartTop[i] - TensionRefS[i]) * (1 - (k_timer[i] * tempsreel[i] / TimeRefD[i]));
        if (k > 16) {
          tension[i] = 4095;
        } else {
          tension[i] = exp(k) + TensionRefS[i] - 1;
        }
      }
      else if (Choix_forme[i] == 2) { // EXP => LOG en descente

        tension[i] = TensionRefS[i] + ((log10((TensionStartTop[i] - (TensionStartTop[i] * (k_timer[i] * tempsreel[i] / TimeRefD[i] * 2))) / (klog * 100))) * ((TensionStartTop[i] - TensionRefS[i]) / log10(TensionStartTop[i] / (klog * 100))));
      }
      tension[i] = constrain(tension[i], TensionRefS[i] - 1, 4095);
    }

  //----------------------------------------------------------------------SUSTAIN--
    else if (Etat_module[i] == 3) {
      tension[i] = TensionRefS[i];
    }

  //----------------------------------------------------------------------RELEASE--
    else if (Etat_module[i] == 4) {
      tempsreel[i] = (micros() - TimerStartTop[i]);
      tension[i] = TensionStartTop[i] - (k_timer[i] * tempsreel[i] * float(4095.00 / TimeRefR[i] * 2)); //  LIN
      if      (Choix_forme[i] == 1) {                                     //  LOG
        float k = log(TensionStartTop[i]) * (1 - (k_timer[i] * tempsreel[i] / TimeRefR[i]));
        if (k > 16) {
          tension[i] = 4095;
        } else {
          tension[i] = exp(k);
          tension[i] -= 50;
        }
      }
      else if (Choix_forme[i] == 2) {                                     //  EXP

        tension[i] = (log10((TensionStartTop[i] - (TensionStartTop[i] * (k_timer[i] * tempsreel[i] / TimeRefR[i] * 2))) / (klog * 50))) * ((TensionStartTop[i]) / log10(TensionStartTop[i] / (klog * 50)));
      }
      tension[i] = constrain(tension[i], 0, TensionRefS[i]);
    }
}

//----------------------------------------------
// ENCHAINEMENT DES PHASES ENTRE ELLES
//
//----------------------------------------------

void AUTOMATE() {
  for (int i = 0; i < VCF_VCA ; i++) {
    byte  mem1 = Etat_module[i];
    float mem2 = k_timer[i];
    byte  k = 1; // V3.01
    if (Choix_forme[i] == 1) {
      k = 2; // V3.01
    }

    //------------------------------------------------------------------------------

    if (Etat_module[i] == 0 && ((Etat_gate_in == 1) + (Choix_gate_in[i] == 1) > 0) ) {
      Etat_module[i] = 1;
    }
    else if (Etat_module[i] == 1 && Etat_gate_in == 0 && Choix_gate_in[i] == 0 ) {
      if (tension[i] > TensionRefS[i]) {
        Etat_module[i] = 2;
      } else {
        Etat_module[i] = 4;
      }
    }
    else if (Etat_module[i] == 1 && tension[i] >= 4095) {
      Etat_module[i] = 2;
    }
    else if (Etat_module[i] == 2 && (tension[i] <= TensionRefS[i] || k_timer[i]*tempsreel[i] > TimeRefD[i] / k )) {
      if (Etat_gate_in == 1) {
        Etat_module[i] = 3;
      } else {
        Etat_module[i] = 4;
      }
    }
    else if (Etat_module[i] == 2 && StartTop == 1) {
      Etat_module[i] = 1;
      StartTop = 0;
    }
    else if (Etat_module[i] == 3 && Etat_gate_in == 0) {
      Etat_module[i] = 4;
    }
    else if (Etat_module[i] == 4 && Etat_gate_in == 1 ) {
      Etat_module[i] = 1;
    }
    StartTop = 0;
    if (Etat_module[i] != mem1 || k_timer[i] != mem2) { // INITIALISATION DE L'INSTANT DE BASCULE ENTRE PHASE
      TimerStartTop[i] = micros();
      TensionStartTop[i] = constrain(tension[i], 0, 4095);
    }
  }
}

//----------------------------------------------
// CALCUL DE LA DUREE DE LA PHASE (ADR) ASSOCIEE AU POTENTIOMETRE
//----------------------------------------------
unsigned long RefTime(unsigned int potar) {
  for (int i = 0; i < VCF_VCA ; i++) {
    unsigned long k = int(potar * (DureeRef[Choix_timer[i]]));;
    k *= float(100000.00 / 1023.00);
    k += Offset[i] + 100;
    return k;
  }
}
// LECTURE PERIODIQUE DES INVERSEURS ET POTENTIOMETRES
//----------------------------------------------
void IHM()
{ for (int i = 0; i < VCF_VCA ; i++) {
    if (Choix_timer[i] == 1) {
      k_timer[i] = 1.00;
    }
    else {
      k_timer[i] = 0.05;
    }
    Offset[i] = int(DureeRef[Choix_timer[i]] * 500);

    TimeRefA[i]     = RefTime(POT[i][0]); TimeRefA[i] /= 2;
    TimeRefD[i]     = RefTime(POT[i][1]);
    TensionRefS[i]  = POT[i][2] * 4 ;
    TimeRefR[i]     = RefTime(POT[i][3]);
  }
}