Manual: Oscillator

[ javadoc | examples ]

Oscillator is an implementation of AudioSignal that handles most of the work associated with oscillatory signals like sine waves, saw waves, and so forth. An Oscillator is always a MONO signal. You will never construct an Oscillator directly because it is an abstract class. Instead you will either use one of the concrete classes like SineWave or SawWave, or you will extend Oscillator with your own class (see Extending Oscillator below).

Manipulating An Oscillator

Oscillator provides four settings that you can tweak: amplitude (volume), frequency (pitch), pan, and portamento:

// Returns the current amplitude.
float amplitude()
// Set the amplitude of the Oscillator, range is [0, 1].
void setAmp(float a) 
// Returns the current frequency.          
float frequency()
// Sets the frequency of the Oscillator in Hz.
void setFreq(float f)
// Returns the current pan value.
float pan()        
// Set the pan of the Oscillator, range is [-1, 1].          
void setPan(float p)
// Sets how many milliseconds it should take to transition 
// from one frequency to another when setting a new frequency.
void portamento(int millis) 
// Turns off portamento.        
void noPortamento()

In Minim, digital audio is represented as floating-point values in the range of -1 to 1. Values outside of this range do not make sense and are clamped to the range by the system. Therefore, amplitude (which you can think of as loudness), is expressed as a value between 0 and 1, where 0 is silent and 1 is full volume. Mathematically, amplitude is simply the amount that Oscillator scales the waveform values it gets from derived classes. It assumes that derived classes will be sampling waveforms with peaks at -1 and 1 so that an amplitude of 1 results in a full volume sound. Note setting the amplitude of an Oscillator is different from setting the amplitude or volume of the AudioOutput it is attached to. An Oscillator might have its amplitude set to 1 and the output might have its amplitude set to 0.5, resulting in a half-volume sound.

The frequency of an Oscillator is in Hertz, which is cycles per second. For reference, 440 Hz is the A above middle C on the piano, and if you double the frequency, the pitch will change by an octave. When changing the frequency, the portamento value will be taken into account. When the portamento value is greater than zero, the Oscillator will smoothly change frequency to the frequency set with the setFreq method from its current frequency, over the number of milliseconds last specified by a call to the portamento method. You can turn off portamento by calling noPortamento, which is the same as setting the portamento to 0.

Since Oscillators are MONO signals, you can set a pan value for them. This is where “in space” the signal will sound between two speakers. -1 is “hard left”, 1 is “hard right”, and 0 is “center”. All this is better heard than described.

Code Sample (online example)

import ddf.minim.*;
import ddf.minim.signals.*;
 
AudioOutput out;
SineWave sine;
 
void setup()
{
  size(512, 200);
  // always start Minim before you do anything with it
  Minim.start(this);
  // get a line out from Minim, default bufferSize is 1024, 
  // default sample rate is 44100, bit depth is 16
  out = Minim.getLineOut(Minim.STEREO);
  // create a sine wave Oscillator, set to 440 Hz, 
  // at 0.5 amplitude, sample rate from line out
  sine = new SineWave(440, 0.5, out.sampleRate());
  // set the portamento speed on the oscillator to 200 milliseconds
  sine.portamento(200);
  // add the oscillator to the line out
  out.addSignal(sine);
}
 
void draw()
{
  background(0);
  stroke(255);
  // draw the waveforms
  for(int i = 0; i < out.bufferSize() - 1; i++)
  {
    line(i, 50 + out.left.get(i)*50, i+1, 50 + out.left.get(i+1)*50);
    line(i, 150 + out.right.get(i)*50, i+1, 150 + out.right.get(i+1)*50);
  }
}
 
void mouseMoved()
{
  // with portamento on the frequency will change smoothly
  float freq = map(mouseY, 0, height, 1500, 60);
  sine.setFreq(freq);
  // pan always changes smoothly to avoid crackles getting into the signal
  // note that we could call setPan on out, instead of on sine
  // this would sound the same, but the waveforms in out would not reflect the panning
  float pan = map(mouseX, 0, width, -1, 1);
  sine.setPan(pan);
}
 
void stop()
{
  // always close Minim classes when you are done with them
  out.close();
  // always stop Minim before exiting
  Minim.stop();
 
  super.stop();
}

Oscillator Properties

// Returns the period of the waveform (the inverse of the frequency).
float period() 
// Returns the sample rate this was constructed with
float sampleRate()

The period of an Oscillator is how long one cycle lasts, expressed as a fraction of a second. It is simply the inverse of the frequency. So if you have an Oscillator set to 440 Hz, the period will be 1/440 (0.0022727 seconds). The sample rate simply returns the sample rate that the Oscillator was constructed with.

Signal Generation

// Fills signal with values in the range of [-1, 1].          
void generate(float[] signal)
// Fills left and right with values in the range of [-1, 1].          
void generate(float[] left, float[] right)

Because Oscillator implements AudioSignal, it must implement the generate methods. Normally these will be called by the AudioOutput you add the Oscillator to, but if you are doing something different, like generating audio for frames of movie, you might want to call this method yourself so that you have control over how quickly the signal is generated.

Extending Oscillator

// Returns the value of the waveform at step.
abstract value(float step)

To create your own oscillator you must extend Oscillator and implement the value method. Oscillator will call this method every time it needs to sample your waveform. The number passed to the method is an offset from the beginning of the waveform’s period and should be used to sample your waveform at that point. Here’s the implementation of the SineWave class used in the above example:

public class SineWave extends Oscillator 
{
  public SineWave(float frequency, float amplitude, float sampleRate) 
  {
    super(frequency, amplitude, sampleRate);
  }
 
  protected float value(float step) 
  {
    return (float)Math.sin(frequency()*TWO_PI*step);
  }
}