![JAR search and dependency download from the Maven repository](/logo.png)
jm.audio.synth.Oscillator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmusic Show documentation
Show all versions of jmusic Show documentation
JMusic - Java Music Library
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.audio.AOException;
import jm.audio.AudioObject;
import jm.audio.Instrument;
/**
* 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;
/**
* which waveform to use
*/
private int waveType = SINE_WAVE;
/**
* 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;
/* modulation sources */
/**
* The constant that represents the sine wave form.
* The pulse width can be set using setPulseWidth() method.
*/
public static final int PULSE_WAVE = 7;
/**
* 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;
/**
* 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
* @throws 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;
}
/**
* Get the fixed amp of this Oscillator
*/
public float getAmp() {
return this.amp;
}
/**
* Set the fixed amp of this Oscillator
*
* @param amp Fixed value amplitude
*/
public void setAmp(float amp) {
this.amp = amp;
this.choice = AMPLITUDE;
}
/**
* 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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy