Created
November 7, 2012 03:18
-
-
Save admsyn/4029393 to your computer and use it in GitHub Desktop.
Sketching out some thoughts for an oF DSP chain
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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