I can't get the envelope to work

Status
Not open for further replies.

nubie

Active member
As I can't get the envelope to work in my project which is a bit complex, I made a very simple example to ask here. I'm not working with the audio shield, but it should be easy to replace to test or explain.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

#include <Bounce.h>


AudioSynthWaveformSine   sine1;         //xy=1141,940
AudioEffectEnvelope      envelope1;      //xy=1290,942
AudioMixer4              mixer1;         //xy=1501.6666666666665,946.6666666666665
AudioOutputAnalog        dac1;           //xy=1751,1062
AudioConnection          patchCord1(sine1, envelope1);
AudioConnection          patchCord2(envelope1, 0, mixer1, 0);
AudioConnection          patchCord3(mixer1, dac1);

// Switch select
#define trigSwitch_PIN 3

// bounce pour Select switch
Bounce trigSwitch = Bounce(trigSwitch_PIN, 15);

void setup() {
  Serial.begin(115200);
  AudioMemory(15);
  
  pinMode(trigSwitch_PIN, INPUT_PULLUP);

  envelope1.attack(10);
  envelope1.hold(100);
  envelope1.decay(250);
  envelope1.sustain(0);
  envelope1.release(10);

  sine1.frequency(1000);
  sine1.amplitude(1.0);
  
  AudioInterrupts();
}

void loop() {
  trigSwitch.update();  // listen to Select switch
  
 if (trigSwitch.fallingEdge()) {
   envelope1.noteOn();
  }

//  if (trigSwitch.risingEdge()) {
//    envelope1.noteOff();
//  }
}
If I click on my momentary switch and let go, nothing happens in the way an ADSR works when changing the parameters. No matter which parameter I change that I need (decay or release). So there's something I'm missing.
Attack works. Hold works (?). But these are not the parameters needed cause I need an envelope.
Sustain acts like opening a VCA. The sound remains open as soon as the value is more than 0. This is strange.
It is not doing what I'm expecting from an ADSR: attack, then decay, then sustain, then comes the release phase, then the sound stops.
Here it's just sustaining forever when the sustain value is 1.
And when the sustain value is at 0, the sound stops (after the hold time-- not really sure), no matter what I have in decay or release.

I'd like to know how this envelope is suposed to work. I look at the Teensy Audio Tutorial & Workshop video tutorial, but it's not clear to me.

Here a longer code where I set pots to change each param. Easier to see what happens, then changing the value in the code and compile each time.
Same result. Does not look like ADSR***

In this example the attack is OK. Acts like it should.
Hold?
Decay?
Sustain: as explained earlier.
Release: nothing.
Very strange!

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>

AudioSynthWaveformSine   sine1;         //xy=1141,940
AudioEffectEnvelope      envelope1;      //xy=1290,942
AudioMixer4              mixer1;         //xy=1501.6666666666665,946.6666666666665
AudioOutputAnalog        dac1;           //xy=1751,1062
AudioConnection          patchCord1(sine1, envelope1);
AudioConnection          patchCord2(envelope1, 0, mixer1, 0);
AudioConnection          patchCord3(mixer1, dac1);

// Switch select
#define trigSwitch_PIN 3

#define attackPot 10
#define holdPot 7
#define decayPot 6
#define sustainPot 5
#define releasePot 11

// store pot readings into ADSR parameter settings
int   attackParam;
int   decayParam;
float sustainParam;
int   releaseParam;

// limit maximum timing for parameters set by pots
#define attackMax  2000
#define decayMax   2000
#define releaseMax 2000

// bounce pour Select switch
Bounce trigSwitch = Bounce(trigSwitch_PIN, 15);

void setup() {
  Serial.begin(115200);
  AudioMemory(15);
  
  pinMode(trigSwitch_PIN, INPUT_PULLUP);

//  attackParam = 50;
//  decayParam = 50;
//  sustainParam = 0.5;
//  releaseParam = 250;

  // assign envelope parameters
  envelope1.delay(0);
  envelope1.attack(attackParam);    // max 11880 mS
  envelope1.hold(0);                // max 11880 mS
  envelope1.decay(decayParam);      // max 11880 mS
  envelope1.sustain(sustainParam);  // gain level from 0 to 1.0
  envelope1.release(releaseParam);  // max 11880 mS

  sine1.frequency(500);
  sine1.amplitude(1.0);
  
  AudioInterrupts();
}

void loop() {
  trigSwitch.update();  // listen to Select switch
  
  if (trigSwitch.fallingEdge()) {
    envelope1.noteOn();
  }
  
//  if (trigSwitch.risingEdge()) {
//    envelope1.noteOff();
//  }

// read in pots and adjust ADSR parameters
  attackParam  = map(analogRead(attackPot), 0, 1023, 0, attackMax);
  decayParam   = map(analogRead(decayPot), 0, 1023, 0, decayMax);
  sustainParam = map(analogRead(sustainPot), 0, 1023, 0, 1.0);
  releaseParam = map(analogRead(releasePot), 0, 1023, 0, releaseMax);

  // debug serial output
  Serial.print("Attack: ");
  Serial.print(attackParam);
  Serial.print("   Decay: ");
  Serial.print(decayParam);
  Serial.print("   Sustain: ");
  Serial.print(sustainParam);
  Serial.print("   Release: ");
  Serial.println(releaseParam);

  // apply ADSR parameters to sound generators
  envelope1.attack(attackParam);    // max 11880 mS
  envelope1.decay(decayParam);      // max 11880 mS
  envelope1.sustain(sustainParam);  // gain level from 0 to 1.0
  envelope1.release(releaseParam);  // max 11880 mS
}

***with an ADSR, I'd be able to make a drum/percussive sound when clicking on a button (or triggering via an external signal), like a drum sound from a AudioSynthSimpleDrum for instance.
 
Oops. I can't edit my post so here a tiny correction in the last code I posted.
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>

AudioSynthWaveformSine   sine1;         //xy=1141,940
AudioEffectEnvelope      envelope1;      //xy=1290,942
AudioMixer4              mixer1;         //xy=1501.6666666666665,946.6666666666665
AudioOutputAnalog        dac1;           //xy=1751,1062
AudioConnection          patchCord1(sine1, envelope1);
AudioConnection          patchCord2(envelope1, 0, mixer1, 0);
AudioConnection          patchCord3(mixer1, dac1);

// Switch select
#define trigSwitch_PIN 3

#define attackPot 10
#define holdPot 7
#define decayPot 6
#define sustainPot 5
#define releasePot 11

// store pot readings into ADSR parameter settings
int   attackParam;
int   holdParam;
int   decayParam;
float sustainParam;
int   releaseParam;

// limit maximum timing for parameters set by pots
#define attackMax  2000
#define holdMax  2000
#define decayMax   2000
#define releaseMax 2000

// bounce pour Select switch
Bounce trigSwitch = Bounce(trigSwitch_PIN, 15);

void setup() {
  Serial.begin(115200);
  AudioMemory(50);
  
  pinMode(trigSwitch_PIN, INPUT_PULLUP);

//  attackParam = 50;
//  decayParam = 50;
//  sustainParam = 0.5;
//  releaseParam = 250;

  // assign envelope parameters
  envelope1.delay(0);
  envelope1.attack(attackParam);    // max 11880 mS
  envelope1.hold(holdParam);                // max 11880 mS
  envelope1.decay(decayParam);      // max 11880 mS
  envelope1.sustain(sustainParam);  // gain level from 0 to 1.0
  envelope1.release(releaseParam);  // max 11880 mS

  sine1.frequency(500);
  sine1.amplitude(1.0);
  
  AudioInterrupts();
}

void loop() {
  trigSwitch.update();  // listen to Select switch
  
  if (trigSwitch.fallingEdge()) {
    envelope1.noteOn();
  }
  
//  if (trigSwitch.risingEdge()) {
//    envelope1.noteOff();
//  }

// read in pots and adjust ADSR parameters
  attackParam  = map(analogRead(attackPot), 0, 1023, 0, attackMax);
  holdParam   = map(analogRead(holdPot), 0, 1023, 0, holdMax);
  decayParam   = map(analogRead(decayPot), 0, 1023, 0, decayMax);
  sustainParam = map(analogRead(sustainPot), 0, 1023, 0, 1.0);
  releaseParam = map(analogRead(releasePot), 0, 1023, 0, releaseMax);

  // debug serial output
  Serial.print("Attack: ");
  Serial.print(attackParam);
  Serial.print("   Decay: ");
  Serial.print(decayParam);
  Serial.print("   Sustain: ");
  Serial.print(sustainParam);
  Serial.print("   Release: ");
  Serial.println(releaseParam);

  // apply ADSR parameters to sound generators
  envelope1.attack(attackParam);    // max 11880 mS
  envelope1.hold(holdParam);      // max 11880 mS
  envelope1.decay(decayParam);      // max 11880 mS
  envelope1.sustain(sustainParam);  // gain level from 0 to 1.0
  envelope1.release(releaseParam);  // max 11880 mS

}
To resume.
Attack works.
Hold works (I corrected my mistake).
Sustain does open or close the audio signal. Not what I'd expect.

Decay and release do not work and this is the main issue.
 
Sustain is a level, not a timing

Sustain is opening the signal as soon as it's > 0.
This is not logical.

Nubie:

Sustain is a *level* control (*not* a *timing* control), which specifies the volume level to be be held from the time decay completes until the time that the NoteOff is activated. If Sustain is set to 100% (1.0), then Decay will have not effect since Sustain will hold the note at full volume until the NoteOff is activated. If Sustain is set to something < 1.0, then the sound will Decay to the Sustain level, then stay at the Sustain level until the NoteOff is activated.

Using a simulated piano, a simulated organ, & a simulated clarinet for comparison, here's are the equivalents for the envelope controls:

Attack [how fast the sound rises] - a piano has an immediate attack (=0), an organ has a slower attac (>0), & a clarinet has a slower attack (>0)

Hold [how long to hold the sound at full volume before starting decay] - a piano has no hold (=0), an organ should probably be configured with no hold (=0) as well, & a clarinet should probably be configured with no hold (=0) as well

Decay [how fast does the sound fall off after full volume is reached, but before the NoteOff is activated] - a piano has a slow decay (=5 secs), an organ has no decay (=0), & a clarinet should probably be configured with no decay (=0)

Sustain [the volume level to maintain until the NoteOff is activated] - a piano has no sustain (=0), an organ would be configured with 100% sustain (=1.0), & a clarinet should probably be configured with 100% sustain (=1.0)

Release [how fast the sound drops after the NoteOff is activated] - a piano has a quick release (=0.5 secs), an organ has a medium release (=1.0 secs), & a clarinet has a medium release (=1.0 secs)

Tapping a key on a simulated piano & immediately releasing it will use Attack (when NoteOn is activated), Decay (until NoteOff is activated), & Release (after NoteOff is activated) - the result is a quick attack, then a quick release.

Holding a key on a simulated piano (shorter than the Decay period) will use Attack (when NoteOn is activated), Decay (until the NoteOff is activated), then Release - the result is a quick attack, a partial decay, then a quick release.

Holding a key on a simulated piano (longer than the Decay period) will use Attack (when NoteOn is activated) & Decay (until it ends) - the result is a quick attack, then a complete decay - release does not come into play as the sound is terminated by decay before the NoteOff is activated (when the key is let up).

Playing a note on a simulated organ (no matter how long) will use Attack (when NoteOn is activated), Sustain (until the NoteOff is activated), then Release - the result is a medium attack, a sustain, then a release - decay does not come into play since Sustain is set to 100% (=1.0).

Playing a note on a simulated clarinet (no matter how long) will use Attack (when NoteOn is activated), Sustain (until the NoteOff is activated), then Release - the result is a medium attack, a sustain, then a release - decay does not come into play since Sustain is set to 100% (=1.0).

Hope that these analogies help to visualize how the different parts of the envelope controls can either interact or hide/prevent the action of other parts, depending upon how you set each of them.

Mark J Culross
KD5RXT
 
Thank you so much for taking time Mark.
What I did in my code was to set sustain to 0, as I needed only attack & decay for the percussive results I was looking for, using trig events (very fast LOW/HIGH) to "shoot" a note, and not pressing/wait/releasing like we'd do when "playing" a note.
Now that I see your post, I understand that the noteOff is the key. I was surprised a sound with sustain value > 0 would never stop, but logically it could not without setting a noteOff. It makes sense now.
Again, thank you very much Mark.
 
Status
Not open for further replies.
Back
Top