Package de.maramuse.soundcomp.generator

Source Code of de.maramuse.soundcomp.generator.Envelope

package de.maramuse.soundcomp.generator;

/*
* Copyright 2010 Jan Schmidt-Reinisch
*
* SoundComp - a sound processing library
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; in version 2.1
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
/*
* todo improvement for all exponential curves that are determined by 2 points:
*
* y=exp(bx+c)
*
* b=(x1-x0)*ln(y0/y1)
* c=ln(y0)-bx0
*
*
*
* where (x1-x0) is the time to reach end point, y0 is start value
* and y1 is end value.
* x0 can be "offset" to 0 (this is the starting time of a new state), so:
* so:
*  b=desttime*ln(nowval-destval)
*  c=ln(nowval)
* this applies to attack, sustain, release...
* There should NEVER be a strong "jump" in the output value as this
* always leads to audible glitches - test this under all conditions
* (start and end triggers in all possible phases).
*/
/**
* An envelope is used for the time dependent volume of a single note
* It is a logarithmic 3-ramp generator
*
* @author jssr67
*
*/

import de.maramuse.soundcomp.process.NamedSource;
import de.maramuse.soundcomp.process.ParameterMap;
import de.maramuse.soundcomp.process.ProcessElement;
import de.maramuse.soundcomp.process.SourceStore;
import de.maramuse.soundcomp.process.StandardParameters;
import de.maramuse.soundcomp.process.Stateful;
import de.maramuse.soundcomp.process.TypeMismatchException;
import de.maramuse.soundcomp.process.UnknownConnectionException;
import de.maramuse.soundcomp.process.ValueType;
import de.maramuse.soundcomp.process.StandardParameters.Parameter;
import de.maramuse.soundcomp.util.GlobalParameters;
import de.maramuse.soundcomp.util.NativeObjects;
import de.maramuse.soundcomp.util.ReadOnlyMap;
import de.maramuse.soundcomp.util.ReadOnlyMapImpl;

public class Envelope implements ProcessElement, Stateful {
  public long nativeSpace=-1;
  private double sampleRate=GlobalParameters.get().getSampleRate();
  public Envelope() {
  NativeObjects.registerNativeObject(this);
  }

  Envelope(boolean s) {
  }

  private static final int WAIT=0;
  private static final int ATTACK=1;
  private static final int DECAY=2;
  private static final int SUSTAIN=3;
  private static final int RELEASE=4;

  private static final double timeConstant=-12; // ln(2^-17), we want -102dB to be the smallest ampl
  private static final double minVal=1d/131072d;

  private static final ReadOnlyMapImpl<Integer, ValueType> destTypeMap=new ReadOnlyMapImpl<Integer, ValueType>();
  static{
  destTypeMap.put(StandardParameters.A.i, ValueType.STREAM);
  destTypeMap.put(StandardParameters.D.i, ValueType.STREAM);
  destTypeMap.put(StandardParameters.S.i, ValueType.STREAM);
  destTypeMap.put(StandardParameters.R.i, ValueType.STREAM);
  destTypeMap.put(StandardParameters.SYNC.i, ValueType.STREAM);
  }
  protected static final ReadOnlyMapImpl<Integer, ValueType> sourceTypeMap=new ReadOnlyMapImpl<Integer, ValueType>();
  static{
  sourceTypeMap.put(StandardParameters.OUT.i, ValueType.STREAM);
  }
  private static ParameterMap inputsMap=new ParameterMap();
  private static ParameterMap outputsMap=new ParameterMap();
  static{
  inputsMap.put(StandardParameters.A);
  inputsMap.put(StandardParameters.D);
  inputsMap.put(StandardParameters.S);
  inputsMap.put(StandardParameters.R);
  inputsMap.put(StandardParameters.SYNC);
  outputsMap.put(StandardParameters.OUT);
  }

  private int phase=WAIT;
  private double countSincePhaseChange;
  private double attack=0;
  private double decay=0;
  private double sustain=1d;
  private double release=0d;
  private double restRelease=0d;
  private double decayStart=0d;
  private double releaseStart=0d;
  private double val=0d, _val=0d;
  private boolean trig=false;
  private boolean trigold=false;
  private String abstractName, instanceName;

  private ReadOnlyMapImpl<Integer, SourceStore> params=new ReadOnlyMapImpl<Integer, SourceStore>();

  @Override
  public ReadOnlyMap<Integer, ValueType> getDestinationTypes() {
  return destTypeMap;
  }

