Need help to MIDI control ADSR envelope

Anoat

Active member
Hello everyone!:cool:

I come to implore your help for my project ...

Let me explain THE PROBLEM:

I am making an analog synthesizer totally controllable in MIDI.

For the management of ADSR envellope I use the OZOE module (https://www.ozoe.fr/), the Ozoe envellope is based on an arduino, the code is available on its site. However, the ozoe envelope is designed to be driven in analog (4 pot and 3 switches).

The idea is basically relatively simple:

1- Remove all that is not useful to me in the code (EOC management)

2- implement MIDI listening in the code

3-replace the control of the 4potentiometers and the 3 switches by MIDI CC commands

4-Replace / Implement the management of an MCP4922 DAC

I am new to coding, however I want to learn.
I spent my last day trying to get the module to work ... In the end it was a total failure:mad: ... Probably my code is messed up ... But where?:confused:


thanks again

ps: sorry for my english (i'm french) ... i admit i use google trad

The ozoe code
Code:
//----------------------------------------------
// CHARGEMENT DES LIBRAIRIES
//----------------------------------------------
        #include <SPI.h>
        #include <math.h>

//----------------------------------------------
// CONSTANTES harware
//----------------------------------------------

// --- PORTC ---
        const int POT_A             = A4;  
        const int POT_D             = A3;  
        const int POT_S             = A2;  
        const int POT_R             = A1;  
        
// --- PORTB ---
        // SPI : 13=CLOCK  11=MOSI
        const int FORM01            = 12;
        const int CS                = 10;  //Chip Select du MCP4822
        const int FORM10            = 9;
        const int TIME10            = 8;
        
// --- PORTD ---
        const int TIME01            = 7;  
        const int MODE10            = 6;  
        const int MODE01            = 5;
       
    
        const int GATE_IN           = 2;  

//----------------------------------------------
// VARIABLES GLOBALES
//----------------------------------------------
        // Possibilitee d adapter le temps maxi des entrees TIMExx
        //             +-------------+--------+--------+-------+
        //             + VALEUR      |   5s   |   0.5  |  60s  |
        //             + UNITE       |    s   | 1/10s  |   s   |
        //             + Choix_timer |   0    |    1   |    2  |
        //             + Choix_timer |  MIDLE |  SHORT |  LONG |
        //             +             |        |        |       |
        byte  DureeRef[3]= {              5   ,   10   ,  60   }; //  MODIFIABLE
        //             +             |        |        |       |
        //             + MAXI        |  128   |        |  128  |
        //             +-------------+--------+--------+-------+

        float         k_timer         = 0.00; // 
        byte          klog         =4; // Courbure equivalente à celles des condensateurs

        unsigned long TimerStartTop   = 0;   // instant en microsecondes du dernier evenement start ou stop
        unsigned long TimerIHM        = 0;   // Intervalle de temps en milliseconde du scan IHM
        unsigned long TimeRefA        = 0;   // Duree en microsecondes de l'Attack
        unsigned long TimeRefD        = 0;   // Duree en microsecondes du Decay
        unsigned long TimeRefR        = 0;   // Duree en microsecondes du Release
        unsigned long tempsreel;             // Differentiel du Temps en microseconde entre l'evenement et le temps absolu
        unsigned long TimerEOC        = 0;   // Debut du Temps en microsecondes de l'impulse de l'OEC
        unsigned long  Offset         = 0; // Amortissement des montees et descente du MCP
        
        
                 int  tension         = 0;   // Tension courante en millivolts
                 int  tensionlin         = 0;   // Tension courante en millivolts
                 int  tensionlog         = 0;   // Tension courante en millivolts
                 int  memo_tension    = 0;   // Tension boucle-1 en millivolts
        unsigned int  TensionRefS     = 0;   // Tension (mV) de reference du SUSTAIN
        unsigned int  TensionStartTop = 0;   // Tension (mV) du dernier evenement start ou stop
        unsigned int  DureeEOC        = 0;   // Duree de l'impulse de l'OEC
        
        byte          Etat_module     = 0;   // 0=NOP      1=A       2=D     3=S     4=R
        byte          Choix_forme     = 0;   // 0=LIN      1=LOG     2=EXP
        byte          Choix_timer     = 0;   // 0=x1       1=x0.1    2=x10
        byte          Choix_gate_in   = 0;   // 0=GATE_IN  1=TRIG_IN 2=LOOP
        byte          ihm_i           = 0;   // temporaire
        
        boolean       Etat_gate_in    = 0;   // Copie de l'etat de l'entree gate  (1=on  0=off)
        boolean       FlagEOC         = 0;   // etat de la sortie End Of Cycle (1=on  0=off)
        boolean       StartTop        = 0;   // Flag fugitif sur l'Etat_gate_in  (1=actif  0=deactive)
        boolean       PatchV2         = 0;   // 0=organisation V3 - 1=Organisation V2 || Organise les inverseurs selon l'ordre de la V2  MODIFIABLE 
        

//----------------------------------------------
// INITIALISATION
//----------------------------------------------        
void setup() {
      //Serial.begin(9600);  // Seulement pour la mise au point
      analogReference(DEFAULT);
     
      pinMode(CS,     OUTPUT);
      
      pinMode(FORM01, INPUT_PULLUP);
      pinMode(FORM10, INPUT_PULLUP);
      pinMode(TIME01, INPUT_PULLUP);
      pinMode(TIME10, INPUT_PULLUP);
      pinMode(MODE01, INPUT_PULLUP);
      pinMode(MODE10, INPUT_PULLUP);
      pinMode(GATE_IN,INPUT_PULLUP);
       
      // INITIALISATION INTERRUPTION (0=INT 0 = Gate_In = Pin 2 )
      attachInterrupt(0, Gate_In_Up, FALLING);  // O:INT0:GATE_IN 

  
      
      // GESTION MCP4822 sur le bus SPI
      SPI.begin();
      SPI.setClockDivider(32);    // defaut : 4. peut être 2,4,8,16,32,64,128
      SPI.setDataMode(SPI_MODE0); // mode: SPI_MODE0, SPI_MODE1, SPI_MODE2, or SPI_MODE3
      PORTB |=(1<<CS-8);          // HIGH Desactive le chip
      ECRIREDAC();
      
      // INITIALISATION VALEURS IHM
      for (byte i=0;i<8;i++){
              TimerIHM= millis()+100;
              IHM();
      }
      
      // Fin INITIALISATION
     
      TimerStartTop=micros();
      TimerIHM= millis()+100;
      
}

//----------------------------------------------
// BOUCLE PRINCIPALE
//----------------------------------------------
void loop() {
        IHM();                // GESTION DES INVERSEURS ET POTENTIOMETRES
        AUTOMATE();           // GESTION DE L'ENCHAINEMENT DES TACHES
        CALCUL();             // DETERMINATION DE LA TENSION DE SORTIE
        ECRIREDAC();          // ENVOIE DE LA TENSION DE SORTIE SUR LE DAC
}

//----------------------------------------------
// ENVOI DE LA TENSION DE SORTIE AU DAC
//----------------------------------------------
void ECRIREDAC(){
        if (memo_tension!=tension){
                memo_tension=tension;
                noInterrupts();     // Ne pas deranger
                PORTB &=~(1<<CS-8); // LOW
                      SPDR=highByte(tension)| B00010000; while (!(SPSR&(1<<SPIF))); // Gainx2
                      SPDR=lowByte(tension); while (!(SPSR&(1<<SPIF)));
                PORTB |=(1<<CS-8);  // HIGH
                interrupts();
        }
}

//----------------------------------------------
// CALCUL DE LA TENSION DE SORTIE (0->4095mV)
//----------------------------------------------
void CALCUL(){
        //----------------------------------------------------------------------ATTACK--
        if (Etat_module==1){
                tempsreel=(micros()-TimerStartTop);
                tension=4095-TensionStartTop;
                tension*=float(tempsreel*k_timer/(TimeRefA*(4095.00-TensionStartTop)/4095.00));
                tension+=TensionStartTop;
                if      (Choix_forme==1){ // LOG
                        tension+=512;// V3.01
                        float k=(4095-TensionStartTop)/3.61;
                        tension=(log10(tension-TensionStartTop)*k*klog)+(4095-(log10(4095-TensionStartTop)*k*klog));
                }
                if      (Choix_forme==2){ // EXP
                        //float k=log(4095.00-TensionStartTop);  // V3.01  Suppress 
                        //k=k*float(tempsreel*k_timer/(TimeRefA*(4095.00-TensionStartTop)/4095.00));
                        //if (k>16){tension=4095;}else{tension=exp(k)+TensionStartTop;}
                        tensionlin=tension;
                        tension=4095-TensionStartTop;
                        float k=tension/3.61;
                        tension*=float((TimeRefA-(tempsreel*k_timer))/(TimeRefA*(4095.00-TensionStartTop)/4095.00));
                        tensionlog=(log10(tension-TensionStartTop)*k*klog)+(4095-(log10(4095-TensionStartTop)*k*klog));
                        tension=tensionlin-tensionlog+tension;
                        
                }
                tension=constrain(tension,TensionStartTop,4095); 
        }
        
        //----------------------------------------------------------------------DECAY--
        else if (Etat_module==2){
                tempsreel=(micros()-TimerStartTop);
                tension=TensionStartTop-((TensionStartTop-TensionRefS)*(k_timer*tempsreel/TimeRefD*2)); // V3.01 "*2" = contraction
                if      (Choix_forme==1){//LOG => EXP en descente
                        float k=log(TensionStartTop-TensionRefS)*(1-(k_timer*tempsreel/TimeRefD));
                        if (k>16){tension=4095;}else{tension=exp(k)+TensionRefS-1;}
                }
                else if (Choix_forme==2){ // EXP => LOG en descente
                                                                                                       // V3.01 "*2" = contraction
                        tension=TensionRefS+((log10((TensionStartTop-(TensionStartTop*(k_timer*tempsreel/TimeRefD*2)))/(klog*100)))*((TensionStartTop-TensionRefS)/log10(TensionStartTop/(klog*100))));
                }
                tension=constrain(tension,TensionRefS-1,4095); 
        }
        
        //----------------------------------------------------------------------SUSTAIN--
        else if (Etat_module==3){tension=TensionRefS;}
        
        //----------------------------------------------------------------------RELEASE--
        else if (Etat_module==4){
                tempsreel=(micros()-TimerStartTop);
                tension=TensionStartTop-(k_timer*tempsreel*float(4095.00/TimeRefR*2));     //  LIN // V3.01 "*2" = contraction
                if      (Choix_forme==1){                                        //  LOG
                        float k=log(TensionStartTop)*(1-(k_timer*tempsreel/TimeRefR));
                        if (k>16){tension=4095;}else{tension=exp(k);tension-=50;} // V3.01 pour l'offset
                }
                else if (Choix_forme==2){                                        //  EXP
                                                                                          // V3.01 "*2" = contraction
                        tension=(log10((TensionStartTop-(TensionStartTop*(k_timer*tempsreel/TimeRefR*2)))/(klog*50)))*((TensionStartTop)/log10(TensionStartTop/(klog*50)));
                }
                tension=constrain(tension,0,TensionRefS);                
        }
}


//----------------------------------------------
// ENCHAINEMENT DES PHASES ENTRE ELLES
//   ET GESTION EOC
//----------------------------------------------
void AUTOMATE(){
        byte  mem1=Etat_module;        
        float mem2=k_timer;
        byte  k=1;  // V3.01
        if (Choix_forme==1){k=2;}// V3.01
   
            
        
        //------------------------------------------------------------------------------
        if (Etat_module==0 && ((Etat_gate_in==1)+(Choix_gate_in==1)>0) ){Etat_module=1;}

        else if (Etat_module==1 && Etat_gate_in==0 && Choix_gate_in==0 ){
                if (tension>TensionRefS){Etat_module=2;}else{Etat_module=4;}
        }

        else if (Etat_module==1 && tension>=4095){Etat_module=2;}
        
        else if (Etat_module==2 && (tension<=TensionRefS || k_timer*tempsreel>TimeRefD/k )){  // V3.01 "/k"
                if (Etat_gate_in==1){Etat_module=3;}else{Etat_module=4;}
        }

        else if (Etat_module==2 && StartTop==1){
               Etat_module=1;
               StartTop=0;
        }

        else if (Etat_module==3 && Etat_gate_in==0){Etat_module=4;}
        
        else if (Etat_module==4 && Etat_gate_in==1 ){Etat_module=1;}

        

        StartTop=0;
        if (Etat_module!=mem1 || k_timer!=mem2){// INITIALISATION DE L'INSTANT DE BASCULE ENTRE PHASE
                TimerStartTop=micros();
                TensionStartTop=constrain(tension,0,4095);
        }
}




//----------------------------------------------
// CALCUL DE LA DUREE DE LA PHASE (ADR) ASSOCIEE AU POTENTIOMETRE
//----------------------------------------------
unsigned long RefTime(unsigned int potar){
        unsigned long k=word(potar*(DureeRef[Choix_timer]));;
        k*=float(100000.00/1023.00);
        k+=Offset+100;
        return k;      
}

//----------------------------------------------
// PATCH DES INVERSEURS SELON L'ORGANISATION DE LA V2
//----------------------------------------------
void patch_inverseur(){

        //---------------------------------------------------
        //   PATCH
        // CABLAGE SELON V3 SUR UNE FACADE V2 et AJOUT d'un INVERSEUR 3 Positions sur la Forme
        //
        //     +-------------+---------+------------+----------+---------  V2 FROM V3  ----------+
        //       INVERSEUR     FORME     MODE GATE    TIME       FORME       MODE GATE     TIME 
        //     +-------------+---------+------------+----------+----------+-------------+--------+
        //          UP  =1      LOG         LOOP       x.1         LIN        GATE_IN      x10
        //         ZERO =0      LIN       GATE_IN      x1          EXP        LOOP         x1
        //         DOWN =2      EXP       TRIG_IN      x10         LOG        TRIG_IN      x.1
        //     +-------------+---------+------------+----------+----------+-------------+--------+

        byte i=Choix_gate_in;
        if (i==0){Choix_gate_in=1;}else if(i==1){Choix_gate_in=0;}else if(i==2){Choix_gate_in=2;}
        
        i=Choix_forme;
        if (i==0){Choix_forme=2;}else if(i==1){Choix_forme=0;}else if(i==2){Choix_forme=1;}   // Avec inverseur ON-OFF-ON
//        if (i==0){Choix_forme=1;}else if(i==1){Choix_forme=0;}                                // Avec inverseur ON-OFF (en V2 pas de forme EXP)
        
        i=Choix_timer;
        if (i==0){Choix_timer=0;}else if(i==1){Choix_timer=2;}else if(i==2){Choix_timer=1;}
        
        //---------------------------------------------------
}                        

//----------------------------------------------
// LECTURE PERIODIQUE DES INVERSEURS ET POTENTIOMETRES
//----------------------------------------------
void IHM(){
        if (millis()-TimerIHM>10){  // scan inverseurs ou d'un potard toutes les 60 ms maxi
                TimerIHM= millis();
                ihm_i++;
                if      (ihm_i==1){
                        byte pb=PINB;
                        byte pd=PIND;
                        Choix_gate_in = !bitRead(pd,MODE01)  +!bitRead(pd,MODE10)*2;
                        Choix_forme   = !bitRead(pb,FORM01-8)+!bitRead(pb,FORM10-8)*2;
                        Choix_timer   = !bitRead(pd,TIME01)  +!bitRead(pb,TIME10-8)*2;
                        
                        if (PatchV2){patch_inverseur();}

//                        if (Choix_timer==2){k_timer=0.100;}else{k_timer=1.00;}
                        if (Choix_timer==1){k_timer=1.00;}else{k_timer=0.05;} // V3.01
                        Offset=word(DureeRef[Choix_timer]*500);
                }
                else if (ihm_i==2){TimeRefA      = RefTime(analogRead(POT_A));TimeRefA/=2;} // V3.01 pour div/2
                else if (ihm_i==3){TimeRefD      = RefTime(analogRead(POT_D));}
                else if (ihm_i==4){TimeRefR      = RefTime(analogRead(POT_R));}
                else if (ihm_i==5){TensionRefS   = map(analogRead(POT_S),0,1023,1,4095);}
                else if (ihm_i==6){DureeEOC      = min(((TimeRefA/1000)+(TimeRefD/1000)+(TimeRefR/1000))/4 , 40 );}  // 40ms pour une utilisation habituelle.
                else if (ihm_i>7){ihm_i=0;}
        }
}

//----------------------------------------------
// Gestion du Gate_In par interruption sur INT0
//    (le gate_in est inverse par le bc547)
//----------------------------------------------
void Gate_In_Up(){
        Etat_gate_in=1;
        StartTop=1;
        
        attachInterrupt(0, Gate_In_Down, RISING );
}
void Gate_In_Down(){
        Etat_gate_in=0;
        
        attachInterrupt(0, Gate_In_Up, FALLING);        
}

void AffValeurs(){
       
                Serial.println();
}



the modified code
Code:
//----------------------------------------------
// CHARGEMENT DES LIBRAIRIES
//----------------------------------------------
       
        #include <math.h>
         #include <MCP4922.h>
        #include <SPI.h>
        MCP4922 DAC(11,13,10,14);
  
//----------------------------------------------
// VARIABLES GLOBALES
//----------------------------------------------
        // Possibilitee d adapter le temps maxi des entrees TIMExx
        //             +-------------+--------+--------+-------+
        //             + VALEUR      |   5s   |   0.5  |  60s  |
        //             + UNITE       |    s   | 1/10s  |   s   |
        //             + Choix_timer |   0    |    1   |    2  |
        //             + Choix_timer |  MIDLE |  SHORT |  LONG |
        //             +             |        |        |       |
        byte  DureeRef[3]= {              5   ,   10   ,  60   }; //  MODIFIABLE
        //             +             |        |        |       |
        //             + MAXI        |  128   |        |  128  |
        //             +-------------+--------+--------+-------+

        
        float         k_timer         = 0.00; // 
        byte          klog         =4; // Courbure equivalente à celles des condensateurs

        unsigned long TimerStartTop   = 0;   // instant en microsecondes du dernier evenement start ou stop
        unsigned long TimerIHM        = 0;   // Intervalle de temps en milliseconde du scan IHM
        unsigned long TimeRefA        = 112;   // Duree en microsecondes de l'Attack
        unsigned long TimeRefD        = 560;   // Duree en microsecondes du Decay
        unsigned long TimeRefR        = 560;   // Duree en microsecondes du Release
        unsigned long tempsreel;             // Differentiel du Temps en microseconde entre l'evenement et le temps absolu
       
        unsigned long  Offset         = 0; // Amortissement des montees et descente du MCP
        
                    
                 int  tension         = 0;   // Tension courante en millivolts
                 int  tensionlin      = 0;   // Tension courante en millivolts
                 int  tensionlog      = 0;   // Tension courante en millivolts
                 int  memo_tension    = 0;   // Tension boucle-1 en millivolts
        unsigned int  TensionRefS     = 560;   // Tension (mV) de reference du SUSTAIN
        unsigned int  TensionStartTop = 0;   // Tension (mV) du dernier evenement start ou stop
   
        
        byte          Etat_module     = 0;   // 0=NOP      1=A       2=D     3=S     4=R
        byte          Choix_forme     = 0;   // 0=LIN      1=LOG     2=EXP
        byte          Choix_timer     = 0;   // 0=x1       1=x0.1    2=x10
        byte          Choix_gate_in   = 0;   // 0=GATE_IN  1=TRIG_IN 2=LOOP
        
        
        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)
       

//----------------------------------------------
// INITIALISATION
//----------------------------------------------        
void setup() {
  
  usbMIDI.setHandleNoteOn(OnNoteOn);
  usbMIDI.setHandleNoteOff(OnNoteOff);
  usbMIDI.setHandleControlChange(OnControlChange);     
    
  SPI.begin();
 
   ECRIREDAC();
    
 
      // INITIALISATION VALEURS IHM
      
      for (byte i=0;i<8;i++){
              TimerIHM= millis()+100;
            
      }
  
      // Fin INITIALISATION
     
      TimerStartTop=micros();
      TimerIHM= millis()+100;
      
}


//----------------------------------------------
// BOUCLE PRINCIPALE
//----------------------------------------------
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
}

//----------------------------------------------
// Gestion du Gate_In par MIDI
//----------------------------------------------

void OnNoteOn(byte channel, byte pitch, byte velocity) {
  if(velocity > 0) {
    
   Etat_gate_in = 1;
   StartTop=1;
   
  }

  else {
   Etat_gate_in = 0;
   
  }
}

void OnNoteOff(byte channel, byte pitch, byte velocity) {
   Etat_gate_in = 0;
   
}
void AffValeurs(){
       
                Serial.println();
}

//----------------------------------------------
// ENVOI DE LA TENSION DE SORTIE AU DAC
//----------------------------------------------

void ECRIREDAC(){
          if (memo_tension!=tension){
                memo_tension=tension;
 
   DAC.Set(tension,tension);                 
        }
}


//----------------------------------------------
// CALCUL DE LA TENSION DE SORTIE (0->4095mV)
//----------------------------------------------
void CALCUL(){
        //----------------------------------------------------------------------ATTACK--
        if (Etat_module==1){
                tempsreel=(micros()-TimerStartTop);
                tension=4095-TensionStartTop;
                tension*=float(tempsreel*k_timer/(TimeRefA*(4095.00-TensionStartTop)/4095.00));
                tension+=TensionStartTop;
                if      (Choix_forme==1){ 
                        tension+=512;
                        float k=(4095-TensionStartTop)/3.61;
                        tension=(log10(tension-TensionStartTop)*k*klog)+(4095-(log10(4095-TensionStartTop)*k*klog));
                }
                if      (Choix_forme==2){ 
                        tensionlin=tension;
                        tension=4095-TensionStartTop;
                        float k=tension/3.61;
                        tension*=float((TimeRefA-(tempsreel*k_timer))/(TimeRefA*(4095.00-TensionStartTop)/4095.00));
                        tensionlog=(log10(tension-TensionStartTop)*k*klog)+(4095-(log10(4095-TensionStartTop)*k*klog));
                        tension=tensionlin-tensionlog+tension;
                        
                }
                tension=constrain(tension,TensionStartTop,4095); 
        }
        
        //----------------------------------------------------------------------DECAY--
        else if (Etat_module==2){
                tempsreel=(micros()-TimerStartTop);
                tension=TensionStartTop-((TensionStartTop-TensionRefS)*(k_timer*tempsreel/TimeRefD*2)); // V3.01 "*2" = contraction
                if      (Choix_forme==1){//LOG => EXP en descente
                        float k=log(TensionStartTop-TensionRefS)*(1-(k_timer*tempsreel/TimeRefD));
                        if (k>16){tension=4095;}else{tension=exp(k)+TensionRefS-1;}
                }
                else if (Choix_forme==2){ // EXP => LOG en descente
                                                                                                       // V3.01 "*2" = contraction
                        tension=TensionRefS+((log10((TensionStartTop-(TensionStartTop*(k_timer*tempsreel/TimeRefD*2)))/(klog*100)))*((TensionStartTop-TensionRefS)/log10(TensionStartTop/(klog*100))));
                }
                tension=constrain(tension,TensionRefS-1,4095); 
        }
        
        //----------------------------------------------------------------------SUSTAIN--
        else if (Etat_module==3){tension=TensionRefS;}
        
        //----------------------------------------------------------------------RELEASE--
        else if (Etat_module==4){
                tempsreel=(micros()-TimerStartTop);
                tension=TensionStartTop-(k_timer*tempsreel*float(4095.00/TimeRefR*2));     //  LIN // V3.01 "*2" = contraction
                if      (Choix_forme==1){                                        //  LOG
                        float k=log(TensionStartTop)*(1-(k_timer*tempsreel/TimeRefR));
                        if (k>16){tension=4095;}else{tension=exp(k);tension-=50;} // V3.01 pour l'offset
                }
                else if (Choix_forme==2){                                        //  EXP
                                                                                          // V3.01 "*2" = contraction
                        tension=(log10((TensionStartTop-(TensionStartTop*(k_timer*tempsreel/TimeRefR*2)))/(klog*50)))*((TensionStartTop)/log10(TensionStartTop/(klog*50)));
                }
                tension=constrain(tension,0,TensionRefS);                
        }
}

//----------------------------------------------
// ENCHAINEMENT DES PHASES ENTRE ELLES
//   
//----------------------------------------------
        
        void AUTOMATE(){
        byte  mem1=Etat_module;        
        float mem2=k_timer;
        byte  k=1;  // V3.01
        if (Choix_forme==1){k=2;}// V3.01
   
            
        
        //------------------------------------------------------------------------------
       
        if (Etat_module==0 && ((Etat_gate_in==1)+(Choix_gate_in==1)>0) ){Etat_module=1;}

        else if (Etat_module==1 && Etat_gate_in==0 && Choix_gate_in==0 ){
                if (tension>TensionRefS){Etat_module=2;}else{Etat_module=4;}
        }

        else if (Etat_module==1 && tension>=4095){Etat_module=2;}
        
        else if (Etat_module==2 && (tension<=TensionRefS || k_timer*tempsreel>TimeRefD/k )){  // V3.01 "/k"
                if (Etat_gate_in==1){Etat_module=3;}else{Etat_module=4;}
        }

        else if (Etat_module==2 && StartTop==1){
               Etat_module=1;
               StartTop=0;
        }

        else if (Etat_module==3 && Etat_gate_in==0){Etat_module=4;}
        
        else if (Etat_module==4 && Etat_gate_in==1 ){Etat_module=1;}

        

        StartTop=0;
        if (Etat_module!=mem1 || k_timer!=mem2){// INITIALISATION DE L'INSTANT DE BASCULE ENTRE PHASE
                TimerStartTop=micros();
                TensionStartTop=constrain(tension,0,4095);
        }
}
//----------------------------------------------
// CALCUL DE LA DUREE DE LA PHASE (ADR) ASSOCIEE AU POTENTIOMETRE
//----------------------------------------------
unsigned long RefTime(unsigned int potar){
        unsigned long k=word(potar*(DureeRef[Choix_timer]));;
        k*=float(100000.00/1023.00);
        k+=Offset+100;
        return k;      
}
//----------------------------------------------
// GESTION DES CONTROLES MIDI
//----------------------------------------------


void OnControlChange (byte channel, byte control, byte value) {
  if ( control == 1) {
  TimeRefA = map(value, 0, 127, 0, 1023);TimeRefA/=2;
      }
  if ( control == 2) {
  TimeRefD = map(value, 0, 127, 0, 1023);
      }
  if ( control == 3) {
  TensionRefS = map(value, 0, 127, 0, 4095);
      }
  if ( control == 4) {
  TimeRefR = map(value, 0, 127, 0, 1023);
      }
  if ( control == 5) {
  Choix_forme = map(value, 0, 127, 0, 2); 
      }
  if ( control == 6) {
  Choix_timer = map(value, 0, 127, 0, 2);
      }
  if (channel == 1 && control == 7) {
  Choix_gate_in = map(value, 0, 127, 0, 2);
      }
}
 
Its good !!! I found the solution !

I don't really understand why but the "word" variable was the problem .... I replaced it with "int" and everything works !!!!

Code:
//----------------------------------------------
// CHARGEMENT DES LIBRAIRIES
//----------------------------------------------
       
        #include <math.h>
         #include <MCP4922.h>
        #include <SPI.h>
        MCP4922 DAC(11,13,10,14);
  
//----------------------------------------------
// VARIABLES GLOBALES
//----------------------------------------------
        // Possibilitee d adapter le temps maxi des entrees TIMExx
        //             +-------------+--------+--------+-------+
        //             + VALEUR      |   5s   |   0.5  |  60s  |
        //             + UNITE       |    s   | 1/10s  |   s   |
        //             + Choix_timer |   0    |    1   |    2  |
        //             + Choix_timer |  MIDLE |  SHORT |  LONG |
        //             +             |        |        |       |
        byte  DureeRef[3]= {              5   ,   10   ,  60   }; //  MODIFIABLE
        //             +             |        |        |       |
        //             + MAXI        |  128   |        |  128  |
        //             +-------------+--------+--------+-------+

        
        float         k_timer         = 0.00; // 
        byte          klog         =4; // Courbure equivalente à celles des condensateurs

        unsigned long TimerStartTop   = 0;   // instant en microsecondes du dernier evenement start ou stop
        unsigned long TimerIHM        = 0;   // Intervalle de temps en milliseconde du scan IHM
        unsigned long TimeRefA        = 0;   // Duree en microsecondes de l'Attack
        unsigned long TimeRefD        = 0;   // Duree en microsecondes du Decay
        unsigned long TimeRefR        = 0;   // Duree en microsecondes du Release
        unsigned long tempsreel;             // Differentiel du Temps en microseconde entre l'evenement et le temps absolu

