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

jm.audio.synth.Envelope 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;
import jm.music.data.Note;

/**
 * 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 worked 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 * @version 1.0, Sun Feb 25 18:42:47 2001 */ public class Envelope extends AudioObject implements JMC { //---------------------------------------------- // Attributes //---------------------------------------------- /** * points on the graph */ private EnvPoint[] graphPoints; /** * a calculated graph with all points filled in */ private float[] graphShape; /** * how far through the envelope shape we are */ private int position = 1; /** * is the a primary object? */ private boolean primary; /* Indicate waether of not to use values from the note */ private boolean useNotePoints = false; /* The index in the note's breakPoint array to use. * See the Note class for specified constant values. */ private int notePointIndex; //---------------------------------------------- // Constructors //---------------------------------------------- /** * An Envelope object can be used as a generator. This * is a method to call to do this. * * @param sampleRate the sampleRate for this AudioObject. * @param graphPoints the points to construct the envelope from */ public Envelope(Instrument inst, int sampleRate, int channels, EnvPoint[] graphPoints) { super(inst, sampleRate, "[Envelope]"); this.channels = channels; this.graphPoints = graphPoints; this.primary = true; } /** * An Envelope object can be used as a generator. This * is a method to call to do this. * * @param sampleRate the sampleRate for this AudioObject. * @param @param breakPoints the data to construct the envelope from */ public Envelope(Instrument inst, int sampleRate, int channels, double[] breakPoints) { super(inst, sampleRate, "[Envelope]"); this.channels = channels; breakPointsToGraphPoints(breakPoints); this.primary = true; } /** * An Envelope object can be used as a generator. This * is a method to call to do this. * * @param sampleRate the sampleRate for this AudioObject. * @param notePointIndex The note's break point envelope values to use. */ public Envelope(Instrument inst, int sampleRate, int channels, int notePointIndex) { super(inst, sampleRate, "[Envelope]"); this.channels = channels; this.useNotePoints = true; // use note's values this.notePointIndex = notePointIndex; this.primary = true; } /** * 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, it uses the note's amp env values. * * @param ao the single AudioObject to use as input. */ public Envelope(AudioObject ao) { super(ao, "[Envelope]"); this.useNotePoints = true; // use note's values this.notePointIndex = Note.AMP_ENV; this.primary = false; } /** * 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, it uses the note's amp env values. * * @param ao the single AudioObject to use as input. * @param notePointindex The index in the note's breakPoint array to use. */ public Envelope(AudioObject ao, int notePointIndex) { super(ao, "[Envelope]"); this.useNotePoints = true; // use note's values this.notePointIndex = notePointIndex; this.primary = false; } /** * 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 graphPoints the points to construct the envelope from * @param ao the single AudioObject to use as input. */ public Envelope(AudioObject ao, EnvPoint[] graphPoints) { super(ao, "[Envelope]"); this.graphPoints = graphPoints; this.primary = false; } /** * 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 breakPoints The data to construct the envelope from. * @param ao the single AudioObject to use as input. */ public Envelope(AudioObject ao, double[] breakPoints) { super(ao, "[Envelope]"); breakPointsToGraphPoints(breakPoints); this.primary = false; } /** * Update the envelope shape immediately. * * @param breakPoints The data to construct the envelope from. */ public void setBreakPoints(double[] breakPoints) { breakPointsToGraphPoints(breakPoints); } //---------------------------------------------- // Protected Methods //---------------------------------------------- /** * This method used to convert double value arrays into * a graphPoint array. */ private void breakPointsToGraphPoints(double[] breakPoints) { this.graphPoints = new EnvPoint[breakPoints.length / 2]; int counter = 0; for (int i = 0; i < (breakPoints.length / 2); i++) { graphPoints[i] = new EnvPoint((float) breakPoints[counter], (float) breakPoints[counter + 1]); counter += 2; } } /** * 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 positional value * will be passed on unchanged. * * @param input input data */ public int work(float[] buffer) throws AOException { // pass on data unchanged after the end of the envelope if (this.finished == true && this.inst.iterations <= 0) return buffer.length; // process data if (primary) { //System.out.println("in primary. Graph size = " + graphShape.length + " Postion = " + position); int returned = buffer.length; int chancount = 1; for (int i = 0; i < returned; i++) { try { buffer[i] = graphShape[this.position]; //System.out.println("buffer = " + buffer[i]); } catch (ArrayIndexOutOfBoundsException aob) { buffer[i] = 0.0f; } if (chancount == channels) { chancount = 1; this.position++; //System.out.println("Position incremeted to" + position); } else { chancount++; } } return returned; } else { //System.out.println("in NOT primary"); int returned = this.previous[0].nextWork(buffer); int chancount = 1; for (int i = 0; i < returned; i++) { try { buffer[i] = buffer[i] * graphShape[this.position]; } catch (ArrayIndexOutOfBoundsException aob) { //System.out.println("POS: "+this.position+" LENGTH: "+graphShape.length); //System.exit(1); buffer[i] = 0.0f; } if (chancount == channels) { chancount = 1; this.position++; } else { chancount++; } } return returned; } } //---------------------------------------------- // Private Methods //---------------------------------------------- /** * Calculates the sampleData for this Envelope */ public void build() { if (this.useNotePoints) breakPointsToGraphPoints(this.currentNote.getBreakPoints(notePointIndex)); //System.out.println("samps = " + numOfSamples + " Graph points = " + graphPoints.length); graphShape = new float[numOfSamples]; // flat envelope for VERY short notes if (numOfSamples <= graphPoints.length * 4) { for (int i = 0; i < graphShape.length; i++) { graphShape[i] = 1.0f; } return; } // otherwise //Adjust the Envelope to suit the length of the waveform if (this.graphPoints[0].x != -1.0) { for (int i = 0; i < this.graphPoints.length; i++) { this.graphPoints[i].X = ((int) ((float) (numOfSamples) * this.graphPoints[i].x)); this.graphPoints[i].X -= 1; } } //Make the new wave shape //Calculate linear lines between EnvPoints //graphShape = new float[(numOfSamples)]; int j = 0; for (int i = 0; i < (this.graphPoints.length) - 1; i++) { float gradient = (this.graphPoints[i].y - this.graphPoints[i + 1].y) / (this.graphPoints[i].X - this.graphPoints[i + 1].X); float yintercept = this.graphPoints[i + 1].y - (gradient * this.graphPoints[i + 1].X); for (; ; ) { //This could be a problem as I don't think the last sample is //set properly. //moving it to other side of graphShape setting. //if(j == this.graphPoints[i+1].X) break; this.graphShape[j] = (gradient * (float) j) + yintercept; //System.out.println(this.graphShape[j] + " " + j); if (j >= this.graphPoints[i + 1].X) break; j++; } } this.position = 0; //this is set to -1 to start channels at 0 //System.out.println("START OF POS: "+this.position); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy