Package jm.audio.synth

Source Code of jm.audio.synth.Oscillator

/*

<This Java Class is part of the jMusic API version 1.5, March 2004.>

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 java.io.IOException;
import jm.audio.AOException;
import jm.audio.AudioObject;
import jm.audio.Instrument;
import jm.music.data.Note;

/**
* The Oscillator class can generate steady tones with
* various wave shapes, inlcuding sine, cosine, triangle, sawtooth,
* pulse wave, square, and more.
* The Oscillator class can be used as a primary object at the start of
* an audio chain, or as a object within the chain who's frequency or
* amplitude is modulated by an earlier object.
* @author Andrew Sorensen and Andrew Brown
* @version 1.0,Sun Feb 25 18:42:52  2001
*/
public class Oscillator extends AudioObject{
  //----------------------------------------------
  // Attributes
  //----------------------------------------------
  /** The constant that represents the sine wave form */
  public static final int SINE_WAVE = 0;
  /** The constant that represents the cosine wave form */
  public static final int COSINE_WAVE = 1;
  /** The constant that represents the triangle wave form */
  public static final int TRIANGLE_WAVE = 2;
  /** The constant that represents the square wave form */
  public static final int SQUARE_WAVE = 3;
  /** The constant that represents the sawtooth wave form */
  public static final int SAWTOOTH_WAVE = 4;
  /** The constant that represents the inverse sawtooth wave form */
  public static final int SAWDOWN_WAVE = 5;
  /** The constant that represents the exponential sawtooth wave form */
  public static final int SABERSAW_WAVE = 6;
  /** The constant that represents the sine wave form.
  * The pulse width can be set using setPulseWidth() method.
  */
  public static final int PULSE_WAVE = 7;   
  /* modulation sources */
  /** Use the modulation source to change the amplitude of this oscillator */
  public static final int AMPLITUDE = 0;
  /** Use the modulation source to change the frequency of this oscillator */
  public static final int FREQUENCY = 1;
  /** how many samples to we skip while passing through the Oscillator */
  private float si;
  /** what is the phase of the Oscillator to start at */
  private float phase;
  /** If we have one input is at amp(0) or freq(1) ? */
  private int choice;
  /** Value to use as a fixed amplitude for the Oscillator.*/
  private float amp = (float) 1.0;
  /** Value to use as a fixed frequency for the Oscillator.*/
  private float frq = (float)-1.0;
  /** Frequency ratio allows an incoming note's pitch to be adjusted to a
  * fixed ratio amount*/
  private float frqRatio = (float)1.0;
  /** which waveform to use */
  private int waveType = SINE_WAVE;
  /** The width of the positive part of the pulse wave */
  private double pulseWidth = 0.15;

  //----------------------------------------------
  // Constructors
  //----------------------------------------------
  /**
  * This constructor sets the Oscillator to act as
  * a processor object taking in two inputs. Input
  * one is defined as amplitude and input two is
  * defined as frequency.
  * @param two AudioObjecs as input
  * @exception AOException thrown when two many inputs are attached
  */
  public Oscillator(AudioObject[] ao)throws AOException{
    super(ao, "[Oscillator]");
    if(ao.length > 2) throw new AOException(this.name,1);
  }

  /**
  * This constructor sets the Oscillator to act as
  * a processor object taking in one input. That
  * input can be either amplitude(0) or frequency(1)
  * and is defined by the choice variable (int).
  * @param ao the one input audio object
  * @param waveType the type of timbre to generate
  * @param choice Is this input amplitude(0) or frequency(1)
  */
  public Oscillator(AudioObject ao, int waveType, int choice){
    super(ao, "[Oscillator]");
    this.waveType = waveType;
    this.choice = choice;
  }

  /**
  * This constructor sets the Oscillator to act as
  * a processor object taking in one input. That
  * input can be either amplitude(0) or frequency(1)
  * and is defined by the choice variable (int).
  * @param ao the one input audio object
  * @param waveType the type of timbre to generate
  * @param choice Is this input amplitude(0) or frequency(1)
  * @param val is used to set a fixed frequency or amplitude based
        *     on the result of choice
  *     (choice=0 for example will set a fixed frequency)
  */
  public Oscillator(AudioObject ao, int waveType, int choice, double val){
    super(ao, "[Oscillator]");
        this.waveType = waveType;
    this.choice = choice;
    if(choice == 1){
      this.frq= (float)val;
    }else{
      this.amp= (float)val;
    }
  }
 
  /**
   * This constructor sets this Oscillator up as a generator
   * using default parameters
   * @param Instrument the instance this is associated with 'this'
   */
  public Oscillator(Instrument inst){
    this(inst, SINE_WAVE);
  }
 
  /**
   * This constructor sets this Oscillator up as a generator
   * specifying the type of waveform to use
   * @param Instrument the instance this is associated with 'this'
   * @param waveType an integer or constant sepcifying the noise type
   */
  public Oscillator(Instrument inst, int waveType){
    this(inst, waveType, 44100);
  }
 
  /**
   * This constructor sets this Oscillator up as a generator
   * specifying the type of waveform and sample rate to use
   * @param Instrument the instance this is associated with 'this'
   * @param waveType an integer or constant sepcifying the noise type
   * @param sampleRate an int that sets the sample rate in samples per second
   */
  public Oscillator(Instrument inst, int waveType, int sampleRate){
    this(inst, waveType, sampleRate, 1);
  }
 
  /**
   * This constructor sets this Oscillator up as a generator
   * specifying the type of waveform and sample rate to use
   * @param Instrument the instance this is associated with 'this'
   * @param waveType an integer or constant sepcifying the noise type
   * @param sampleRate an int that sets the sample rate in samples per second
   * @param cahannels 1 for mono 2 for stereo etc.
   */
  public Oscillator(Instrument inst, int waveType, int sampleRate, int channels){
    super(inst, sampleRate, "[Oscillator]");
        this.waveType = waveType;
    this.channels = channels;
  }   
   
  /**
  * This constructor sets this Oscillator up as a generator
  * specifying the type of wavetable and ferquency
  * @param Instrument the instance this is associated with 'this'
  * @param waveType an integer or constant sepcifying the noise type
  * @param sampleRate an int that sets the sample rate in samples per second
  * @param cahannels 1 for mono 2 for stereo etc.
  * @param fixedModChoice Is this input amplitude(0) or frequency(1)
  * @param freqVal is used to set a fixed frequency or amplitude based on the
  * result of choice (choice=0 for example will set a fixed frequency)
  */
  public Oscillator(Instrument inst, int waveType, int sampleRate, int channels, int fixedModChoice, double freqVal){
    super(inst, sampleRate, "[Oscillator]");
    this.waveType = waveType;
    this.channels = channels;
    this.choice = fixedModChoice;
    if(choice == 1){
      this.frq= (float)freqVal;
    }else{
      this.amp= (float)freqVal;
    }             
  } 

  //----------------------------------------------
  // Methods
  //----------------------------------------------
  /**
   * Moves through the Oscillator array (noramally forwards but sometimes
   * backwards) by increments set by si (sample increment value).  This nextWork
   * method can take one or two inputs which are either amplitude, frequency
   * or both (a single input can be assigned to either frequency or amplitude
   * by assigning the choice value to either (0)Amp or (1)Frq in the
   * appropriate constructor.  A Oscillator that takes two inputs expects the
   * first input to be amplitude and the second input to be frequency.
   * @param buffer The sample buffer.
   */
  public int work(float[] buffer)throws AOException{
    //because Oscillator contains mono sample data we need to pass the same
    //sample information to as many channels as are present.
    int buffneed=buffer.length/channels;
    int ret=0; //the number of samples to return
    if(inputs==2){ //Amp and Freq
      float[] ampbuf = new float[buffneed];
      int returned = this.previous[0].nextWork(ampbuf);
      float[] freqbuf = new float[returned];
      if(returned != this.previous[1].work(freqbuf)){
        throw new AOException(this.name,0);
      }
      for(int i=0;ret<buffer.length;i++){
        setSI(freqbuf[i] * frqRatio); // FM
        float sample = getWaveSample()* this.amp * ampbuf[i]; // AM
        for(int j=0;j<channels;j++){
          buffer[ret++] = sample;
        }
      }
    }else if(inputs==1 && choice==AMPLITUDE){ //Amp only
      float[] ampbuf = new float[buffneed];
      int returned = this.previous[0].nextWork(ampbuf);
      for(int i=0;ret<buffer.length;i++){
        float sample = getWaveSample()*this.amp * ampbuf[i]
        for(int j=0;j<channels;j++){
          buffer[ret++] = sample;
        }
      }
    }else if(inputs==1 && choice == FREQUENCY){ //Frq only
      //System.out.println("Frq only");
      float[] frqbuf = new float[buffneed];
      int returned = this.previous[0].work(frqbuf);
      for(int i=0;i<buffneed;i++){
        setSI(frqbuf[i] * frqRatio);
        float sample = getWaveSample()* this.amp;
        for(int j=0;j<channels;j++){
          buffer[ret++] = sample;
        }
      }
    }else{ //no inputs
                    //System.out.println("no inputs");
                    for(;ret<buffer.length;){
                        if(choice == FREQUENCY) setSI(this.frq * this.frqRatio);
                        float sample = getWaveSample() * this.amp;//Oscillator[(int)phase]*this.amp;     
                        for(int j=0;j<channels;j++){
                            try{
                                    buffer[ret++] = sample;
                            }catch(ArrayIndexOutOfBoundsException e){
                                //This can happen if a non mono signal chain wants
                                //to access the Oscillator as a mono signal
                                //Ignore and skip over
                                //
                                //We do need to remove one back off ret though to return
                                //the right number of samples to return
                                ret--;
                            }
                        }
                    }
    }
    return ret;
  }

  /**
   */
  public void build(){
      //this.numOfSamples = numOfSamples; 
        //System.out.println("Oscillator: NumOfSample = " + numOfSamples);
      if(this.frq < (float)0.0){
      // get pitch
      float notesFrq = (float)currentNote.getFrequency();
      // adjust for ratio setting
      notesFrq *= frqRatio;
      this.setSI(notesFrq);
      }else{
      // adjust for ratio setting
      this.frq *= frqRatio;
      this.setSI(this.frq);
     
  }
   
    /**
   * Set the parameter of this Oscillator to accept the fixed value
   * @param choiceVal  0 = fixed amplitude, 1 = fixed frequency
   */
  public void setChoice(int choiceVal){
    this.choice = choiceVal;
  }

  /**
   * Set the fixed amp of this Oscillator
   * @param amp Fixed value amplitude
   */
  public void setAmp(float amp){
    this.amp = amp;
    this.choice = AMPLITUDE;
  }
 
  /**
  * Get the fixed amp of this Oscillator
  */
  public float getAmp(){
    return this.amp;
  }

  /**
   * Set the fixed Frequency of this Oscillator
   * @param frq Fixed value frequency
   */
  public void setFrq(float frq){
    this.frq = frq;
    this.choice = FREQUENCY;
  }

  /**
   * Sets the frequency ratio to alter a notes pitch by
   * @param frqRatio Fixed ratio value to change frequency by
   */
  public void setFrqRatio(double frqRatio){
    this.frqRatio = (float)frqRatio;
  }

  //------------------------------------------
  // Protected Methods
  //------------------------------------------
  /**
   * Returns the sampling increment which is used
   * to nextWork out how many samples in the Oscillator
   * skip on each pass.
   * @param frequency the frequency used to find si
   */
  protected void setSI(double frequency){
      // Revoved error check to allow more felxability in FM synthesis
      /*if(frequency <= 0f) {
                System.err.println("Oscillator error: You tried to use a frequency less than zero - woops!");
                System.exit(1);
            } */
      this.si = 2.0f*(float)Math.PI/((float)this.sampleRate/(float)frequency);
  }

  /**
   * Returns a sample from any of the following waveforms
   */
  protected float getWaveSample(){
    switch(waveType){
    case SINE_WAVE:
      if(phase < 0){
        phase += 2.0f*(float)Math.PI;
      }
      float sample=(float)Math.sin((double)(phase+(2.0f*(float)Math.PI)));
      phase += si;
      if(phase >= (2.0f*(float)Math.PI)){
        phase -= 2.0f*(float)Math.PI;
      }
      return sample;
              case COSINE_WAVE:
      if(phase < 0){
        phase += 2.0f*(float)Math.PI;
      }
      sample=(float)Math.cos((double)(phase+(2.0f*(float)Math.PI)));
      phase += si;
      if(phase >= (2.0f*(float)Math.PI)){
        phase -= 2.0f*(float)Math.PI;
      }
      return sample;
               
                case TRIANGLE_WAVE:
                        sample = 0f;
                        if(phase < 0){
                            phase += 2.0f*(float)Math.PI;
                        }
                        float position = 0.5f / (float)Math.PI * phase;
                        if (position <= 0.25f) {
                            sample = (float)(position * 4.0);
                        }
                        if (position > 0.25f && position <= 0.75f) {
                            sample = (float)(4.0 * (0.5 - position));
                        }
                        if (position > 0.75f) {
                            sample = (float)((position - 1.0) * 4.0);
                        }
                        phase += si;
                        if(phase >= (2.0f*(float)Math.PI)){
                            phase -= 2.0f*(float)Math.PI;
                        }
                        return sample;
                case SQUARE_WAVE:
                        sample = 0f;
                        if(phase < 0){
                            phase += 2.0f*(float)Math.PI;
                        }
                        position = 0.5f / (float)Math.PI * phase;
                        if (position < 0.5f) {
                            sample = (float)(1.0);
                        } else sample = (float)(-1.0);
                        phase += si;
                        if(phase >= (2.0f*(float)Math.PI)){
                            phase -= 2.0f*(float)Math.PI;
                        }
                        return sample;
                case SAWTOOTH_WAVE:
                         if(phase < 0){
                            phase += 2.0f*(float)Math.PI;
                        }
                         position = (float)(1.0/ Math.PI) * phase;
                         sample = (float)(position - 1.0);
                         phase += si;
                        if(phase >= (2.0f*(float)Math.PI)){
                            phase -= 2.0f*(float)Math.PI;
                        }
                        return sample;
                case SAWDOWN_WAVE:
                         if(phase < 0){
                            phase += 2.0f*(float)Math.PI;
                        }
                         position = (float)(1.0/ Math.PI) * phase;
                         sample = (float)(1.0 - position);
                         phase += si;
                        if(phase >= (2.0f*(float)Math.PI)){
                            phase -= 2.0f*(float)Math.PI;
                        }
                        return sample;
                case SABERSAW_WAVE:
                         if(phase < 0){
                            phase += 2.0f*(float)Math.PI;
                        }
                         position = (float)(0.5 / Math.PI) * phase;
                         sample = (float)Math.exp(position) - 2.0f;
                         //System.out.println("Position = " + position + " Sample = " + sample);
                         phase += si;
                        if(phase >= (2.0f*(float)Math.PI)){
                            phase -= 2.0f*(float)Math.PI;
                        }
                        return sample;
                case PULSE_WAVE:
                        sample = 0f;
                        if(phase < 0){
                            phase += 2.0f*(float)Math.PI;
                        }
                        position =  0.5f / (float)Math.PI * phase;
                        if (position < (float)pulseWidth) {
                            sample = (float)(1.0);
                        } else sample = (float)(-1.0);
                        phase += si;
                        if(phase >= (2.0f*(float)Math.PI)){
                            phase -= 2.0f*(float)Math.PI;
                        }
                        return sample;
    default:
      System.err.println("Incorrect oscillator type selected.");
      System.exit(1);
      return 0f;
    }
  }
       
    /**
        * Specify the positive proportion of the pulse wave
        * @param width A value between 0.0 and 1.0
        */
    public void setPulseWidth(double width) {
        if(width >= 0.0 && width <= 1.0) {
            this.pulseWidth = width;
        } else System.err.println("Pulse wide must be between 0.0 and 1.0");
    }
   
    /**
        * Specify the initial phase of the waveform
        * @param phase The phase in radians (between 0.0 and 2 * PI)
        */
    public void setPhase(double phase) {
            this.phase = (float)phase;
    }
       
}
TOP

Related Classes of jm.audio.synth.Oscillator

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.