package ddf.minim.ugens;
import ddf.minim.AudioOutput;
import ddf.minim.UGen;
/**
* A UGen that plays input audio through a standard ADSR (Attack, Decay, Sustain, Release)
* envelope based on time from noteOn and noteOff.
*
* @example Synthesis/ADSRExample
*
* @author Anderson Mills
*
*/
public class ADSR extends UGen
{
/**
* The default input is "audio."
* You won't need to patch to this directly, since
* simply patching to the ADSR itself will achieve
* the same result.
*
* @related ADSR
*/
public UGenInput audio;
// amplitude before the ADSR hits
private float beforeAmplitude;
// amplitude after the release of the ADSR
private float afterAmplitude;
// the max amplitude of the envelope
private float maxAmplitude;
// the current amplitude
private float amplitude;
// the time of the attack
private float attackTime;
// the time of the decay
private float decayTime;
// the level of the sustain
private float sustainLevel;
// the time of the release
private float releaseTime;
// the current size of the step
private float timeStepSize;
// the time from noteOn
private float timeFromOn;
// the time from noteOff
private float timeFromOff;
// the envelope has received noteOn
private boolean isTurnedOn;
// the envelope has received noteOff
private boolean isTurnedOff;
// unpatch the note after it's finished
private boolean unpatchAfterRelease;
private AudioOutput output;
private UGen ugenOutput;
/**
* Constructor for an ADSR envelope.
* Maximum amplitude is set to 1.0.
* Attack and decay times are set to 1 sec.
* Sustain level is set to 0.0. Release time is set to 1 sec.
* Amplitude before and after the envelope is set to 0.
*/
public ADSR()
{
this(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f);
}
/**
* Constructor for an ADSR envelope with maximum amplitude.
* Attack and decay times are set to 1 sec.
* Sustain level is set to 0.0. Release time is set to 1 sec.
* Amplitude before and after the envelope is set to 0.
*
* @param maxAmp
* float: the maximum amplitude for the envelope
*/
public ADSR(float maxAmp)
{
this(maxAmp, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f);
}
/**
* Constructor for an ADSR envelope with maximum amplitude, attack Time.
* Decay time is set to 1 sec.
* Sustain level is set to 0.0. Release time is set to 1 sec.
* Amplitude before and after the envelope is set to 0.
*
* @param maxAmp
* float: the maximum amplitude for the envelope
* @param attTime
* float: the attack time, in seconds
*/
public ADSR( float maxAmp, float attTime )
{
this(maxAmp, attTime, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f);
}
/**
* Constructor for an ADSR envelope with maximum amplitude, attack Time, and decay time.
* Sustain level is set to 0.0. Release time is set to 1 sec.
* Amplitude before and after the envelope is set to 0.
*
* @param maxAmp
* float: the maximum amplitude for the envelope
* @param attTime
* float: the attack time, in seconds
* @param decTime
* float: the decay time, in seconds
*
*/
public ADSR( float maxAmp, float attTime, float decTime )
{
this(maxAmp, attTime, decTime, 0.0f, 1.0f, 0.0f, 0.0f);
}
/**
* Constructor for an ADSR envelope with maximum amplitude, attack Time, decay time, and sustain level.
* Release time is set to 1 sec. Amplitude before and after the envelope is set to 0.
*
* @param maxAmp
* float: the maximum amplitude for the envelope
* @param attTime
* float: the attack time, in seconds
* @param decTime
* float: the decay time, in seconds
* @param susLvl
* float: the percentage of the maximum amplitude to maintain after the decay completes
*/
public ADSR( float maxAmp, float attTime, float decTime, float susLvl )
{
this(maxAmp, attTime, decTime, susLvl, 1.0f, 0.0f, 0.0f);
}
/**
* Constructor for an ADSR envelope with maximum amplitude, attack Time, decay time, sustain level,
* and release time. Amplitude before and after the envelope is set to 0.
*
* @param maxAmp
* float: the maximum amplitude for the envelope
* @param attTime
* float: the attack time, in seconds
* @param decTime
* float: the decay time, in seconds
* @param susLvl
* float: the percentage of the maximum amplitude to maintain after the decay completes
* @param relTime
* float: the release time, in seconds
*/
public ADSR(float maxAmp, float attTime, float decTime, float susLvl, float relTime)
{
this(maxAmp, attTime, decTime, susLvl, relTime, 0.0f, 0.0f);
}
/**
* Constructor for an ADSR envelope with maximum amplitude, attack Time, decay time, sustain level,
* release time, an amplitude before the envelope. Amplitude after the envelope is set to 0.
*
* @param maxAmp
* float: the maximum amplitude for the envelope
* @param attTime
* float: the attack time, in seconds
* @param decTime
* float: the decay time, in seconds
* @param susLvl
* float: the percentage of the maximum amplitude to maintain after the decay completes
* @param relTime
* float: the release time, in seconds
* @param befAmp
* float: the amplitude to apply before the envelope is activated
*/
public ADSR(float maxAmp, float attTime, float decTime, float susLvl, float relTime, float befAmp)
{
this(maxAmp, attTime, decTime, susLvl, relTime, befAmp, 0.0f);
}
/**
* Constructor for an ADSR envelope.
*
* @param maxAmp
* float: the maximum amplitude for the envelope
* @param attTime
* float: the attack time, in seconds
* @param decTime
* float: the decay time, in seconds
* @param susLvl
* float: the percentage of the maximum amplitude to maintain after the decay completes
* @param relTime
* float: the release time, in seconds
* @param befAmp
* float: the amplitude to apply before the envelope is activated
* @param aftAmp
* float: the amplitude to apply once the envelope has completed
*/
public ADSR(float maxAmp, float attTime, float decTime, float susLvl, float relTime, float befAmp, float aftAmp)
{
super();
audio = new UGenInput(InputType.AUDIO);
maxAmplitude = maxAmp;
attackTime = attTime;
decayTime = decTime;
sustainLevel = susLvl;
releaseTime = relTime;
beforeAmplitude = befAmp;
afterAmplitude = aftAmp;
amplitude = beforeAmplitude;
isTurnedOn = false;
isTurnedOff = false;
timeFromOn = -1.0f;
timeFromOff = -1.0f;
unpatchAfterRelease = false;
}
/**
* Permits the changing of the ADSR parameters.
*
* @param maxAmp
* float: the maximum amplitude for the envelope
* @param attTime
* float: the attack time, in seconds
* @param decTime
* float: the decay time, in seconds
* @param susLvl
* float: the percentage of the maximum amplitude to maintain after the decay completes
* @param relTime
* float: the release time, in seconds
* @param befAmp
* float: the amplitude to apply before the envelope is activated
* @param aftAmp
* float: the amplitude to apply once the envelope has completed
*
* @related ADSR
*/
public void setParameters( float maxAmp, float attTime, float decTime, float susLvl, float relTime, float befAmp, float aftAmp)
{
maxAmplitude = maxAmp;
attackTime = attTime;
decayTime = decTime;
sustainLevel = susLvl;
releaseTime = relTime;
beforeAmplitude = befAmp;
afterAmplitude = aftAmp;
}
/**
* Specifies that the ADSR envelope should begin.
*
* @example Synthesis/ADSRExample
*
* @related ADSR
*/
public void noteOn()
{
timeFromOn = 0f;
isTurnedOn = true;
// ddf: reset these so that the envelope can be retriggered
timeFromOff = -1.f;
isTurnedOff = false;
}
/**
* Specifies that the ADSR envelope should start the release time.
*
* @example Synthesis/ADSRExample
*
* @related ADSR
*/
public void noteOff()
{
timeFromOff = 0f;
isTurnedOff = true;
}
/**
* Use this method to notify the ADSR that the sample rate has changed.
*/
@Override
protected void sampleRateChanged()
{
timeStepSize = 1/sampleRate();
}
/**
* Tell the ADSR that it should unpatch itself from the output after the release time.
*
* @param output
* AudioOutput: the output this should unpatch itself from
*
* @example Synthesis/ADSRExample
*
* @related ADSR
*/
public void unpatchAfterRelease( AudioOutput output )
{
unpatchAfterRelease = true;
this.output = output;
}
/**
* Tell the ADSR that it should unpatch itself from this UGen after the release time.
*
* @param ugen
* the UGen this should unpatch itself from
*
* @related ADSR
*/
public void unpatchAfterRelease( UGen ugen )
{
unpatchAfterRelease = true;
ugenOutput = ugen;
}
@Override
protected void uGenerate(float[] channels)
{
// before the envelope, just output the beforeAmplitude*audio
if (!isTurnedOn)
{
for(int i = 0; i < channelCount(); i++)
{
channels[i] = beforeAmplitude*audio.getLastValues()[i];
}
}
// after the envelope, just output the afterAmplitude*audio
else if (timeFromOff > releaseTime)
{
for(int i = 0; i < channelCount(); i++)
{
channels[i] = afterAmplitude*audio.getLastValues()[i];
}
if ( unpatchAfterRelease )
{
if ( output != null )
{
unpatch( output );
output = null;
}
if ( ugenOutput != null )
{
unpatch( ugenOutput );
ugenOutput = null;
}
unpatchAfterRelease = false;
}
}
// inside the envelope
else
{
if ((isTurnedOn) && (!isTurnedOff))
{
// ATTACK
if (timeFromOn <= attackTime)
{
// use time remaining until maxAmplitude to change amplitude
float timeRemain = (attackTime - timeFromOn);
amplitude += (maxAmplitude - amplitude)*timeStepSize/timeRemain;
}
// DECAY
else if ((timeFromOn > attackTime) && (timeFromOn <= (attackTime+decayTime)))
{
// use time remaining until sustain to change to sustain level
float timeRemain = (attackTime + decayTime - timeFromOn);
amplitude += (sustainLevel*maxAmplitude - amplitude)*timeStepSize/timeRemain;
}
// SUSTAIN
else if (timeFromOn > (attackTime+decayTime))
{
// hold the sustain level
amplitude = sustainLevel*maxAmplitude;
}
timeFromOn += timeStepSize;
}
// RELEASE
else //isTurnedOn and isTurnedOFF and timeFromOff < releaseTime
{
// use remaining time to get to afterAmplitude
float timeRemain = (releaseTime - timeFromOff);
amplitude += (afterAmplitude - amplitude)*timeStepSize/timeRemain;
timeFromOff += timeStepSize;
}
// finally multiply the input audio to generate the output
for(int i = 0; i < channelCount(); i++)
{
channels[i] = amplitude*audio.getLastValues()[i];
}
}
}
}