int POT_A;
int POT_D;
int POT_S;
int POT_R;


 int FORM01            = 0;
 int FORM10            = 0;
 int TIME10            = 0;
        

 int TIME01            = 0;  
 int MODE10            = 0;  
 int MODE01            = 0;
       
    
       
        unsigned long  Offset         = 0; // Amortissement des montees et descente du MCP
        
                    
                 int  tension         = 0;   // Tension courante en millivolts
                 int  tensionlin      = 0;   // Tension courante en millivolts
                 int  tensionlog      = 0;   // Tension courante en millivolts
                 int  memo_tension    = 0;   // Tension boucle-1 en millivolts
        unsigned int  TensionRefS     = 0;   // Tension (mV) de reference du SUSTAIN
        unsigned int  TensionStartTop = 0;   // Tension (mV) du dernier evenement start ou stop

        
        byte          Etat_module     = 0;   // 0=NOP      1=A       2=D     3=S     4=R
        byte          Choix_forme     = 0;   // 0=LIN      1=LOG     2=EXP
        byte          Choix_timer     = 0;   // 0=x1       1=x0.1    2=x10
        byte          Choix_gate_in   = 0;   // 0=GATE_IN  1=TRIG_IN 2=LOOP
         byte          ihm_i           = 0;   // temporaire
        
        boolean       Etat_gate_in    = 0;   // Copie de l'etat de l'entree gate  (1=on  0=off)
             boolean       PatchV2         = 0;   // 
        boolean       StartTop        = 0;   // Flag fugitif sur l'Etat_gate_in  (1=actif  0=deactive)
       

