All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jm.audio.synth.ADSR Maven / Gradle / Ivy

The newest version!
/*



Copyright (C) 2000 Andrew Sorensen & Andrew Brown

This program 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 any
later version.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

package jm.audio.synth;

import jm.JMC;
import jm.audio.AOException;
import jm.audio.AudioObject;
import jm.audio.Instrument;

/**
 * Envelope which can be set with an arbitrary number of points
 * the envelope is constructed with linear lines between each
 * specifed point.
 * The points excepted by this class are positioned as a percent
 * of the total length of the sound data being nextWorked on and
 * the envelope itself is constructed in the build() method.
* Envelope objects can be used as either Generator Audio Objects * (ie the first in the chain) or as processor Audio Objects (ie * in the centre or the chain) depending on the constructor used.
* As a generator the Envelope can be used to pass each envelope position * onto another Audio Object as input data.
* As a processor the Envelope object is used to change the Amplitude * of incoming samples to reflect the shape of the envelope.
* NOTE: The important distinction here is that when being used as * a processor object the envelopes only possible function is to * alter amplitude. But when used as a generator the Envelope can * be used to send data to any AudioObjects input. (the volume of a * volume object for example for doing crescendos on each note) * * @author Andrew Sorensen, Andrew Brown, Joel Joslin * @version 1.0, Sun Feb 25 18:42:47 2001 */ public class ADSR extends AudioObject implements JMC { //---------------------------------------------- // Attributes //---------------------------------------------- // variables for samples per section int maxAttackCount; int maxDecayCount; /** * points on the graph */ private EnvPoint[] graphPoints; /** * a calculated graph with all points filled in */ private float[] graphShape; /** * is the a primary object? */ private boolean primary; // ADSR values private int attack, decay, release; private double sustain; // the number of samples that takes into account the release time private int totalSamples; // keep track of the number of samples processed private int sampleCounter = 0; // keep track of the location along the envelope array private int position = 0; // keep the number of samples for attack, decay and release private double attackSamps, decaySamps, releaseSamps; // the previous rhythmValue private double prevRV = 0.0; //---------------------------------------------- // Constructors //---------------------------------------------- /** * An ADSR object can be used as a generator. This * is a method to call to do this. * * @param The instrumt to use - Usually put 'this' here. * @param sampleRate the sampleRate for this AudioObject. * @param channels The number of tracks for this file (1 = mono , 2 = stereo) * @param attack The number of milliseconds for the attack portion of the envelope * @param decay The number of milliseconds for the decay portion of the envelope * @param sustain The percentage lavel for the sustain portion of the envelope (0.0 - 1.0) * @param release The number of milliseconds for the release portion of the envelope */ public ADSR(Instrument inst, int sampleRate, int channels, int attack, int decay, double sustain, int release) { super(inst, sampleRate, "[ADSR]"); this.channels = channels; this.attack = attack; this.decay = decay; this.sustain = sustain; this.release = release; this.primary = true; this.finished = false; calcSamps(); } /** * This constructor takes a single AudioObject as input * and in this form becomes a processor object which * changes the amplitude of incoming samples based on * the envelope. * * @param The instrumt to use - Usually put 'this' here. * @param sampleRate the sampleRate for this AudioObject. * @param channels The number of tracks for this file (1 = mono , 2 = stereo) * @param attack The number of milliseconds for the attack portion of the envelope * @param decay The number of milliseconds for the decay portion of the envelope * @param sustain The percentage lavel for the sustain portion of the envelope (0.0 - 1.0) * @param release The number of milliseconds for the release portion of the envelope */ public ADSR(AudioObject ao, int attack, int decay, double sustain, int release) { super(ao, "[ADSR]"); // ADSR -> points this.attack = attack; this.decay = decay; this.sustain = sustain; this.release = release; this.primary = false; this.finished = false; } //---------------------------------------------- // Protected Methods //---------------------------------------------- /** * Alter the samples value so that it meets the * shape of the graph, then send the new sample * onto the next audio object.
* NOTE: if the nextWork method receives a value * of 1.0 the graphs current poitional value * will be passed on unchanged. * * @param input input data */ public int work(float[] buffer) throws AOException { // claculate the number of samples processed if (sampleCounter > totalSamples) { this.finished = true; } // process data if (primary) { int returned = buffer.length; //int chancount=1; for (int i = 0; i < returned; i += channels) { for (int j = 0; j < channels; j++) { try { buffer[i + j] = graphShape[this.position]; //System.out.println("buffer = " + buffer[i]); } catch (ArrayIndexOutOfBoundsException aob) { buffer[i + j] = 0.0f; } } this.position++; } sampleCounter += buffer.length; return returned; } else { //System.out.println("in NOT primary"); // int returned = this.previous[0].nextWork(buffer); for (int i = 0; i < buffer.length; i += channels) { for (int j = 0; j < channels; j++) { try { if (sampleCounter < maxAttackCount) //attack buffer[i + j] = buffer[i + j] * (float) (sampleCounter * 1.0 / maxAttackCount); else if (sampleCounter < maxDecayCount + maxAttackCount) //decay buffer[i + j] = buffer[i + j] * (float) (1.0 - (sampleCounter - maxAttackCount) * (1.0 - sustain) / maxDecayCount); else if (sampleCounter < this.numOfSamples) //sustain buffer[i + j] = buffer[i + j] * (float) sustain; else if (sampleCounter < totalSamples) // release buffer[i + j] = buffer[i + j] * (float) (sustain - (sampleCounter - numOfSamples) * sustain / releaseSamps); else buffer[i + j] = 0.0f; //buffer[i+j] = buffer[i+j] * graphShape[this.position]; } catch (ArrayIndexOutOfBoundsException aob) { buffer[i + j] = 0.0f; } } sampleCounter++; this.position++; } // /* int returned = this.previous[0].nextWork(buffer); int chancount=1; for(int i=0;i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy