/*
* Created on Feb 26, 2005
*
* Copyright (c) 2005 Peter Johan Salomonsen (http://www.petersalomonsen.com)
*
* http://www.frinika.com
*
* This file is part of Frinika.
*
* Frinika is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* Frinika is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Frinika; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.frinika.synth.envelope;
import com.frinika.audio.Decibel;
/**
*
* @author Peter Johan Salomonsen
*
*/
public final class VolumeEnvelope {
static final int ENVELOPESTATE_DELAY = 0;
static final int ENVELOPESTATE_ATTACK = 1;
static final int ENVELOPESTATE_HOLD = 2;
static final int ENVELOPESTATE_DECAY = 3;
static final int ENVELOPESTATE_SUSTAIN = 4;
static final int ENVELOPESTATE_RELEASE = 5;
float min;
float max;
float maxdB;
int sampleRate;
int envelopeState = ENVELOPESTATE_DELAY;
int delaySampleCount;
int holdSampleCount;
float attackDeltaLevelPerSample;
float decayAttenuationPerSample;
float releaseAttenuationPerSample;
float attenuation;
float sustainAttenuation;
boolean isReleased = false;
public VolumeEnvelope(int sampleRate, float mindB, float maxdB)
{
this.min = Decibel.getAmplitudeRatio(mindB);
this.max = Decibel.getAmplitudeRatio(maxdB);
this.maxdB = maxdB;
this.attenuation = min;
this.sampleRate = sampleRate;
this.setDelay(-32768);
this.setAttack(0);
this.setHold(-32768);
this.setDecay(0);
this.setSustain(400);
this.setRelease(0);
}
public final void setDelay(int delayTimeCents)
{
delaySampleCount = timeCentsToSampleCount(delayTimeCents);
}
public final void setAttack(int attackTimeCents)
{
attackDeltaLevelPerSample = 1f / timeCentsToSampleCount(attackTimeCents);
}
public final void setHold(int holdTimeCents)
{
holdSampleCount = timeCentsToSampleCount(holdTimeCents);
}
public final void setDecay(int decayTimeCents)
{
decayAttenuationPerSample = getAttenuationPerSample(-100f,decayTimeCents);
}
public final void setSustain(int centiBelsDecrease)
{
float dB = (maxdB - (centiBelsDecrease / 10f));
if(dB<-100)
dB=-100;
sustainAttenuation = Decibel.getAmplitudeRatio(dB); // centiBels
}
public final void setRelease(int releaseTimeCents)
{
releaseAttenuationPerSample = getAttenuationPerSample(-100f,releaseTimeCents);
}
public final void release()
{
envelopeState = ENVELOPESTATE_RELEASE;
}
public final boolean isReleased()
{
return isReleased;
}
public final float getAttenuation()
{
switch(envelopeState)
{
case ENVELOPESTATE_DELAY:
if(--delaySampleCount<0)
envelopeState = ENVELOPESTATE_ATTACK;
else
break;
case ENVELOPESTATE_ATTACK:
if(attenuation>=max)
envelopeState = ENVELOPESTATE_HOLD;
else
{
attenuation+=attackDeltaLevelPerSample;
if(attenuation>=max)
attenuation=max;
break;
}
case ENVELOPESTATE_HOLD:
if(--holdSampleCount<0)
envelopeState = ENVELOPESTATE_DECAY;
else
break;
case ENVELOPESTATE_DECAY:
if(attenuation<=sustainAttenuation)
envelopeState = ENVELOPESTATE_SUSTAIN;
else
attenuation*=decayAttenuationPerSample;
break;
case ENVELOPESTATE_RELEASE:
if(attenuation<=min)
isReleased = true;
else
attenuation*=releaseAttenuationPerSample;
}
return attenuation;
}
final int timeCentsToSampleCount(int timeCents)
{
return(int)( (sampleRate*Math.pow(2.0,timeCents/1200.0)) +1);
}
public final float getAttenuationPerSample(float deciBels, int timeCents)
{
return((float)Math.pow(10.0,(deciBels / ( 20 * timeCentsToSampleCount(timeCents)))));
}
public static void main(String[] args)
{
VolumeEnvelope env = new VolumeEnvelope(10,-100f,0);
env.setAttack(0);
env.setSustain(500);
System.out.println(env.timeCentsToSampleCount(7973));
System.out.println(Decibel.getAmplitudeRatio(-40));
for(int n=0;n<50;n++)
System.out.println(n+" "+env.getAttenuation()+" "+env.envelopeState);
env.release();
for(int n=0;n<50;n++)
System.out.println(n+" "+env.getAttenuation()+" "+env.envelopeState);
}
}