//----------------------------------------------
// INITIALISATION
//----------------------------------------------        
void setup() {
  
  usbMIDI.setHandleNoteOn(OnNoteOn);
  usbMIDI.setHandleNoteOff(OnNoteOff);
  usbMIDI.setHandleControlChange(OnControlChange);     
    
  SPI.begin();
 
   ECRIREDAC();
    
 
      // INITIALISATION VALEURS IHM
    for (byte i=0;i<8;i++){
              TimerIHM= millis()+100;
              IHM();
      }
  
      // Fin INITIALISATION
     
      TimerStartTop=micros();
      TimerIHM= millis()+100;
      
}


//----------------------------------------------
// BOUCLE PRINCIPALE
//----------------------------------------------
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
}

//----------------------------------------------
// Gestion du Gate_In par MIDI
//----------------------------------------------

void OnNoteOn(byte channel, byte pitch, byte velocity) {
  if(velocity > 0) {
    
   Etat_gate_in = 1;
   StartTop=1;
   
  }

  else {
   Etat_gate_in = 0;
   
  }
}

void OnNoteOff(byte channel, byte pitch, byte velocity) {
   Etat_gate_in = 0;
   
}
void AffValeurs(){
       
                Serial.println();
}

//----------------------------------------------
// ENVOI DE LA TENSION DE SORTIE AU DAC
//----------------------------------------------

