Monday 28 October 2013

ADSR Exercises...

The exponential attack curves available will have these different options, in the final version !

I had to try to rethink the ADSR block of my project, as there are some variations that i want to include, and that make it different from other methods more popular often used in synths. Plus, what im building is not your average synth either, so here are some of the results of my tests ! Still need to test it as far as performance goes to be able to decide if ill go with an exponential attack( i am quite confident it can be calculated between interrupts, so as not to weigh too much on the interrupt load, itself !
I tried to make it as readable as possible; There are a lot of short cuts available to make it more efficient and optimize it. Which shall be taken into consideration in the final code . This one was wrote in tutorial style; several of the calculations can be shortened , optimized, for better performance. But then, it wouldn't have been as readable as this, i guess !
So, here it is for now !.


//*    Synth Building blocks 
//*    ATTACK-DECAY-SUSTAIN-RELEASE for Wavetable-based Generator #0.1
//*    Check section "Synth building blocks related" @
//*    http://dubworks.blogspot.co.uk/p/my-self-documented-tutorials.html
//*    Result of my experiences into the ADSR part of my project. Keep in mind that i decided to re-trhink it, 
//*    as what im working on is not your usual synth, but something else slightly(?!) different.
//*    This code was wrote for readability more than anything else ! There are a lot of short cuts available
//*    to make it more efficient and optimize it. Which shall be taken into consideration in the final code 

#define xinc (1.00/param.attack) // Allows an exponential attack
#define sigl 4095 // Despite being a constant here, it should be replaced by the wavetable equivalent
//
uint32_t phaseinc,output;
//
//  Declare the struct for our ADSR
struct ADSR {
  uint16_t period;  // total of the whole period
  uint16_t attack;
  uint16_t decay;
  uint16_t sustain;
  uint16_t rlease;
  float x;  // increment for period, to accum xinc by addition
};
struct ADSR param; // initialize the structure, so it is in scope !
//
//boolean led = false; 
//
void update (){
  if(phaseinc <= param.period){
    //
    if(phaseinc < param.attack){
      float y;
      y= pow(param.x , 0.50);  // Expon. attack-generator. This is where my latest tests been focusing on
      output= sigl * y;  //Ramp up can be achieved by just multiplying it by param.x as well, though 
      //  it would be a waste of computing power, as there are easier and "cheaper" ways to do it !

      //      Serial.print ("attack ");
      //      Serial.println (attack);
      //      Serial.print ("Output ");
      //      Serial.println (output);
    }
    else if(phaseinc >= param.attack && phaseinc <(param.attack + param.decay)){
      //
      output = sigl-((phaseinc - param.attack)*(512.0/param.decay)); //512= 4096 * 1/8
      //      Serial.println ("decay");
      //      Serial.println (param.decay);
      //      Serial.print ("Output ");
      //      Serial.println (output);
    }
    else if(phaseinc >=(param.attack + param.decay) && phaseinc <(param.attack + param.decay + param.sustain)){
      //
      //      Serial.print ("sustain");
      //      Serial.println (param.sustain);
      output = sigl *(7/8.00);
      //      Serial.print ("Output ");
      //      Serial.println (output);
    }
    else if(phaseinc >(param.attack + param.decay + param.sustain) && phaseinc < param.period){
      //
      //      Serial.print ("rlease");
      //      Serial.println (param.rlease);
      output=(param.rlease-(phaseinc-(param.attack+param.decay+param.sustain)))*((sigl*(7/8.00))/param.rlease);
      //      Serial.print ("Output ");
      //      Serial.println (output);  
    }
  }
}
//
void ADSR_calculation()
{ 
  //  There are more exciting ways to calculate division and remainders.
  //  We wrap-up with  a "modulo-like" operator for
  //  the last variable sustain- a "greatest-integer function" of sorts .
  //  This way we can go from decay to release directly. Todo list is to loop inside the sustain
  //  For now im planning using 3 sec/1 as MAXIMUM of the whole period for ADSR.
  uint16_t b    = param.period >> 1;  // These operations help us achieve a cheap aproximation
  param.attack  = param.period >> 2; 
  param.decay   = b/3;
  param.rlease  = param.period >> 2;
  param.sustain = param.period -(param.attack + param.rlease + param.decay); 
  // same as 
  // param.sustain = param.period %(param.attack + param.rlease + param.decay);
  /*if(Serial){
   Serial.print ("attack ");
   Serial.println (param.attack);
   Serial.print ("decay ");
   Serial.println (param.decay);
   Serial.print ("sustain ");
   Serial.println (param.sustain);
   Serial.print ("rlease ");
   Serial.println (param.rlease);
   }*/
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  //variable for the period; To be analogous to the analog read/s and should be declared with an initial 
  //value(21504?!), before the buffering of the initial values , at the time of wavetable generation
  param.period=128;  //this value should be the sum of all ADSR values, so we can have variable-lenght 
  //  on each of the individual parameters of the ADSR !It is already in the tODO list !
  //  But for now, ill leave it as it is: weighed proportionally !
  //
  ADSR_calculation();
}

void loop() {

//  if(Serial){
//    Serial.print ("phaseinc ");
//    Serial.println (phaseinc);
//  }
  //
  update();  
  //This can be done between interrupts, as there is some calculations involved.

  phaseinc++;
  //Serial.println(phaseinc);
  if( phaseinc >= param.period ){
    phaseinc= phaseinc - param.period;
    ADSR_calculation();
  }
  param.x += xinc;
  if (param.x >= 1){
    param.x = 0;
  }
  Serial.println (output);
}


No comments:

Post a Comment

Feel free to contact me with any suggestions, doubts or requests.

Bless