/** * Copyright (c) 2005 - Bob Lang (http://www.cems.uwe.ac.uk/~lrlang/) * * 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.contrib.boblang; /** Implements a basic Attack/decay/sustain/release envelope shaper. @author Bob Lang @version 25 Apr 2004 */ public class BezierEnvelopeShaper { // Constants imported from Bezier Params private static final int MAX_AMPLITUDE = BezierParamsV3_5.MAX_AMPLITUDE, MAX_PITCH = BezierParamsV3_5.MAX_PITCH, HIGH_PITCH = BezierParamsV3_5.HIGH_PITCH, LOW_PITCH = BezierParamsV3_5.LOW_PITCH; // Extra time after release has finished private static final int RELEASE_EXTRA = 48000; // Needed to give a full buffer of zeroes // Envelope shaping attributes private int attackTime, // Time in mS for attack after note start decayTime, // Time in mS for decay following attack releaseTime, // Time in mS for decay after release attackIndex, // Point of maximum attack decayIndex; // Point of final decay private double sustainLevel, // Sustain level as fraction of max amplitude attackLength, // Number of samples for attack period decayLength, // Number of samples for decay period releaseLength; // Number of samples for release period /** Constructor which converts envelope data in the Ant Params object into their equivalent attributes. In practice, it calls the public method reshapeEnvelope () to allow dynamic envelope changes */ public BezierEnvelopeShaper (BezierParams params) { reshapeEnvelope (params); } // BezierEnvelopeShaper () /** Convert the envelope data in the parameter object into equivalent attributes. This method may be used to dynamically change the envelope, perhaps as a note is playing. */ public final void reshapeEnvelope (BezierParams params) { // Copy directly equivalent parameters attackTime = params.getEnvAttackTime(); decayTime = params.getEnvDecayTime(); releaseTime = params.getEnvReleaseTime(); sustainLevel = (double) params.getEnvSusLevel() / 100.0; // Calculate ending position for the attack and sustain phases attackIndex = attackTime*params.getSampleRate()/1000; decayIndex = (attackTime + decayTime)*params.getSampleRate()/1000; // Calculate number of samples for decay and release periods attackLength = (double) attackIndex; decayLength = (double) (decayIndex - attackIndex); releaseLength = (double) releaseTime*params.getSampleRate()/1000; } // reshapeEnvelope () /** Get the multiplication factor for the current sample based on its position in the sound wave. The position is obtained from index and this version of the method assumes that the note has not been released. <p>If window is non-zero, then the factor is the average value for a window of samples starting at index and of the specified width */ public final double getFactor (int index) { // Result to be returned double dblIndex, factor; // Modify the index to take the window into account dblIndex = (double) (index); // Is this in the attack phase? if (index < attackIndex) { // Calculate the attack time factor factor = dblIndex/attackLength; if (factor > 1.0) { factor = 1.0; } //System.out.println ("A" + factor); } // if attack phase // Is this the decay phase? else if (index < decayIndex) { // Calculate the decay time factor double decayOffset = (double) (decayIndex - index); double attackOffset = (double) (index - attackIndex); factor = (decayOffset + sustainLevel*attackOffset)/decayLength; //System.out.println ("D" + factor); } // if decay phase // Assume it's in the sustain phase else { factor = sustainLevel; //System.out.println ("S" + factor); } // sustain phase // Return the final result return factor; } // getFactor () /** Get the multiplication factor for the current sample based on its position in the sound wave but assuming it was released at the specified time. */ public final double getRelFactor (int index, int releaseIndex) { double releaseFactor; double dblReleaseIndex = (double) (index - releaseIndex); // Calculate the further factor assuming a linear release releaseFactor = sustainLevel * (1.0 - dblReleaseIndex/releaseLength); if (releaseFactor < 0.01) { releaseFactor = 0.0; } // if //System.out.println ("R" + releaseFactor); return releaseFactor; } // getFactorAfterRelease () /** Return true if the note should have finished by now */ public final boolean isNoteFinished (int index, int releaseIndex) { return (getRelFactor(index,releaseIndex)<0.01) ? true : false; // The 2 is a fudge factor so that all zeroes are written to the buffer //return index-releaseIndex > releaseLength; } // isNoteFinished () } // EnvelopeShaper