void ECRIREDAC(){
          if (memo_tension!=tension){
                memo_tension=tension;
 
   DAC.Set(tension,tension);                 
        }
}


//----------------------------------------------
// CALCUL DE LA TENSION DE SORTIE (0->4095mV)
//----------------------------------------------
void CALCUL(){
        //----------------------------------------------------------------------ATTACK--
        if (Etat_module==1){
                tempsreel=(micros()-TimerStartTop);
                tension=4095-TensionStartTop;
                tension*=float(tempsreel*k_timer/(TimeRefA*(4095.00-TensionStartTop)/4095.00));
                tension+=TensionStartTop;
                if      (Choix_forme==1){ 
                        tension+=512;
                        float k=(4095-TensionStartTop)/3.61;
                        tension=(log10(tension-TensionStartTop)*k*klog)+(4095-(log10(4095-TensionStartTop)*k*klog));
                }
                if      (Choix_forme==2){ 
                        tensionlin=tension;
                        tension=4095-TensionStartTop;
                        float k=tension/3.61;
                        tension*=float((TimeRefA-(tempsreel*k_timer))/(TimeRefA*(4095.00-TensionStartTop)/4095.00));
                        tensionlog=(log10(tension-TensionStartTop)*k*klog)+(4095-(log10(4095-TensionStartTop)*k*klog));
                        tension=tensionlin-tensionlog+tension;
                        
                }
                tension=constrain(tension,TensionStartTop,4095); 
        }
        
        //----------------------------------------------------------------------DECAY--
        else if (Etat_module==2){
                tempsreel=(micros()-TimerStartTop);
                tension=TensionStartTop-((TensionStartTop-TensionRefS)*(k_timer*tempsreel/TimeRefD*2)); // V3.01 "*2" = contraction
                if      (Choix_forme==1){//LOG => EXP en descente
                        float k=log(TensionStartTop-TensionRefS)*(1-(k_timer*tempsreel/TimeRefD));
                        if (k>16){tension=4095;}else{tension=exp(k)+TensionRefS-1;}
                }
                else if (Choix_forme==2){ // EXP => LOG en descente
                                                                                                       // V3.01 "*2" = contraction
                        tension=TensionRefS+((log10((TensionStartTop-(TensionStartTop*(k_timer*tempsreel/TimeRefD*2)))/(klog*100)))*((TensionStartTop-TensionRefS)/log10(TensionStartTop/(klog*100))));
                }
                tension=constrain(tension,TensionRefS-1,4095); 
        }
        
        //----------------------------------------------------------------------SUSTAIN--
        else if (Etat_module==3){tension=TensionRefS;}
        
        //----------------------------------------------------------------------RELEASE--
        else if (Etat_module==4){
                tempsreel=(micros()-TimerStartTop);
                tension=TensionStartTop-(k_timer*tempsreel*float(4095.00/TimeRefR*2));     //  LIN // V3.01 "*2" = contraction
                if      (Choix_forme==1){                                        //  LOG
                        float k=log(TensionStartTop)*(1-(k_timer*tempsreel/TimeRefR));
                        if (k>16){tension=4095;}else{tension=exp(k);tension-=50;} // V3.01 pour l'offset
                }
                else if (Choix_forme==2){                                        //  EXP
                                                                                          // V3.01 "*2" = contraction
                        tension=(log10((TensionStartTop-(TensionStartTop*(k_timer*tempsreel/TimeRefR*2)))/(klog*50)))*((TensionStartTop)/log10(TensionStartTop/(klog*50)));
                }
                tension=constrain(tension,0,TensionRefS);                
        }
}

