Showing posts with label attack. Show all posts
Showing posts with label attack. Show all posts

Wednesday, 6 November 2013

Quick pictures update on ADSR

 Exp.attack at 0.4 and sustain level at 7/8 ( 20 Ms div)




A quick update to show that first try, worked as expected. Spent a few good hours in the process of starting to optimize the different building blocks id gathered, and make them interact. Tried to keep it as readable as possible and document the little quirks, and possible variations, references, etc in the implementation methods.
We have officially an ADSR with variable exponential attack, variable "decay-sustain ratio" ( allows both bypassing the sustain stage, and also looping/extending the sustain stage ), and variable duration/period... For now !


Next comes LFO's from all the wavetables available, simple filters, and start implementing simple convolution reverb. Lets see how far can i  "push the envelope" ( No pun intended).





Square wave and exp.attack at 0.5 ( .2 s Div )


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


Saturday, 12 October 2013

options to attack envelope


Ill keep it simple and share the graphic of the differences and options. Ramp legend is a linear ramp-up attack.
Following the example of period 128, we'd get : attack=32; decay=22; sustain=42; rlease=32.
the picture below is related to the ATTACK stage only.

More soon to come !



void adsr_calculation()
{
  uint16_t b = period >> 1;  // These operations help us achieve a cheap aproximation
  uint16_t a = b >> 3;  // of division by 3 (?!) with a loss of bits/precision, but weighed 
  //  enough for what we need, regarding the fact we wrap-up with  a modulo operator for
  //   the last variable sustain- a "greatest-integer function" of sorts .
 
 /*
 By combining the terms in an obvious manner we can reduce the number of operations:
   
   a = (period >> 2) + (period >> 4) 
   b += (b >> 4)
   b += (b >> 8)
   b += (b >> 16)
   There are more exciting ways to calculate division and remainders.
       b * 3 = shift left 1 bit and then add b
*/

  attack  = period >> 2; 
  decay   = (a <<1)+(a>>1)+(a>>2);
  rlease  = period >> 2;
  sustain = period -(attack + rlease + decay);
  // roughly the same as 
  // sustain = period %(attack + rlease + decay);
  led =!led;    //toggle led
  /* Serial.println ("a");
   Serial.println (a);
   Serial.println ("b");
   Serial.println (b);
   Serial.println ("attack");
   Serial.println (attack);
   Serial.println ("decay");
   Serial.println (decay);
   Serial.println ("sustain");
   Serial.println (sustain);
   Serial.println ("rlease");
   Serial.println (rlease);
   */
}

Regarding REAL division by three



// Crazy as this might sound, but the method below indeed does divide by 3. 
// All it needs for doing so is a single 64 bit multiplication and a shift 
// (multiplications might be 3 to 4 times faster than divisions 
// on your CPU). In a 64 bit application this code will be a lot faster than 
// in a 32 bit application (in a 32 bit application multiplying two 64 bit 
// numbers take 3 multiplications and 3 additions on 32 bit values) 
// - however, it might be still faster than a division on a 32 bit machine.
// It only works for constant numbers. Why ?!
// You always need to know the magic number (here 0xaaaaaaab, 
// 0b1010 1010 1010 1010 1010 1010 1010 1011 ; 2,863,311,531 decimal
// and the correct operations after the multiplication (shifts and/or additions 
// in most cases)and both is different depending on the number you want to divide// by (and to calculate both take too much CPU time, on the fly (that would be 
// slower than  hardware division). However, it's easy for a compiler to 
// calculate these during compile time 
static inline uint32_t div3 (
uint32_t bdivided
) {
  return (uint32_t)(((uint64_t)0xaaaaaaabULL * bdivided) >> 33);
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin (115200);
}

void loop() {
  // put your main code here, to run repeatedly: 
  uint32_t test=1000;
  uint32_t avar=div3(test);
  Serial.println(avar);
  delay(500);
}