  @Override
  public void setSource(int connection, NamedSource source,
            int sourceIndex) throws UnknownConnectionException,
      TypeMismatchException {
  params.put(connection, new SourceStore(source, sourceIndex));
  }

  @Override
  public ReadOnlyMap<Integer, ValueType> getSourceTypes() {
  return sourceTypeMap;
  }

  @Override
  public double getValue(int index) {
  return val;
  }

  @Override
  public void advanceState() {
  SourceStore s=params.get(StandardParameters.A.i);
  if(phase!=ATTACK){
    if(s!=null)
    attack=s.getValue();
    if(attack<0)
    attack=0;
  }
  if(phase!=DECAY){
    s=params.get(StandardParameters.D.i);
    if(s!=null)
    decay=s.getValue();
    if(decay<0)
    decay=0;
  }
  if(phase!=SUSTAIN){
    s=params.get(StandardParameters.S.i);
    if(s!=null)
    sustain=s.getValue();
    if(sustain<0)
    sustain=0;
    if(sustain>1)
    sustain=1;
  }
  if(phase!=RELEASE){
    s=params.get(StandardParameters.R.i);
    if(s!=null)
    release=s.getValue();
    if(release<0)
    release=0;
  }
  s=params.get(StandardParameters.SYNC.i);
  trigold=trig;
  trig=s!=null&&s.getValue()>0;
  if(trig&&!trigold){
    // attack starts
    restRelease=_val;
    _val=minVal+restRelease;// we do not expect more than 16 bits resolution in the end.
                // starting with even one power below should be safe
                // ~ 2^timeConstant
    phase=ATTACK;
    countSincePhaseChange=0;
  }else if(trigold&&!trig){
    // release starts
    if(phase==ATTACK||phase==DECAY||phase==SUSTAIN){
    phase=RELEASE;
    countSincePhaseChange=0d;
    releaseStart=val;
    }
  }else{
    // no change by external event
  }
  switch(phase){
    case ATTACK:
    // double restTime=attack-countSincePhaseChange;
    if(attack>0){
      _val=Math.exp(timeConstant*(1-countSincePhaseChange/attack))+restRelease
        *Math.exp(timeConstant*countSincePhaseChange/attack);
      if(_val>=1){
      _val=decayStart=1;
      countSincePhaseChange=0d;
      phase=DECAY;
      }
    }else
      _val=1d;
    break;
    case RELEASE:
    // restTime=release-countSincePhaseChange;
    if(release>0){
      _val=releaseStart*Math.exp(timeConstant*countSincePhaseChange/release);
      if(_val<minVal){
      countSincePhaseChange=0d;
      phase=WAIT;
      _val=0d;
      }
    }else
      _val=0d;
    break;
    case SUSTAIN:
    _val=sustain;
    break;
    case DECAY:
    // restTime=decay-countSincePhaseChange;
    if(decay>0){
      _val=decayStart*Math.exp(timeConstant*countSincePhaseChange/decay);
      if(_val<=sustain){
      _val=sustain;
      phase=SUSTAIN;
      countSincePhaseChange=0d;
      }
      if(_val<minVal){
      countSincePhaseChange=0d;
      phase=WAIT;
      _val=0d;
      }
    }else
      _val=0d;
    break;
  }
  countSincePhaseChange+=(1/sampleRate);
  }

  @Override
  public void advanceOutput() {
  val=_val;
  }

  @Override
  public long getNativeSpace() {
  return nativeSpace;
  }

  @Override
  public String getAbstractName() {
  return abstractName;
  }

  @Override
  public String getInstanceName() {
  return instanceName;
  }

  @Override
  public void setAbstractName(String abstractName) {
  this.abstractName=abstractName;
  }

  @Override
  public void setInstanceName(String instanceName) {
  this.instanceName=instanceName;
  }

  /**
   * @see de.maramuse.soundcomp.process.ProcessElement#clone()
   */
  @Override
  public Envelope clone() {
  Envelope c=new Envelope();
  c.abstractName=abstractName;
  return c;
  }

  @Override
  public ReadOnlyMap<Integer, SourceStore> getSourceMap() {
  return params;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.ProcessElement#outputsByName()
   */
  @Override
  public ReadOnlyMap<String, Parameter> outputsByName() {
  return outputsMap;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.ProcessElement#inputsByName()
   */
  @Override
  public ReadOnlyMap<String, Parameter> inputsByName() {
  return inputsMap;
  }
}
TOP

Related Classes of de.maramuse.soundcomp.generator.Envelope

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.