Created
January 7, 2014 19:42
-
-
Save kylemcdonald/8305512 to your computer and use it in GitHub Desktop.
Simulation of synthesizer built from 256 perfect logic gates with 32 fixed inputs.
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 "ofMain.h" | |
// what happens when we can look back in time? i.e., add delays to io? | |
// this can be modeled with one pass-through-gate or two not-gates. | |
// or with a single and-gate with both inputs connect to the same output. | |
// what happens when the the circuit can control its own topology? | |
// this can be modeled with a very large circuit that switches between sub-circuits | |
// what happens when we use gates with more than two inputs? | |
// this can be modeled with a sub-circuit | |
// what happens when gates have floating point values with schmitt triggers? | |
// not sure that this can be modeled. | |
// how does distance from the inputs affect the randomness of the behavior? | |
// this can be evaluated much faster for large circuits on the GPU. | |
typedef bool (*Operation) (bool, bool); | |
bool bool_or(bool a, bool b) {return a || b;} | |
bool bool_nor(bool a, bool b) {return !(a || b);} | |
bool bool_and(bool a, bool b) {return a && b;} | |
bool bool_nand(bool a, bool b) {return !(a && b);} | |
bool bool_xor(bool a, bool b) {return a ^ b;} | |
bool bool_xnor(bool a, bool b) {return !(a ^ b);} | |
class HasOutput { | |
private: | |
bool output, bufferedOutput; | |
protected: | |
void bufferOutput(bool output) { | |
this->bufferedOutput = output; | |
} | |
void updateOutput() { | |
this->output = this->bufferedOutput; | |
} | |
public: | |
HasOutput() | |
:output(false) | |
,bufferedOutput(false) { | |
} | |
bool getOutput() const { | |
return output; | |
} | |
}; | |
class Input : public HasOutput { | |
public: | |
void setOutput(bool output) { | |
bufferOutput(output); | |
updateOutput(); | |
} | |
}; | |
class Gate : public HasOutput { | |
protected: | |
HasOutput *input0, *input1; | |
Operation operation; | |
public: | |
Gate() | |
:input0(NULL) | |
,input1(NULL) | |
,operation(NULL) { | |
} | |
void setOperation(Operation operation) { | |
this->operation = operation; | |
} | |
void setInput0(HasOutput& input) { | |
input0 = &input; | |
} | |
void setInput1(HasOutput& input) { | |
input1 = &input; | |
} | |
void evaluate() { | |
bufferOutput((*operation) (input0->getOutput(), input1->getOutput())); | |
} | |
void update() { | |
updateOutput(); | |
} | |
}; | |
template <class T> | |
T& randomElement(vector<T>& elements) { | |
return elements[ofRandom(0, elements.size())]; | |
} | |
int n = 256; | |
int downsample = 1; | |
float volume = .1; | |
vector<Input> inputs(32); | |
vector<Gate> gates(n); | |
vector<Operation> operations; | |
class ofApp : public ofBaseApp { | |
public: | |
bool needToReset = false; | |
ofImage raw, img; | |
void setup() { | |
raw.allocate(gates.size(), n, OF_IMAGE_GRAYSCALE); | |
raw.setColor(ofColor::black); | |
img.allocate(gates.size(), n, OF_IMAGE_GRAYSCALE); | |
img.setColor(ofColor::black); | |
operations.push_back(&bool_or); | |
operations.push_back(&bool_nor); | |
operations.push_back(&bool_and); | |
operations.push_back(&bool_nand); | |
operations.push_back(&bool_xor); | |
operations.push_back(&bool_xnor); | |
reset(); | |
ofSoundStreamSetup(2, 0, 44100, n, 4); | |
} | |
void reset() { | |
// randomly connect all the gates | |
for(int i = 0; i < n; i++) { | |
gates[i] = Gate(); | |
gates[i].setOperation(randomElement(operations)); | |
gates[i].setInput0(randomElement(gates)); | |
gates[i].setInput1(randomElement(gates)); | |
} | |
// connect and set all the inputs | |
for(int i = 0; i < inputs.size(); i++) { | |
inputs[i].setOutput(ofRandom(1) > .5); | |
randomElement(gates).setInput0(inputs[i]); | |
} | |
} | |
void update() { | |
img.update(); | |
raw.update(); | |
} | |
void draw() { | |
raw.draw(0, 0); | |
img.draw(n, 0); | |
} | |
void keyPressed(int key) { | |
if(key == ' ') { | |
needToReset = true; | |
} | |
} | |
void audioOut(float* input, int bufferSize, int nChannels) { | |
if(needToReset) { | |
reset(); | |
needToReset = false; | |
} | |
int sqn = sqrt(n); | |
int mx = ofClamp(mouseX, 0, n - 1) / sqn, my = ofClamp(mouseY, 0, n - 1) / sqn; | |
int request = my * sqn + mx; | |
int totalIterations = bufferSize / downsample; | |
int duplicates = nChannels * downsample; | |
for(int i = 0; i < totalIterations; i++) { | |
// evaluate all gates | |
for(int j = 0; j < n; j++) { | |
gates[j].evaluate(); | |
} | |
// update all gates | |
for(int j = 0; j < n; j++) { | |
gates[j].update(); | |
} | |
// save state to image | |
for(int j = 0; j < n; j++) { | |
int xs = i % sqn, ys = i / sqn; | |
int xb = j % sqn, yb = j / sqn; | |
int x = xb * sqn + xs, y = yb * sqn + ys; | |
raw.setColor(x, y, gates[j].getOutput() ? ofColor::white : ofColor::black); | |
img.setColor(i, j, gates[j].getOutput() ? ofColor::white : ofColor::black); | |
} | |
float result = gates[request].getOutput() ? +volume : -volume; | |
for(int j = 0; j < duplicates; j++) { | |
input[i * duplicates + j] = result; | |
} | |
} | |
} | |
}; | |
int main() { | |
ofSetupOpenGL(2 * n, n, OF_WINDOW); | |
ofRunApp(new ofApp()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment