Skip to content

Instantly share code, notes, and snippets.

@admsyn
Created November 7, 2012 03:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save admsyn/4029393 to your computer and use it in GitHub Desktop.
Save admsyn/4029393 to your computer and use it in GitHub Desktop.
Sketching out some thoughts for an oF DSP chain
#include <vector>
#include <math.h>
#include <iostream>
using namespace std;
// some shortcuts to make this sketch simpler
typedef vector<float> ofSoundBuffer;
size_t bufferSize = 16;
#pragma mark Base Classes
// - - - - - - -
// Base class for everything in the DSP chain
class ofSoundNode {
protected:
virtual bool setSource(ofSoundNode &source) {
return false;
}
virtual void handleDisconnect(ofSoundNode &source) {
destination = NULL;
}
ofSoundNode * destination;
public:
ofSoundNode()
: destination(NULL) { }
virtual void renderIntoBuffer(ofSoundBuffer &outBuffer) {
fill(outBuffer.begin(), outBuffer.end(), 0);
}
virtual bool connectTo(ofSoundNode &newDestination) {
bool connectionSuccess = newDestination.setSource(*this);
if(connectionSuccess) {
if(destination) {
destination->handleDisconnect(*this);
}
destination = &newDestination;
}
return connectionSuccess;
}
}
ofGlobalSilentSoundNode; // <- Global instance which acts as a "no source" node / sentinel value
// - - - - - - -
// Shortcut subclass for a node which produces sound and has no source
class ofSoundGenerator : public ofSoundNode {
protected:
virtual void produceSound(ofSoundBuffer &outBuffer) = 0;
public:
void renderIntoBuffer(ofSoundBuffer &outBuffer) {
produceSound(outBuffer);
}
};
// - - - - - - -
// Shortcut subclass for a node which takes in sound, processes it, and sends it down the chain
class ofSoundEffect : public ofSoundNode {
protected:
virtual void processSound(const ofSoundBuffer &inBuffer, ofSoundBuffer &outBuffer) = 0;
bool setSource(ofSoundNode &source) {
this->source = &source;
return true;
}
ofSoundNode * source;
public:
ofSoundEffect()
: source(&ofGlobalSilentSoundNode) { }
void renderIntoBuffer(ofSoundBuffer &outBuffer) {
if(source) {
source->renderIntoBuffer(outBuffer);
}
processSound(outBuffer, outBuffer);
}
};
#pragma mark - Example Implementations
// - - - - - - -
// sine wave generator
class SineSynth : public ofSoundGenerator {
double phase;
protected:
void produceSound(ofSoundBuffer &outBuffer) {
for(int i = 0; i < outBuffer.size(); i++) {
outBuffer[i] = sin(phase);
phase += 0.5;
}
}
};
// - - - - - - -
// "tremolo" effect (sets every other other sample to 0)
class Tremolo : public ofSoundEffect {
long sampleCounter;
protected:
void processSound(const ofSoundBuffer &inBuffer, ofSoundBuffer &outBuffer) {
size_t bufferSize = min(inBuffer.size(), outBuffer.size());
for(int i = 0; i < bufferSize; i++) {
outBuffer[i] = (sampleCounter % 2) ? inBuffer[i] : 0;
sampleCounter++;
}
}
};
// - - - - - - -
// "amplifier" effect (multiplies every sample by 0.5)
class Amplifier : public ofSoundEffect {
protected:
void processSound(const ofSoundBuffer &inBuffer, ofSoundBuffer &outBuffer) {
size_t bufferSize = min(inBuffer.size(), outBuffer.size());
for(int i = 0; i < bufferSize; i++) {
outBuffer[i] = inBuffer[i] * 0.5;
}
}
};
// - - - - - - -
// 8 channel simple additive mixer, handles its own connections
class Mixer : public ofSoundNode {
ofSoundNode * sources[8];
ofSoundBuffer buffers[8];
protected:
bool setSource(ofSoundNode & source) {
cout << "Mixer : use addSourceOnBus() instead of connectTo()" << endl;
return false;
}
void handleDisconnect(ofSoundNode & source) {
for(int i = 0; i < 8; i++) {
if(sources[i] == &source) {
sources[i] = NULL;
return;
}
}
}
public:
Mixer() {
for(int i = 0; i < 8; i++) {
sources[i] = NULL;
buffers[i] = ofSoundBuffer(bufferSize);
}
}
void addSourceOnBus(ofSoundNode & source, unsigned bus) {
source.connectTo(ofGlobalSilentSoundNode);
sources[bus] = &source;
}
void renderIntoBuffer(ofSoundBuffer &outBuffer) {
for(int i = 0; i < 8; i++) {
if(sources[i]) {
sources[i]->renderIntoBuffer(buffers[i]);
}
}
for(int i = 0; i < bufferSize; i++) {
float totalSample = 0;
for(int j = 0; j < 8; j++) {
totalSample += buffers[j][i];
}
outBuffer[i] = totalSample;
}
}
};
#pragma mark - Example Program
int main(int argc, const char * argv[]) {
// [A] simple example
// setting up a chain and rendering through it
ofSoundBuffer bufferA(bufferSize);
SineSynth synth;
Tremolo tremolo;
Amplifier amp;
// this sets up an audio path of synth -> tremolo -> amp
synth.connectTo(tremolo);
tremolo.connectTo(amp);
// causes amp to pull from tremolo, and tremolo to pull from synth
amp.renderIntoBuffer(bufferA);
printf("[A] : ");
for(int i = 0; i < bufferA.size(); i++) {
printf("%+.1f ", bufferA[i]);
}
printf("\n");
// [B] auto disconnect
// connecting synth straight to amp, disconnecting from tremolo
// in the process
synth.connectTo(amp);
ofSoundBuffer bufferB(bufferSize);
amp.renderIntoBuffer(bufferB);
printf("[B] : ");
for(int i = 0; i < bufferB.size(); i++) {
printf("%+.1f ", bufferB[i]);
}
printf("\n");
// [C] mixer
// demonstrating mixer using its own semantics for connections,
// in order to make things less ambiguous
Mixer mixer;
amp.connectTo(mixer); // <- generates a warning message, doesn't do anything else
mixer.addSourceOnBus(amp, 2); // <- actually makes the connection
ofSoundBuffer bufferC(bufferSize);
mixer.renderIntoBuffer(bufferC);
printf("[C] : ");
for(int i = 0; i < bufferC.size(); i++) {
printf("%+.1f ", bufferC[i]);
}
return 0;
}
[A] : +0.0 +0.2 +0.0 +0.5 +0.0 +0.3 +0.0 -0.2 +0.0 -0.5 +0.0 -0.4 +0.0 +0.1 +0.0 +0.5
[B] : +0.5 +0.4 +0.2 -0.0 -0.3 -0.4 -0.5 -0.4 -0.3 -0.0 +0.2 +0.4 +0.5 +0.5 +0.3 +0.1
Mixer : use addSourceOnBus() instead of connectTo()
[C] : -0.1 -0.4 -0.5 -0.5 -0.4 -0.2 +0.1 +0.3 +0.5 +0.5 +0.4 +0.2 -0.0 -0.2 -0.4 -0.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment