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:

[snip java]
// 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()
[/snip]

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)

[snip code_sample]http://code.compartmental.net/minim/examples/AudioOutput/SineWaveSignal/SineWaveSignal.pde[/snip]

Oscillator Properties

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

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

[snip java]
// 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)
[/snip]

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

[snip java]
// Returns the value of the waveform at step.
abstract value(float step)
[/snip]

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 a normalized offset from the beginning of the waveform's period (a number between 0 and 1) and should be used to sample your waveform at that point. Here's the implementation of the SineWave class used in the above example:

[snip java]
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(TWO_PI*step);
}
}
[/snip]

9 thoughts on “Manual: Oscillator

  1. There’s a typo in the SineWave class above – the body of value() should be

    return (float)Math.sin(TWO_PI*step);

    since Oscillator already takes care of the frequency(). At least with the latest version of Processing (1.0.9).

  2. Pingback: Palmera blog » Archivo del Blog » Síntesis Modular con Processing

  3. Hello!

    The value of the step parameter coming in to the value method in the SineWave example is not actually normalised between 0 and 1 as it continues to rise past 1 the longer you run the sketch for. It is therefore the number of seconds since the sketch started. (I think … ).

    cheers

    Matthew

  4. No, current code for Oscillator steps the value being passed to the value method like so:


    step += stepSize;
    step = step - PApplet.floor(step);

    It may not be immediately clear that this keeps the value between 0 and 1, but the idea is that stepSize will never be so large as to send the value above 2.

  5. Tried to create a custom oscillator class with amp envelope generator thusly:


    public class MyOscillator extends Oscillator {
    AsrEnvelope env;

    public MyOscillator(float newFreq, float newAmp, float newSampleRate) {
    super( newFreq, newAmp, newSampleRate );
    env = new AsrEnvelope(10f, 200f, 10f, 1f);
    }

    protected float value(float step) {
    return env.generate() * (float) Math.sin( TWO_PI * step);
    }

    public void trigger() {
    env.trigger();
    }
    }

    public class AsrEnvelope {
    float attack; // Attack time in ms
    float sustain; // Sustain time in ms
    float release; // Release time in ms
    float start; // Start time of envelope in ms
    // Set every time envelope is triggered
    float len; // Overall length of envelope in ms
    float end; // End of envelope in ms
    // Set every time envelope is triggered
    float amplitude; // Amplitude used to scale the envelope values

    public AsrEnvelope(float newA, float newS, float newR, float newAmp) {
    amplitude = newAmp;
    attack = newA;
    sustain = newS;
    release = newR;
    start = 0f;
    len = attack + sustain + release;
    end = len;
    }

    public void trigger() {
    start = millis();
    end = start + len;
    }

    public float generate() {
    float ms = millis();
    float b1 = start + attack;
    float b2 = b1 + sustain;
    float b3 = b2 + release;
    float value;

    if(start <= ms && ms < b1) {
    value = map(ms, start, b1, 0f, amplitude);
    }
    else if(b1 <= ms && ms < b2) {
    value = amplitude;
    }
    else if(b2 <=ms && ms < b3) {
    value = map(ms, b2, b3, amplitude, 0f);
    }
    else {
    value = 0f;
    }
    return value;
    }
    }

    But it didn’t seem to work because env.generate() is not being called from within the value method of Oscillator. How to better implement an envelope generator class?

    Thx
    BS

  6. Well, you could override the value method of Oscillator, do your work and then return super.value(). You could also check out the UGen framework in the Beta release, which already contains an ADSR class that you can use.

  7. Pingback: April 7 – Harrison – Sensor in Processing | Creative Computing, Spring 2011

  8. Pingback: Joujou avec Substrate … Partie 2: inspiration et premiers tests. | Le Press de Fabien

Comments are closed.