/* * Copyright (c) 2007 by Damien Di Fede <ddf@compartmental.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package ddf.minim.signals; import processing.core.PApplet; import ddf.minim.AudioSignal; import ddf.minim.Minim; /** * <code>Oscillator</code> is an implementation of an <code>AudioSignal</code> * that handles most of the work associated with an oscillatory signal like a * sine wave. To create your own oscillator you must extend * <code>Oscillator</code> and implement the {@link #value(float) value} * method. <code>Oscillator</code> 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. * * @author Damien Di Fede * */ public abstract class Oscillator implements AudioSignal { /** The float value of 2*PI. Provided as a convenience for subclasses. */ protected static final float TWO_PI = (float) (2 * Math.PI); /** The current frequency of the oscillator. */ private float freq; /** The frequency to transition to. */ private float newFreq; /** The sample rate of the oscillator. */ private float srate; /** The current amplitude of the oscillator. */ private float amp; /** The amplitude to transition to. */ private float newAmp; /** The current position in the waveform's period. */ private float step; /** * The amount to increment step between calls to the <code>value</code> * method. */ private float stepSize; /** The portamento state. */ private boolean port; /** The portamento speed in milliseconds. */ private float portSpeed; // in milliseconds /** * The amount to increment or decrement <code>freq</code> during the * transition to <code>newFreq</code>. */ private float portStep; /** The current pan position. */ private float pan; /** The pan position to transition to. */ private float newPan; /** * The amount to scale the left channel's amplitude to achieve the current pan * setting. */ private float leftScale; /** * The amount to scale the right channel's amplitude to achieve the current * pan setting. */ private float rightScale; /** * Constructs an Oscillator with the requested frequency, amplitude and sample * rate. * * @param frequency * the frequency of the Oscillator * @param amplitude * the amplitude of the Oscillator * @param sampleRate * the sample rate of the Oscillator */ public Oscillator(float frequency, float amplitude, float sampleRate) { freq = frequency; newFreq = freq; amp = amplitude; newAmp = amp; srate = sampleRate; step = 0; stepSize = 1 / (float) sampleRate; port = false; portStep = 0.01f; pan = 0; newPan = 0; leftScale = rightScale = 1; } public final float sampleRate() { return srate; } /** * Sets the frequency of the Oscillator in Hz. If portamento is on, the * frequency of the Oscillator will transition from the current frequency to * <code>f</code>. * * @param f * the new frequency of the Oscillator */ public final void setFreq(float f) { newFreq = f; // we want to step from freq to new newFreq in portSpeed milliseconds // first off, we want to divide the difference between the two freqs // by the number of milliseconds it's supposed to take to get there float msStep = (newFreq - freq) / portSpeed; // but since freq is incremented at every sample, we need to divide // again by the number of samples per millisecond float spms = srate / 1000; portStep = msStep / spms; } /** * Returns the current frequency. * * @return the current frequency */ public final float frequency() { return freq; } /** * Set the amplitude of the Oscillator, range is [0, 1]. * * @param a * the new amplitude, it will be constrained to [0, 1] */ public final void setAmp(float a) { newAmp = PApplet.constrain(a, 0, 1); } /** * Returns the current amplitude. * * @return the current amplitude */ public final float amplitude() { return amp; } /** * Set the pan of the Oscillator, range is [-1, 1]. * * @param p - * the new pan value, it will be constrained to [-1, 1] */ public final void setPan(float p) { newPan = PApplet.constrain(p, -1, 1); } /** * Returns the current pan value. * * @return the current pan value */ public final float pan() { return pan; } /** * Sets how many milliseconds it should take to transition from one frequency * to another when setting a new frequency. * * @param millis * the length of the portamento */ public final void portamento(int millis) { if (millis <= 0) { Minim .error("Oscillator.portamento: The portamento speed must be greater than zero."); } port = true; portSpeed = millis; } /** * Turns off portamento. * */ public final void noPortamento() { port = false; } public final void generate(float[] signal) { if (port && freq != newFreq) { for (int i = 0; i < signal.length; i++) { signal[i] = amp * value(step); if (Math.abs(freq - newFreq) < 0.1f) freq = newFreq; else freq += portStep; monoStep(); } } else if (freq != newFreq) { for (int i = 0; i < signal.length / 2; i++) { float fadeOut = PApplet.map(i, 0, signal.length / 2, amp, 0); signal[i] = fadeOut * value(step); monoStep(); } freq = newFreq; for (int i = signal.length / 2; i < signal.length; i++) { float fadeIn = PApplet.map(i, signal.length / 2, signal.length, 0, amp); signal[i] = fadeIn * value(step); monoStep(); } } else { for (int i = 0; i < signal.length; i++) { signal[i] = amp * value(step); monoStep(); } } } public final void generate(float[] left, float[] right) { if (port && freq != newFreq) { for (int i = 0; i < left.length; i++) { left[i] = leftScale * amp * value(step); right[i] = rightScale * amp * value(step); if (Math.abs(freq - newFreq) < 0.1f) freq = newFreq; else freq += portStep; stereoStep(); } } else if (freq != newFreq) { for (int i = 0; i < left.length / 2; i++) { float fadeOut = PApplet.map(i, 0, left.length / 2, amp, 0); left[i] = leftScale * fadeOut * value(step); right[i] = rightScale * fadeOut * value(step); stereoStep(); } freq = newFreq; for (int i = left.length / 2; i < left.length; i++) { float fadeIn = PApplet.map(i, left.length / 2, left.length, 0, amp); left[i] = leftScale * fadeIn * value(step); right[i] = rightScale * fadeIn * value(step); stereoStep(); } } else { for (int i = 0; i < left.length; i++) { left[i] = leftScale * amp * value(step); right[i] = rightScale * amp * value(step); stereoStep(); } } } private void monoStep() { stepStep(); stepAmp(); } private void stereoStep() { stepStep(); stepAmp(); calcLRScale(); stepPan(); } private void stepStep() { step += stepSize; if (step > period()) step %= period(); } private void calcLRScale() { if (pan <= 0) { rightScale = PApplet.map(pan, -1, 0, 0, 1); leftScale = 1; } if (pan >= 0) { leftScale = PApplet.map(pan, 0, 1, 1, 0); rightScale = 1; } if (pan == 0) { leftScale = rightScale = 1; } } private static float panAmpStep = 0.0001f; private void stepPan() { if (pan != newPan) { if (pan < newPan) pan += panAmpStep; else pan -= panAmpStep; if (Math.abs(pan - newPan) < panAmpStep) pan = newPan; } } private void stepAmp() { if (amp != newAmp) { if (amp < newAmp) amp += panAmpStep; else amp -= panAmpStep; if (Math.abs(amp - newAmp) < panAmpStep) pan = newPan; } } /** * Returns the period of the waveform (the inverse of the frequency). * * @return the period of the waveform */ public final float period() { return 1 / freq; } /** * Returns the value of the waveform at <code>step</code>. To take * advantage of all of the work that <code>Oscillator</code> does, you can * create your own periodic waveforms by extending <code>Oscillator</code> * and implementing this function. All of the oscillators included with Minim * were created in this way. * * @param step * an offset from the beginning of the waveform's period * @return the value of the waveform at <code>step</code> */ protected abstract float value(float step); }