Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: jm/audio/synth/WaveTable.java


1   /*
2   
3   <This Java Class is part of the jMusic API version 1.4, February 2003.>
4   
5   Copyright (C) 2000 Andrew Sorensen & Andrew Brown
6   
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or any
10  later version.
11  
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16  
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  
21  */
22  
23  package jm.audio.synth;
24  
25  import java.io.IOException;
26  import jm.audio.AOException;
27  import jm.audio.AudioObject;
28  import jm.audio.Instrument;
29  import jm.music.data.Note;
30  
31  /**
32   * Wavetable lookup creates an efficient means for resampling data
33   * into any frequency. It is particularly useful for holding simple
34   * wave information such as sinewaves and is often used as an
35   * oscillator.<br>
36   * This WaveTable implementation can accept either one or two inputs.  Two
37   * inputs expects that amplitude is the left input and frequency is the right
38   * input. One input allows the user to specify whether the input is for
39   * amplitude or frequency by setting the aoDestination variable((0)amplitude
40   * (1)frequency.<br>
41   * A WaveTable can use fixed variables for both amplitude and frequency.  The
42   * default is to use a default amplitude of 1.0 and a frequency based upon the
43   * value of the build methods Note.pitch(). These fixed variables are
44   * amp(amplitude) and frq(frequency) and both have set methods. NOTE (please do
45   * not add more constructors to set amp and frq but leave them as set
46   * methods).<br> 
47   * The frqRatio variable is used to produce a frequency which is a ratio
48   * against the current notesFrq (the build methods note.getPitch()) variable 
49   * setting.<br>
50   * It is common to use WaveTables as oscillators and even more common for these
51   * oscillators to based on simple wave forms.  Simple wave forms in jMusic can
52   * be retrieved using static method calls to the Oscillator class.<br>
53   * 
54   * @author Andrew Sorensen
55   * @version 1.0,Sun Feb 25 18:42:52  2001
56   */
57  public class WaveTable extends AudioObject{
58    //----------------------------------------------
59    // Attributes
60    //----------------------------------------------
61    /** this contains the wavetable data as samples */
62    private float[] waveTable;
63    /** how many samples to we skip while passing through the wavetable */
64    private float si;
65    /** what is the phase of the wavetable to start at */
66    private float phase;
67    /** If we have one audio object input is at amp(0) or freq(1) ? */
68    private int aoDestination;
69    /** Value to use as a fixed amplitude for the waveTable.*/
70    private float amp = (float) 1.0;
71    /** Value to use as a fixed frequency for the waveTable.*/
72    private float frq = (float)-1.0;
73    /** Frequency ratio allows an incoming note's pitch to be adjusted to a
74      * fixed ratio amount*/
75    private float frqRatio = (float)1.0;
76          /** constant for use with aoDestination */
77          public static final int AMPLITUDE = 0;
78          /** constant for use with aoDestination */
79          public static final int FREQUENCY = 1;
80          /** constant for use with channels */
81          public static final int MONO = 1;
82          /** constant for use with channels */
83          public static final int STEREO = 2;
84    //----------------------------------------------
85    // Constructors
86    //----------------------------------------------
87    /**
88     * This constructor sets the WaveTable to act as
89     * a processor object taking in two inputs. Input
90      * one is defined as amplitude and input two is
91      * defined as frequency. 
92     * @param ao AudioObject as input 
93     * @param waveTable the lookup table data
94     * @exception AOException thrown when two many inputs are attached
95     */
96    public WaveTable(AudioObject[] ao, float[] waveTable)throws AOException{
97      super(ao, "[WaveTable]");
98      if(ao.length > 2) throw new AOException(this.name,1);
99      this.waveTable = waveTable;
100   }
101 
102   /**
103     * This constructor sets the WaveTable to act as
104    * a processor object taking in one input. That
105    * input can be either amplitude(0) or frequency(1)
106    * and is defined by the aoDestination variable (int).
107          * @param ao the one input audio object
108    * @param wavetable the wave table data
109     * @param aoDestination Is this input amplitude(0) or frequency(1)
110    */
111   public WaveTable(AudioObject ao, float[] waveTable, int aoDestination){
112     super(ao, "[WaveTable]");
113     this.waveTable = waveTable;
114     this.aoDestination = aoDestination;
115   }
116 
117   /**
118     * This constructor sets the WaveTable to act as
119    * a generator object taking in one input. That
120    * input can be either amplitude(0) or frequency(1)
121    * and is defined by the aoDestination variable (int).
122    * @param inst the parent instrument (usually "this")
123    * @param wavetable the wave table data
124     * @param aoDestination Is this input amplitude(0) or frequency(1)
125    * @param val is used to set a fixed frequency or amplitude based on the
126    * result of aoDestination (aoDestination=1 for example will set a fixed frequency)
127    */
128   public WaveTable(Instrument inst, int sampleRate, float[] waveTable, 
129             int channels, int aoDestination, float val){
130     super(inst, sampleRate, "[WaveTable]");
131     this.waveTable = waveTable;
132                 this.channels = channels;
133     this.aoDestination = aoDestination;
134     if(aoDestination == 1){
135       this.frq=val;
136     }else{
137       this.amp=val;
138     }
139   }
140 
141   /**
142    * This constructor sets this wavetable up as a generator
143    * object meaning that it will pass sample information 
144    * down the chain based on its wave table data.<br>
145    * Set WaveTable with some initial values including 
146    * the sampling rate and the samples to use for this
147    * wave table
148    * @param inst the parent instrument (usually "this")
149    * @param sampleRate the sampling rate
150    * @param waveTable the wave table data
151    * @param channels the number of channels to use
152    */
153   public WaveTable(Instrument inst, int sampleRate, float[] waveTable,
154         int channels){
155     super(inst, sampleRate, "[WaveTable]");
156     this.waveTable = waveTable;
157     this.channels = channels;
158   }    
159 
160   //----------------------------------------------
161   // Methods
162   //----------------------------------------------
163   /**
164    * Moves through the WaveTable array (noramally forwards but sometimes
165    * backwards) by increments set by si (sample increment value).  This nextWork
166    * method can take one or two inputs which are either amplitude, frequency
167    * or both (a single input can be assigned to either frequency or amplitude
168    * by assigning the aoDestination value to either (0)Amp or (1)Frq in the
169    * appropriate constructor.  A WaveTable that takes two inputs expects the
170    * first input to be amplitude and the second input to be frequency. 
171    * @param buffer The sample buffer.
172    */
173   public int work(float[] buffer)throws AOException{
174     //because wavetable contains mono sample data we need to pass the same
175     //sample information to as many channels as are present.
176     int buffneed=buffer.length/channels;
177     int ret=0; //the number of samples to return 
178 
179     if(inputs==2){ //Amp and Freq
180       float[] ampbuf = new float[buffneed];
181       int returned = this.previous[0].nextWork(ampbuf);
182       float[] freqbuf = new float[returned];
183       if(returned != this.previous[1].work(freqbuf)){
184         throw new AOException(this.name,0);
185       }
186       for(int i=0;ret<buffer.length;i++){
187         setSI((int)freqbuf[i]);
188         if(phase < 0){
189           phase = this.waveTable.length+phase;
190         } // amplitude values assumed to be between -1 and 1
191         float sample = waveTable[(int)phase]*(this.amp * ampbuf[i]);
192         this.phase += si;
193         if(phase >= this.waveTable.length){ 
194           phase -= this.waveTable.length;
195         }
196         for(int j=0;j<channels;j++){ 
197           buffer[ret++] = sample;
198         }
199       }
200     }else if(inputs==1 && aoDestination==0){ //Amp only
201       float[] ampbuf = new float[buffneed];
202       int returned = this.previous[0].nextWork(ampbuf);
203       for(int i=0;ret<buffer.length;i++){
204         float sample = waveTable[(int)phase]*(this.amp * ampbuf[i]);
205         this.phase += si;
206         if(phase >= this.waveTable.length){ 
207           phase -= this.waveTable.length;
208         }
209         for(int j=0;j<channels;j++){ 
210           buffer[ret++] = sample;
211         }
212       }
213     }else if(inputs==1 && aoDestination==1){ //Frq only
214       float[] frqbuf = new float[buffneed];
215       int returned = this.previous[0].work(frqbuf);
216       for(int i=0;i<buffneed;i++){
217         setSI((int)frqbuf[i]);
218         if(phase < 0){
219           phase = this.waveTable.length+phase;
220         }
221         float sample = waveTable[(int)phase]*this.amp;
222         this.phase += si;
223         if(phase >= this.waveTable.length){
224           phase -= this.waveTable.length;
225         }
226         for(int j=0;j<channels;j++){
227           buffer[ret++] = sample;
228         }
229       }
230     }else{ //no inputs
231       for(;ret<buffer.length;){
232         float sample = waveTable[(int)phase]*this.amp;      
233         this.phase += si;
234         if(phase >= this.waveTable.length){ 
235           phase -= this.waveTable.length;
236         }
237         for(int j=0;j<channels;j++){ 
238           try{
239             buffer[ret++] = sample;
240           }catch(ArrayIndexOutOfBoundsException e){
241             //This can happen if a non mono signal chain wants
242             //to access the wavetable as a mono signal
243             //Ignore and skip over
244             //
245             //We do need to remove one back off ret though to return
246             //the right number of samples to return
247             ret--;
248           }
249         }
250       }
251     }
252     return ret; 
253   }
254 
255   /**
256    */
257   public void build(){
258     this.numOfSamples = numOfSamples;
259     float notesFrq = (float)FRQ[currentNote.getPitch()]*frqRatio;
260     if(this.frq<(float)0.0){
261       this.setSI(notesFrq);   
262     }else{
263       this.setSI(this.frq);
264     }  
265   }
266 
267   /**
268    * Set the fixed amp of this wavetable
269    * @param amp Fixed value amplitude
270    */
271   public void setAmp(float amp){
272     this.amp = amp;
273   }
274 
275   /**
276    * Set the fixed Frequecy of this wavetable
277    * @param frq Fixed value frequency
278    */
279   public void setFrq(float frq){
280     this.frq = frq;
281   }
282 
283   /**
284    * Sets the frequency ratio to alter a notes pitch by
285    * @param frqRatio Fixed ratio value to change frequency by
286    */
287   public void setFrqRatio(float frqRatio){
288     this.frqRatio = frqRatio;
289   }
290 
291   //------------------------------------------
292   // Protected Methods
293   //------------------------------------------
294   /**
295    * Returns the sampling increment which is used
296    * to nextWork out how many samples in the wavetable
297    * skip on each pass.
298    * @param frequency the frequency used to find si
299    */
300   protected void setSI(float frequency){
301       this.si = (int)((frequency / (float)this.sampleRate) * 
302              (float)this.waveTable.length);
303   }
304 }