Skip to content

Lesson 4: sequencing 101

Nicola Pisanti edited this page Apr 8, 2016 · 24 revisions

In this lesson we will learn about more sequencing with ofxPDSP. Copy again the init app code again. in 'ofApp.h' we need:

    // pdsp modules
    ofxPDSPEngine       engine;

    pdsp::Sequence      sequence;        
    pdsp::ADSR          adsr;
    pdsp::Amp           amp;
    pdsp::FMOperator    sine;

now in the ofApp.cpp code:

void ofApp::patch(){
    engine.score.setTempo(108.0);
    
    engine.score.sections.resize(1); // by default we have 0 sections, we need 1

    engine.score.sections[0].setCell(0, &sequence, pdsp::Behavior::Loop); 
    // arguments are: index, pointer to pdsp::ScoreCell or pdsp::Sequence, pointer to pdsp::CellChange

    engine.score.sections[0].launchCell(0); // cells are stopped by default, start the first cell of sections[0]

    // we patch our section to our synth
    engine.score.sections[0].out_trig(0) >> adsr; // first output is patched to envelope

    // our synth is a simple sine wave
    adsr.set(0.0f, 50.0f, 1.0f, 50.0f) >> amp.in_mod(); 
    sine >> amp * 0.5f >> engine.audio_out(0);
            amp * 0.5f >> engine.audio_out(1);
   
    // SEQUENCE CODING
    sequence.set( {  1.0f, 0.0f, 0.5f, 0.0f, 0.3f, 0.0f, 0.2f, 0.0f }, 16.0, 1.0);    
    // arguments are: an inline array of values, the time division (16.0 = 1/16t), the sequence length
}

Now i will explain what it's happening here. Inside our engine we have a score member. score is the object we use for updating the global playhead and for sequencing. Inside score there is a vector member called sections. Think of those sections as tracks of your arrangment, or different sections of an orchestral score. Each section has one ore more outputs you can patch to your modules. Also each sections has a table with pointers to our pdsp::ScoreCell object with each index.
pdsp::ScoreCell is a class that rapresent the message we are scoring. We will use pdsp::Sequence, that is a subclass of pdsp::CellChange easier to work with. We are setting sequence giving it a list of values that will be sent to the connected envelope sequentially at the given clock division (in our case 8.0 = 1/16th). After the sequence length is expired another ScoreCell/Sequence is triggered (in our case the sequence retrigger itself as we have set pdsp::Behavior::Loop for that section index). The positive values are opening the envelope gate, the 0.0f value are closing it, so even if we have set our time to 16.0 we will hear an 8th division.

Compile and run, you should hear our sequentially triggered beeps.

void ofApp::patch(){
    engine.score.setTempo(108.0);
    
    engine.score.sections.resize(1); // by default we have 0 sections, we need 1
    engine.score.sections[0].setCell(0, &sequence, pdsp::Behavior::Self); 
    engine.score.sections[0].launchCell(0);
    
    // we patch our section to our synth
    // in out_trig() or out_value() you pass the output number (or 0 if you don't give an argument)
    // you can't use both out_trig() and out_value() for the same output number
    engine.score.sections[0].out_trig(0) >> ahr; // first output is patched to envelope
    engine.score.sections[0].out_value(1) >> sine.in_pitch(); // second is patched to pitch

    ahr.set(0.0f, 50.0f, 50.0f) >> amp.in_mod(); 
    sine >> amp * 0.5f >> engine.audio_out(0);
            amp * 0.5f >> engine.audio_out(1);

}