//----------------------------------------------
// ENCHAINEMENT DES PHASES ENTRE ELLES
//   
//----------------------------------------------
        
        void AUTOMATE(){
        byte  mem1=Etat_module;        
        float mem2=k_timer;
        byte  k=1;  // V3.01
        if (Choix_forme==1){k=2;}// V3.01
   
            
        
        //------------------------------------------------------------------------------
       
        if (Etat_module==0 && ((Etat_gate_in==1)+(Choix_gate_in==1)>0) ){Etat_module=1;}

        else if (Etat_module==1 && Etat_gate_in==0 && Choix_gate_in==0 ){
                if (tension>TensionRefS){Etat_module=2;}else{Etat_module=4;}
        }

        else if (Etat_module==1 && tension>=4095){Etat_module=2;}
        
        else if (Etat_module==2 && (tension<=TensionRefS || k_timer*tempsreel>TimeRefD/k )){  // V3.01 "/k"
                if (Etat_gate_in==1){Etat_module=3;}else{Etat_module=4;}
        }

        else if (Etat_module==2 && StartTop==1){
               Etat_module=1;
               StartTop=0;
        }

        else if (Etat_module==3 && Etat_gate_in==0){Etat_module=4;}
        
        else if (Etat_module==4 && Etat_gate_in==1 ){Etat_module=1;}

        

        StartTop=0;
        if (Etat_module!=mem1 || k_timer!=mem2){// INITIALISATION DE L'INSTANT DE BASCULE ENTRE PHASE
                TimerStartTop=micros();
                TensionStartTop=constrain(tension,0,4095);
        }
}



void patch_inverseur(){

        //---------------------------------------------------
        //   PATCH
        // CABLAGE SELON V3 SUR UNE FACADE V2 et AJOUT d'un INVERSEUR 3 Positions sur la Forme
        //
        //     +-------------+---------+------------+----------+---------  V2 FROM V3  ----------+
        //       INVERSEUR     FORME     MODE GATE    TIME       FORME       MODE GATE     TIME 
        //     +-------------+---------+------------+----------+----------+-------------+--------+
        //          UP  =1      LOG         LOOP       x.1         LIN        GATE_IN      x10
        //         ZERO =0      LIN       GATE_IN      x1          EXP        LOOP         x1
        //         DOWN =2      EXP       TRIG_IN      x10         LOG        TRIG_IN      x.1
        //     +-------------+---------+------------+----------+----------+-------------+--------+

        byte i=Choix_gate_in;
        if (i==0){Choix_gate_in=1;}else if(i==1){Choix_gate_in=0;}else if(i==2){Choix_gate_in=2;}
        
        i=Choix_forme;
        if (i==0){Choix_forme=2;}else if(i==1){Choix_forme=0;}else if(i==2){Choix_forme=1;}   // Avec inverseur ON-OFF-ON
//        if (i==0){Choix_forme=1;}else if(i==1){Choix_forme=0;}                                // Avec inverseur ON-OFF (en V2 pas de forme EXP)
        
        i=Choix_timer;
        if (i==0){Choix_timer=0;}else if(i==1){Choix_timer=2;}else if(i==2){Choix_timer=1;}
        
        //---------------------------------------------------
}                        



//----------------------------------------------
// GESTION DES CONTROLES MIDI
//----------------------------------------------


void OnControlChange (byte channel, byte control, byte value) {
      if ( control == 1  )  { 
       POT_A =  map(value, 0, 127, 0, 1023) ;
       }
        if ( control == 2  )  { 
         POT_D =  map(value, 0, 127, 0, 1023) ;
       }
        if ( control == 3  )  { 
         POT_S =  map(value, 0, 127, 0, 1023) ;
       }
        if ( control == 4  )  { 
         POT_R =  map(value, 0, 127, 0, 1023) ;
       }
        if ( control == 5  )  { 
         Choix_forme =  map(value, 0, 127, 0, 2) ;
       }
        if ( control == 6  )  { 
         Choix_timer =  map(value, 0, 127, 0, 2) ;
       }
        if ( control == 7  )  { 
         Choix_gate_in =  map(value, 0, 127, 0, 2) ;
       }

    
}
//----------------------------------------------
// CALCUL DE LA DUREE DE LA PHASE (ADR) ASSOCIEE AU POTENTIOMETRE
//----------------------------------------------
unsigned long RefTime(unsigned int potar){
unsigned long k=int(potar*(DureeRef[Choix_timer]));;
        k*=float(100000.00/1023.00);
        k+=Offset+100;
        return k;      
}    
 



// LECTURE PERIODIQUE DES INVERSEURS ET POTENTIOMETRES
//----------------------------------------------
void IHM(){


  
        if (millis()-TimerIHM>10){  
                TimerIHM= millis();
                ihm_i++;
              
                if      (ihm_i==1){
                                             
                        
                        if (Choix_timer==1){k_timer=1.00;}
                        else{k_timer=0.05;} 
                        Offset= int(DureeRef[Choix_timer]*500);
                }
                else if (ihm_i==2){TimeRefA       = RefTime(POT_A) ;TimeRefA/=2;} 
                else if (ihm_i==3){TimeRefD       = RefTime(POT_D);}
                else if (ihm_i==4){TimeRefR       = RefTime(POT_R);}
                else if (ihm_i==5){TensionRefS    = map(POT_S,0,1023,1,4095);}  
                else if (ihm_i>7){ihm_i=0;}
        }
}
 
good now step 2:

There it is well it works by midday in Usb, however for a synthesizer you need two envelopes, one for the vcf and one for the vca. So now the goal is to be able to manage two separate envelopes simultaneously.

Honestly, I have no idea how to proceed ... if you have any ideas, proposal, I'm a taker
 
Back
Top