-
Notifications
You must be signed in to change notification settings - Fork 37
Lesson 3: making Modules
In this lesson we will learn about making new modules out of basic elements. Start again from scratch. Add a theremin.h file to you /src folder. We will write our new class in the .h code (really bad coding practice, but we are just sketching). In theremin.h
#pragma once
#include "ofxPDSP.h"
class Theremin : public pdsp::Patchable{
public:
Theremin() {
//add inputs / outputs with these methods
addModuleInput("amp", amp.in_mod()); // arguments are tag and the Unit in/out to link to that tag
addModuleInput("pitch", osc.in_pitch());
addModuleOutput("signal", amp );
//patching
osc >> amp;
}
private:
pdsp::Amp amp;
pdsp::FMOperator osc;
};
pdsp::Patchable
is the base class you have to extend for creating new modules. Our amp
and osc
objects are private, but using the methods addModuleInput(const char *tag, Patchable &unit)
and addModuleOutput(const char *tag, Patchable &unit)
we are making some of those objects inputs/outputs available for patching outside the class using the in("tag")
and out("tag")
methods. When you are adding an input you can set its tag name as first argument and you can select the input/output to assign as second argument ( if you are not selecting an in/out defaults are used). The first input added will become the default input, and the first output added will become the default output.
So in our ofApp.h
, on the top add
#include "theremin.h"
and on the bottom:
// pdsp modules
pdsp::Processor engine;
Theremin theremin; //our new class
in ofApp.cpp
void ofApp::setup(){
ofSetWindowShape(640, 360);
//--------PATCHING-------
theremin * 0.5f >> engine.audio_out(0); // the amp output is default in Theremin
theremin * 0.5f >> engine.audio_out(1);
//------------SETUPS AND START AUDIO-------------
engine.listDevices();
engine.setDeviceID(0); // REMEMBER TO SET THIS AT THE RIGHT INDEX!!!!
engine.setup( 44100, 512, 3);
}
void ofApp::mouseMoved(int x, int y ){
float gain = ofMap(y, 0.0f, ofGetHeight(), 1.0f, 0.0f );
float p = ofMap(x, 0.0f, ofGetWidth(), 48.f, 84.f ); //three octave
gain >> theremin.in("amp");
p >> theremin.in("pitch");
}
Now compile and run. By hovering your mouse on the window you should be able to control both the synth amp and pitch.
There is still the problem of some audible zipper noise caused by the control being updated in steps. We can use pdsp::CRSlew
to smoothen signals, so modify theremin.h
:
#pragma once
#include "ofxPDSP.h"
class Theremin : public pdsp::Patchable{
public:
Theremin() {
//add inputs / outputs with this methods
addModuleInput("amp", ampSlew); // arguments are tag and the Unit in/out to link to that tag
addModuleInput("pitch", pitchSlew);
addModuleOutput("signal", amp );
//patching
pitchSlew.set(250.0f) >> osc.in_pitch(); // 250 millisecond slew
ampSlew.set(100.0f) >> amp.in_mod(); // 100 millisecond slew
osc >> amp;
}
private:
pdsp::Amp amp;
pdsp::FMOperator osc;
pdsp::CRSlew ampSlew;
pdsp::CRSlew pitchSlew;
};
You don't need to modify the other files. Notice we have substituted our pdsp::CRSlew
in the addModuleInput()
calls and patched them to the amp mod and pitch inputs. Compile and run. Now you can try again and everything will be nicely smoothen out.
Notice that we patched our module using a string passed to the in() method. Basically it's what happens inside all the in_tagname() and out_tagname() methods, they're just handy methods if your IDE has autocompletion. We can add them to our class too:
#pragma once
#include "ofxPDSP.h"
class Theremin : public pdsp::Patchable{
public:
Theremin() {
//add inputs / outputs with this methods
addModuleInput("amp", ampSlew); // arguments are tag and the Unit in/out to link to that tag
addModuleInput("pitch", pitchSlew);
addModuleOutput("signal", amp );
//patching
pitchSlew.set(250.0f) >> osc.in_pitch(); // 250 millisecond slew
ampSlew.set(100.0f) >> amp.in_mod(); // 100 millisecond slew
osc >> amp;
}
pdsp::Patchable& in_amp(){
return in("amp");
}
pdsp::Patchable& in_pitch(){
return in("pitch");
}
pdsp::Patchable& out_signal(){
return out("signal");
}
private:
pdsp::Amp amp;
pdsp::FMOperator osc;
pdsp::CRSlew ampSlew;
pdsp::CRSlew pitchSlew;
};
Now this module will work perfectly fine until you use some kind of data structure of Theremin, like a vector for having multiple voices. Then the compiler will spit out lot of errors, as some of the classes we are using cannot create default copy constructors, so we have to refactor our code like this:
#pragma once
#include "ofxPDSP.h"
class Theremin : public pdsp::Patchable{
public:
Theremin() { patch(); }
Theremin(const Theremin & other) { patch(); cout<<"warning! copy constructing modules is bad!!!\n"; }
void patch(){
//add inputs / outputs with this methods
addModuleInput("amp", ampSlew); // arguments are tag and the Unit in/out to link to that tag
addModuleInput("pitch", pitchSlew);
addModuleOutput("signal", amp );
//patching
pitchSlew.set(250.0f) >> osc.in_pitch(); // 250 millisecond slew
ampSlew.set(100.0f) >> amp.in_mod(); // 100 millisecond slew
osc >> amp;
}
pdsp::Patchable& in_amp(){
return in("amp");
}
pdsp::Patchable& in_pitch(){
return in("pitch");
}
pdsp::Patchable& out_signal(){
return out("signal");
}
private:
pdsp::Amp amp;
pdsp::FMOperator osc;
pdsp::CRSlew ampSlew;
pdsp::CRSlew pitchSlew;
};
You can find also this lesson finished code in the lessons repo.