// ------------------------------------------------------------ // Class for a basic sine/triangle/sawtooth wave oscillator. // Not designed for audio generation or other high performance tasks. // Construct with [[waveShape,] name,] [period[, amplitude[, median[, phaseOffset]]]] // ...well, something like that. Numbers taken to be period, amplitude, median, phaseOffset (in order), // but none of these are required. A string can be passed before these (if any), optionally // preceeded by a waveShape. ie, waveShape can only be specified if a name is also specified (since // the wave shape on its own would be confused with just the period on its own... Procesing 1.0 // doesn't yet support enum's, which would be perfect for waveShape. // Synchronise by calling tick(). // Retrieve current output value via value(). // ------------------------------------------------------------ static final int maxWaveShape = 3; static final String defaultName = "unnamed"; static int defaultNameCounter = 0; static final int defaultWaveShape = 0; static final float defaultPeriod = 1; static final float defaultAmplitude = 1; static final float defaultMedian = 0; static final float defaultPhaseOffset = 0; class Oscillator { // MEMBERS String name; int waveShape; // 0 = sine, 1 = triangle, 2 = sawtooth, 3 = square float period; // Period in seconds float amplitude; // amplitude in units float median; // Median value in units float phase; // Phase in radians float phaseStepPerMilli; // Phase step per second in radians boolean backwards; // Set true to run backwards boolean paused; // True: paused (don't update phase), false: not paused (update phase) // CONSTRUCTORS // "Default" constructor. Oscillator() { this(defaultWaveShape, null, defaultPeriod, defaultAmplitude, defaultMedian, defaultPhaseOffset); } Oscillator(float period) { this(defaultWaveShape, null, period, defaultAmplitude, defaultMedian, defaultPhaseOffset); } Oscillator(float period, float amplitude) { this(defaultWaveShape, null, period, amplitude, defaultMedian, defaultPhaseOffset); } Oscillator(float period, float amplitude, float median) { this(defaultWaveShape, null, period, amplitude, median, defaultPhaseOffset); } Oscillator(float period, float amplitude, float median, float phaseOffset) { this(defaultWaveShape, null, period, amplitude, median, phaseOffset); } Oscillator(String name) { this(defaultWaveShape, name, defaultPeriod, defaultAmplitude, defaultMedian, defaultPhaseOffset); } Oscillator(String name, float period) { this(defaultWaveShape, name, period, defaultAmplitude, defaultMedian, defaultPhaseOffset); } Oscillator(String name, float period, float amplitude) { this(defaultWaveShape, name, period, amplitude, defaultMedian, defaultPhaseOffset); } Oscillator(String name, float period, float amplitude, float median) { this(defaultWaveShape, name, period, amplitude, median, defaultPhaseOffset); } Oscillator(String name, float period, float amplitude, float median, float phaseOffset) { this(defaultWaveShape, name, period, amplitude, median, phaseOffset); } Oscillator(int waveShape, String name) { this(waveShape, name, defaultPeriod, defaultAmplitude, defaultMedian, defaultPhaseOffset); } Oscillator(int waveShape, String name, float period) { this(waveShape, name, period, defaultAmplitude, defaultMedian, defaultPhaseOffset); } Oscillator(int waveShape, String name, float period, float amplitude) { this(waveShape, name, period, amplitude, defaultMedian, defaultPhaseOffset); } Oscillator(int waveShape, String name, float period, float amplitude, float median) { this(waveShape, name, period, amplitude, median, defaultPhaseOffset); } // Main Constructor Oscillator(int waveShape, String name, float period, float amplitude, float median, float phaseOffset) { this.name = (name != null && name != "") ? name : defaultName + (defaultNameCounter++); this.waveShape = (waveShape < 0 || waveShape > maxWaveShape) ? 0 : waveShape; setPeriod(period); this.amplitude = amplitude; this.median = median; this.phase = phaseOffset; this.backwards = false; this.paused = false; } // METHODS void setPeriod(float period) { this.period = period; updatePhaseStepPerMilli(); } void setBackwards(boolean backwards) { this.backwards = backwards; updatePhaseStepPerMilli(); } void reverseDirection() { backwards = !backwards; updatePhaseStepPerMilli(); } // This helper function should be called any time the period is changed, since the // tick() methdod needs to know how far to advance. private void updatePhaseStepPerMilli() { if (period == 0) phaseStepPerMilli = 0; else phaseStepPerMilli = ( backwards ? -0.001 : 0.001 ) * TWO_PI / period; } // If not paused, advance the phase for the next timeslice/iteration/what-have-you void tick(float milliseconds) { if (!paused) phase += milliseconds * phaseStepPerMilli; } void pause() { paused = true; } void unPause() { paused = false; } void togglePause() { paused = !paused; } // Retrieve the current output value for the oscillator float value() { float frac; switch (waveShape) { case 0: default: // SINE return median + amplitude * sin( phase ); case 1: // TRIANGLE // | /\ : /\ : // |/ \ :/ \ : // +----+----+----+----+ 4pi // | \ /: \ /: // | \/ : \/ : frac = ((phase + HALF_PI) % TWO_PI) / TWO_PI; if (frac < 0) frac += 1; if (frac < 0.5) return median + 2 * amplitude * (frac * 2 - 0.5); return median - 2 * amplitude * ((frac - 0.5) * 2 - 0.5); case 2: // SAWTOOTH (RISING) // | ,/o : ,/o : [The o's show a discontinuities] // |,/ | :,/ | : // +----+----+----+----+ 4pi // | | ,/: | ,/: // | |,/ : |,/ : frac = ((phase + PI) % TWO_PI) / TWO_PI; if (frac < 0) frac += 1; return median + 2 * amplitude * (frac - 0.5); case 3: // SQUARE // |---------o : [The o's show a discontinuities] // | | : // +----+----+----+----+ 4pi // | | : // | +---------o frac = (phase % TWO_PI) / TWO_PI; if (frac < 0) frac += 1; return median + amplitude * (frac < 0.5 ? 1 : -1); } } // Return a string description of the oscillator // eg "twist: ~6.0s" meaning name "twist", wave shape sine, period 6.0 seconds // eg "curve: ^3.5s" meaning name "curve", wave shape triangle, period 3.5 seconds // eg "bob: / (paused)" meaning name "bob", wave shape sawtooth, paused (period not shown) String str() { String message = name + ":"; switch (waveShape) { case 0: default: // SINE message = message + " ~"; break; case 1: // TRIANGLE message = message + " ^v"; break; case 2: // SAWTOOTH (RISING) message = message + " /|"; break; case 3: // SQUARE message = message + " -_"; break; } if (paused) message = message + " (paused)"; else message = message + " " + period + "s"; return message; } }