Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 4 of 4

Thread: Need help to MIDI control ADSR envelope

  1. #1
    Junior Member
    Join Date
    Nov 2019
    Posts
    18

    Need help to MIDI control ADSR envelope

    Hello everyone!

    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 ... Probably my code is messed up ... But where?


    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);
          }
    }

  2. #2
    Junior Member
    Join Date
    Nov 2019
    Posts
    18
    Damn I forgot to specify, I use a Teensy 3.6 and an MPC4922 for the DAC

  3. #3
    Junior Member
    Join Date
    Nov 2019
    Posts
    18
    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;}
            }
    }

  4. #4
    Junior Member
    Join Date
    Nov 2019
    Posts
    18
    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •