[ NODE_ENVELOPE      ]       
      +--------------------+       
      |                    |       
IN00--| SIGNAL      SIGNAL |--OUT00
      |                    |       
      +--------------------+       

this node allows the variation of a value over time.

SET_VALUE

  • TIME_SCALE_F32
  • ADD_STAGE_F32_F32
  • CLEAR_STAGES
  • START
  • STOP
/*
 * this example how to control the oscillator parameters frequency, cutoff, and amplitude with an envelope over time.
 * an envelope can have multiple stages. a start value is defined via `set_start_value(value)` ( default = 0.0 ) and
 * stages are add with `add_stage(duration, value)` or `add_stage_ms(duration_in_milli_seconds, value)`.
 *
 * once an envelope is started with `start()` it will interpolate from its current value to the value of the next
 * stage. e.g the following code will generate values from `0.0` to `2.0` over 1.0 sec then from `2.0` to `1.0` over
 * 0.5 sec:
 *
 * ```
 * mEnvelope.set_start_value(0.0);
 * mEnvelope.add_stage(1.0, 2.0);
 * mEnvelope.add_stage(0.5, 1.0);
 * ```
 *
 * envelopes can be stopped at any point with `stop()`. envelopes can be set to loop with `set_loop(true)`. the
 * envelope duration can be scaled with `set_time_scale(scale)` where a value of `1.0` is the normal duration. the 
 * envelopes values can be scaled with `set_value_scale(scale)` where a value of `1.0` is the normal value.
 *
 * envelopes can also be triggered from an incoming signal where a `start()` event is triggered whenever it passes a
 * threshold ( 0.0 ) on a rising edge and a `stop()` event on a falling edge.
 *
 * in this example pressing `1` on the keyboard will trigger the envelope, `2` will set the envelope to loop and `3`
 * will stop the envelope. the mouse will change the oscillator frequency on the y-axis and the envelope’s time scaling
 * on the x-axis.
 *
 */

#include "Klangstrom.h"
#include "KlangNodes.hpp"

using namespace klang;
using namespace klangstrom;

NodeDAC          mDAC;
NodeEnvelope     mEnvelopeFreq;
NodeEnvelope     mEnvelopeAmp;
NodeEnvelope     mEnvelopeCutoff;
NodeVCOWavetable mOsc;
NodeVCFMoogLHBP  mFilter;
NodeVCA          mMultiply;
bool             fLoopEnvelopes = false;

void setup() {
    Klang::connect(mEnvelopeFreq, Node::CH_OUT_SIGNAL, mMultiply, Node::CH_IN_SIGNAL);
    Klang::connect(mMultiply, Node::CH_OUT_SIGNAL, mOsc, NodeVCOWavetable::CH_IN_FREQ);
    Klang::connect(mEnvelopeAmp, Node::CH_OUT_SIGNAL, mOsc, NodeVCOWavetable::CH_IN_AMP);
    Klang::connect(mEnvelopeCutoff, Node::CH_OUT_SIGNAL, mFilter, NodeVCF::CH_IN_CUTOFF);
    Klang::connect(mOsc, mFilter);
    Klang::connect(mFilter, Node::CH_OUT_SIGNAL, mDAC, NodeDAC::CH_IN_SIGNAL);

    mEnvelopeFreq.add_stage(0.1, 4.0);
    mEnvelopeFreq.add_stage(0.2, 0.5);
    mEnvelopeFreq.add_stage(0.01, 1.0);
    mEnvelopeFreq.add_stage(0.1, 2.0);
    mEnvelopeFreq.add_stage(0.5, 0.0);

    mEnvelopeCutoff.set_start_value(0.4);
    mEnvelopeCutoff.add_stage(0.05, 0.5);
    mEnvelopeCutoff.add_stage(0.15, 0.1);
    mEnvelopeCutoff.add_stage(0.1, 0.2);
    mEnvelopeCutoff.add_stage(0.1, 0.7);
    mEnvelopeCutoff.add_stage(0.4, 0.0);

    mEnvelopeAmp.add_stage_ms(100, 0.5);
    mEnvelopeAmp.add_stage_ms(300, 0.3);
    mEnvelopeAmp.add_stage_ms(450, 0.0);

    mOsc.set_waveform(NodeVCOWavetable::WAVEFORM::SAWTOOTH);
    mOsc.set_frequency(55);
    mOsc.set_amplitude(0.33);

    mFilter.set_cutoff(0.1);
    mFilter.set_resonance(0.5);
}

void loop() {}

void audioblock(float** input_signal, float** output_signal) {
    mDAC.process_frame(output_signal[LEFT], output_signal[RIGHT]);
}

void start_envelopes() {
    mEnvelopeFreq.start();
    mEnvelopeAmp.start();
    mEnvelopeCutoff.start();
}

void event_receive(const EVENT_TYPE event, const void* data) {
    if (event == EVENT_KEY_PRESSED) {
        const char key = (char)keyboard_event(data).keys[0];
        switch (key) {
            case '1':
                start_envelopes();
                break;
            case '2':
                fLoopEnvelopes = !fLoopEnvelopes;
                mEnvelopeFreq.set_loop(fLoopEnvelopes);
                mEnvelopeCutoff.set_loop(fLoopEnvelopes);
                mEnvelopeAmp.set_loop(fLoopEnvelopes);
                break;
            case '3':
                mEnvelopeAmp.stop();
                mEnvelopeFreq.stop();
                mEnvelopeCutoff.stop();
                break;
        }
    }
    if (event == EVENT_MOUSE_MOVED) {
        mMultiply.set_amplification(55 * floor(1.0 + mouse_event(data).y * 8.0));
        float mTimeScale = mouse_event(data).x * 0.9 + 2.1;
        mEnvelopeFreq.set_time_scale(mTimeScale);
        mEnvelopeAmp.set_time_scale(mTimeScale);
        mEnvelopeCutoff.set_time_scale(mTimeScale);
    }
}

Source Code

NodeEnvelope.hpp