summaryrefslogtreecommitdiff
path: root/passadesgui/src
diff options
context:
space:
mode:
authorTim Redfern <tim@getdrop.com>2022-09-29 21:12:40 +0100
committerTim Redfern <tim@getdrop.com>2022-09-29 21:12:40 +0100
commita2c919f85c366e02cdc71f0fb46f4ebd4e4afebe (patch)
treeba1b88607e8de35b0b49683648d7f64ac8259fe1 /passadesgui/src
parent8c43a654fdc0a0fd822fd0bd9462cdadf6d96456 (diff)
revert to working single plugin chaos
Diffstat (limited to 'passadesgui/src')
-rw-r--r--passadesgui/src/AudioPlotter.cpp114
-rw-r--r--passadesgui/src/AudioPlotter.h57
-rw-r--r--passadesgui/src/lineSegmenter.cpp109
-rw-r--r--passadesgui/src/lineSegmenter.h48
-rw-r--r--passadesgui/src/lineTransformer.cpp126
-rw-r--r--passadesgui/src/lineTransformer.h21
-rw-r--r--passadesgui/src/main.cpp48
-rw-r--r--passadesgui/src/ofApp.cpp1294
-rw-r--r--passadesgui/src/ofApp.h243
9 files changed, 2060 insertions, 0 deletions
diff --git a/passadesgui/src/AudioPlotter.cpp b/passadesgui/src/AudioPlotter.cpp
new file mode 100644
index 0000000..7c775fa
--- /dev/null
+++ b/passadesgui/src/AudioPlotter.cpp
@@ -0,0 +1,114 @@
+#include "Audioplotter.h"
+
+colourPolyline Audioplotter::compute_chaos(colourPolyline& poly,float colourFade){
+ colourPolyline tempPoly;
+ for (int i=0;i<poly.size();i++){
+ REAL iv[6]={(poly[i].x-(ofGetWidth()/2))/chaosscale,(poly[i].y-(ofGetHeight()/2))/chaosscale,chaos_a,chaos_b,chaos_k,chaos_p};
+ //ofLog() << i<<": calculating chaos with: "<<poly[i].x<<"->"<<((poly[i].x-(ofGetWidth()/2))/chaosscale)<<" "<<poly[i].y<<"->"<<((poly[i].y-(ofGetHeight()/2))/chaosscale)<<" "<<attractor.a<<" "<<attractor.b<<" "<<attractor.k<<" "<<attractor.p;
+ attractor.set(iv);
+ attractor.calc();
+ //ofLog() << i<<": got points: "<<attractor.nx<<" "<<attractor.ny;
+ tempPoly.addVertex(ofPoint((attractor.nx*chaosscale)+(ofGetWidth()/2),(attractor.ny*chaosscale)+(ofGetHeight()/2)),poly.getColourAt(i)*colourFade);
+ }
+ return tempPoly;
+}
+vector <colourPolyline> Audioplotter::output(float plotscale,float plotdecay){
+
+ ofMatrix4x4 x = ofMatrix4x4::newIdentityMatrix();
+ x.translate((-ofGetWidth()/2)+((ofVec2f)translate).x,(-ofGetHeight()/2)+((ofVec2f)translate).y,0);
+ x.rotate(rotate,0,0,1);
+
+ x.scale(((ofVec2f)scale).x,((ofVec2f)scale).y,1.0f);
+
+ x.translate((ofGetWidth()/2),(ofGetHeight()/2),0);
+
+
+ ofMatrix4x4 flip = ofMatrix4x4::newIdentityMatrix();
+ flip.translate((-ofGetWidth()/2),(-ofGetHeight()/2),0);
+ flip.scale(-1.0f,1.0f,1.0f);
+ flip.translate((ofGetWidth()/2),(ofGetHeight()/2),0);
+ //destructive or non?
+ float fadefactor=plotdecay<0.0f?1.0f-(1.0f/history_size):plotdecay;
+
+ vector <colourPolyline> outdata;
+
+ for (int i=0;i<data.size();i++){
+ vector <colourPolyline> newdata;
+ for (int j=0;j<data[i].size();j++){
+ colourPolyline line=lineTransformer::polyLineTransform(x,data[i][j]); //,fadefactor);
+ line.setColour((line.getColourAt(0)*fadefactor)+(((ofColor)endColour)*(1.0f-fadefactor)));
+
+ if (usechaos) {
+ colourPolyline chaosline=compute_chaos(line); //,fadefactor);
+ line.mix(chaosline,chaosamount);
+ }
+
+
+ //ofLog() << "set colour to "<<col;
+ newdata.push_back(line);
+ outdata.push_back(line);
+
+ }
+ data[i]=newdata;
+ if (mirror){
+ for (int j=0;j<data[i].size();j++){
+ outdata.push_back(lineTransformer::polyLineTransform(flip,data[i][j]));
+ }
+ }
+ }
+ return outdata;
+}
+void Audioplotter::addpoints(vector <float> &audio){
+ int num=min((int)num_points,(int)audio.size());
+ float step=((float)audio.size())/(num+1);
+ vector <colourPolyline> newdata;
+ if (joined){
+ colourPolyline line;
+ if (bars&&num>1){
+ for (int i=0;i<num-1;i++){
+ line.addVertex(((step*(i+1))*ofGetWidth())/audio.size(),(0.5f+audio[step*(i+1)])*ofGetHeight(),startColour);
+ line.addVertex(((step*(i+2))*ofGetWidth())/audio.size(),(0.5f+audio[step*(i+1)])*ofGetHeight(),startColour);
+ line.addVertex(((step*(i+2))*ofGetWidth())/audio.size(),(0.5f+audio[step*(i+2)])*ofGetHeight(),startColour);
+ }
+ }
+ else {
+ for (int i=0;i<num;i++){
+ line.addVertex(((step*(i+1))*ofGetWidth())/audio.size(),(0.5f+audio[step*(i+1)])*ofGetHeight(),startColour);
+ }
+ }
+ newdata.push_back(line);
+ }
+ else{
+ for (int i=0;i<num;i++){
+ colourPolyline line;
+ if (random){
+ float in=ofRandom(0.0f,audio.size());
+ line.addVertex(((in*ofGetWidth())/audio.size())-(width/2),(0.5f+audio[in])*ofGetHeight(),startColour);
+ line.addVertex(((in*ofGetWidth())/audio.size())+(width/2),(0.5f+audio[in])*ofGetHeight(),startColour);
+ }
+ else {
+ line.addVertex(((step*(i+1))*ofGetWidth())/audio.size()-(width/2),(0.5f+audio[step*(i+1)])*ofGetHeight(),startColour);
+ line.addVertex(((step*(i+1))*ofGetWidth())/audio.size()+(width/2),(0.5f+audio[step*(i+1)])*ofGetHeight(),startColour);
+ }
+ newdata.push_back(line);
+ }
+ }
+ data.insert(data.begin(),newdata);
+ while (data.size()>history_size) {
+ data.pop_back();
+ }
+}
+void Audioplotter::blankframe(){
+ vector <colourPolyline> newdata;
+ data.insert(data.begin(),newdata);
+ while (data.size()>history_size) {
+ data.pop_back();
+ }
+}
+int Audioplotter::numpoints(){
+ int num=0;
+ for (auto d=data.begin();d!=data.end();d++){
+ num+=d->size();
+ }
+ return num;
+} \ No newline at end of file
diff --git a/passadesgui/src/AudioPlotter.h b/passadesgui/src/AudioPlotter.h
new file mode 100644
index 0000000..9cd37ae
--- /dev/null
+++ b/passadesgui/src/AudioPlotter.h
@@ -0,0 +1,57 @@
+#include "ofMain.h"
+#include "ofxAChaoslib.h"
+
+#include "lineTransformer.h"
+
+class Audioplotter{
+ //store and draw a numbr of audio samples
+ //how best to handle transforms - maybe pass in a transform to be added to 2nd and subsequent
+ //how best to handle length of history data - fixed number that can be set, or line budget?
+public:
+ Audioplotter(int _size=1,bool _joined=true,bool _bars=false,int _width=2){
+ setup(_size,_joined,_bars,_width);
+ }
+ void setup(int _size=1,bool _joined=true,bool _bars=false,int _width=2){
+ history_size=_size;
+ joined=_joined;
+ bars=_bars;
+ width=_width;
+ startColour=ofColor(255,255,255);
+ endColour=ofColor(0,0,0);
+ attractor.setup();
+ }
+ colourPolyline compute_chaos(colourPolyline& poly,float colourFade=1.0f);
+ vector <colourPolyline> output(float scale=1.0f,float decay=-1.0f);
+ void addpoints(vector <float> &audio);
+ void blankframe();
+ int numpoints();
+
+ ofParameter<bool> random;
+ ofParameter<bool> joined;
+ ofParameter<bool> bars;
+ ofParameter<bool> mirror;
+ ofParameter<int> width;
+ ofParameter<int> history_size;
+ ofParameter<int> num_points;
+
+//feedback transformation
+ ofParameter<ofVec2f> translate;
+ ofParameter<float> rotate;
+ ofParameter<ofVec2f> scale;
+
+ ofParameter<ofColor> startColour;
+ ofParameter<ofColor> endColour;
+
+ ofParameter<bool> usechaos;
+ ofParameter<float> chaosamount;
+ ofParameter<float> chaosscale;
+ ofParameter<float> chaos_a;
+ ofParameter<float> chaos_b;
+ ofParameter<float> chaos_k;
+ ofParameter<float> chaos_p;
+
+private:
+ vector < vector<colourPolyline>> data;
+ AChaosIkeda attractor;
+
+}; \ No newline at end of file
diff --git a/passadesgui/src/lineSegmenter.cpp b/passadesgui/src/lineSegmenter.cpp
new file mode 100644
index 0000000..3f434b1
--- /dev/null
+++ b/passadesgui/src/lineSegmenter.cpp
@@ -0,0 +1,109 @@
+ #include "lineSegmenter.h"
+
+const vector <ofPolyline> & lineSegmenter::getSegments(int num,float coverage, float phase){
+ //num - number of segments
+ //coverage - amount that each segment fills it's slot from 0-1
+ //phase - from 0-1
+
+ //if the path is closed, we can make a segment that crosses the end/beginning
+ //however we want to be able to deal with open paths
+
+/*
+
+segments 0...n - 1
+phase 0...1
+
+phase 0
+
+segment 0 is (0 -> coverage) / n
+segment n - 1 is ((0 -> coverage) + (n-1)) /n
+
+phase 1: has to be the loop target, it has to look identical
+
+segment 0 is (1 -> coverage) / n
+segment n - 1 is (1 - > coverage) + (n-1)
+
+*/
+
+
+ segments.clear();
+
+ for (int i=0;i<num;i++){
+ float startIndex=line.getIndexAtPercent((phase+i)/num); //always <1
+ float endPoint=(phase+i+coverage)/num; //can be >1
+ float endIndex=line.getIndexAtPercent(endPoint>1.0f?endPoint-1.0f:endPoint);
+ ofPolyline segment;
+ segment.addVertex(line.getPointAtIndexInterpolated(startIndex));
+ for (int j=(int)ceil(startIndex);j<(endPoint>1?line.size():(int)ceil(endIndex));j++){
+ segment.addVertex(line[j]);
+ }
+ if (endPoint>1){
+ segments.push_back(segment);
+ segment.clear();
+ for (int j=0;j<(int)ceil(endIndex);j++){
+ segment.addVertex(line[j]);
+ }
+ segment.addVertex(line.getPointAtIndexInterpolated(endIndex));
+ }
+ else {
+ segment.addVertex(line.getPointAtIndexInterpolated(endIndex));
+ }
+ segments.push_back(segment);
+ }
+
+ return segments;
+}
+
+void lineSegmenter::draw(){
+ line.draw();
+ return;
+}
+int lineSegmenter::size(){
+ return line.size();
+}
+
+const vector <colourPolyline> & colourLineSegmenter::getSegments(int num,float coverage, float phase){
+
+ segments.clear();
+
+ for (int i=0;i<num;i++){
+ float startIndex=line.getIndexAtPercent((phase+i)/num); //always <1
+ float endPoint=(phase+i+coverage)/num; //can be >1
+ float endIndex=line.getIndexAtPercent(endPoint>1.0f?endPoint-1.0f:endPoint);
+ colourPolyline segment;
+ segment.addVertex(line.getPointAtIndexInterpolated(startIndex),line.getColourAtIndexInterpolated(startIndex));
+ for (int j=(int)ceil(startIndex);j<(endPoint>1?line.size():(int)ceil(endIndex));j++){
+ segment.addVertex(line[j],line.getColourAt(j));
+ }
+ if (endPoint>1){
+ segments.push_back(segment);
+ segment.clear();
+ for (int j=0;j<(int)ceil(endIndex);j++){
+ segment.addVertex(line[j],line.getColourAt(j));
+ }
+ segment.addVertex(line.getPointAtIndexInterpolated(endIndex),line.getColourAtIndexInterpolated(endIndex));
+ }
+ else {
+ segment.addVertex(line.getPointAtIndexInterpolated(endIndex),line.getColourAtIndexInterpolated(endIndex) );
+ }
+ segments.push_back(segment);
+ }
+
+ return segments;
+}
+
+void colourLineSegmenter::draw(){
+ line.draw();
+ return;
+}
+int colourLineSegmenter::size(){
+ return line.size();
+}
+
+
+/*
+
+
+
+
+*/ \ No newline at end of file
diff --git a/passadesgui/src/lineSegmenter.h b/passadesgui/src/lineSegmenter.h
new file mode 100644
index 0000000..2467c6a
--- /dev/null
+++ b/passadesgui/src/lineSegmenter.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "ofMain.h"
+#include "colourPolyline.h"
+
+class lineSegmenter{
+ public:
+ lineSegmenter(ofPolyline &_line){
+ line=_line;
+ if (line.isClosed()){
+ line.addVertex(line[0]);
+ }
+ }
+ const vector <ofPolyline> &getSegments(int num,float coverage, float phase);
+ ofPolyline getPoly(){
+ return line;
+ }
+ void draw();
+ int size();
+ private:
+ ofPolyline line;
+ vector <ofPolyline> segments;
+};
+
+class colourLineSegmenter{
+ public:
+ colourLineSegmenter(colourPolyline &_line){
+ line=_line;
+ //if (line.isClosed()){
+ // line.addVertex(line[0]);
+ //}
+ }
+ colourLineSegmenter(ofPolyline &_line,const ofColor color=ofColor(255,255,255)){
+ line=colourPolyline(_line,color);
+ if (_line.isClosed()){
+ line.addVertex(line[0],line.getColourAt(0));
+ }
+ }
+ const vector <colourPolyline> &getSegments(int num,float coverage, float phase);
+ colourPolyline getPoly(){
+ return line;
+ }
+ void draw();
+ int size();
+ private:
+ colourPolyline line;
+ vector <colourPolyline> segments;
+}; \ No newline at end of file
diff --git a/passadesgui/src/lineTransformer.cpp b/passadesgui/src/lineTransformer.cpp
new file mode 100644
index 0000000..8e2bd80
--- /dev/null
+++ b/passadesgui/src/lineTransformer.cpp
@@ -0,0 +1,126 @@
+#include "lineTransformer.h"
+
+
+void lineTransformer::drawWarpFrame(glm::vec2 warpframe[4]){
+ ofSetColor(255,255,255);
+ ofNoFill();
+ for (int i=0;i<4;i++){
+ ofDrawCircle(warpframe[i],25);
+ ofDrawLine(warpframe[i],warpframe[(i+1)%4]);
+ }
+}
+
+void lineTransformer::gaussianElimination(float * input, int n)
+{
+ auto i = 0;
+ auto j = 0;
+ auto m = n - 1;
+
+ while (i < m && j < n)
+ {
+ auto iMax = i;
+ for (auto k = i + 1; k < m; ++k)
+ {
+ if (fabs(input[k * n + j]) > fabs(input[iMax * n + j]))
+ {
+ iMax = k;
+ }
+ }
+
+ if (input[iMax * n + j] != 0)
+ {
+ if (i != iMax)
+ {
+ for (auto k = 0; k < n; ++k)
+ {
+ auto ikIn = input[i * n + k];
+ input[i * n + k] = input[iMax * n + k];
+ input[iMax * n + k] = ikIn;
+ }
+ }
+
+ float ijIn = input[i * n + j];
+ for (auto k = 0; k < n; ++k)
+ {
+ input[i * n + k] /= ijIn;
+ }
+
+ for (auto u = i + 1; u < m; ++u)
+ {
+ auto ujIn = input[u * n + j];
+ for (auto k = 0; k < n; ++k)
+ {
+ input[u * n + k] -= ujIn * input[i * n + k];
+ }
+ }
+
+ ++i;
+ }
+ ++j;
+ }
+
+ for (auto i = m - 2; i >= 0; --i)
+ {
+ for (auto j = i + 1; j < n - 1; ++j)
+ {
+ input[i * n + m] -= input[i * n + j] * input[j * n + m];
+ }
+ }
+}
+
+glm::mat4 lineTransformer::getPerspectiveTransformMatrix(const glm::vec2 src[4], const glm::vec2 dst[4])
+{
+ float p[8][9] =
+ {
+ { -src[0][0], -src[0][1], -1, 0, 0, 0, src[0][0] * dst[0][0], src[0][1] * dst[0][0], -dst[0][0] }, // h11
+ { 0, 0, 0, -src[0][0], -src[0][1], -1, src[0][0] * dst[0][1], src[0][1] * dst[0][1], -dst[0][1] }, // h12
+ { -src[1][0], -src[1][1], -1, 0, 0, 0, src[1][0] * dst[1][0], src[1][1] * dst[1][0], -dst[1][0] }, // h13
+ { 0, 0, 0, -src[1][0], -src[1][1], -1, src[1][0] * dst[1][1], src[1][1] * dst[1][1], -dst[1][1] }, // h21
+ { -src[2][0], -src[2][1], -1, 0, 0, 0, src[2][0] * dst[2][0], src[2][1] * dst[2][0], -dst[2][0] }, // h22
+ { 0, 0, 0, -src[2][0], -src[2][1], -1, src[2][0] * dst[2][1], src[2][1] * dst[2][1], -dst[2][1] }, // h23
+ { -src[3][0], -src[3][1], -1, 0, 0, 0, src[3][0] * dst[3][0], src[3][1] * dst[3][0], -dst[3][0] }, // h31
+ { 0, 0, 0, -src[3][0], -src[3][1], -1, src[3][0] * dst[3][1], src[3][1] * dst[3][1], -dst[3][1] }, // h32
+ };
+
+ gaussianElimination(&p[0][0], 9);
+
+ return glm::mat4(p[0][8], p[3][8], 0, p[6][8],
+ p[1][8], p[4][8], 0, p[7][8],
+ 0, 0, 1, 0,
+ p[2][8], p[5][8], 0, 1);
+}
+
+ofPolyline lineTransformer::polyLineTransform(const ofMatrix4x4 xform, const ofPolyline& poly){
+ ofPolyline tempPoly;
+ for (auto& p:poly){
+ tempPoly.addVertex(ofVec3f(p)*xform);
+ }
+ return tempPoly;
+}
+
+colourPolyline lineTransformer::polyLineTransform(const ofMatrix4x4 xform,colourPolyline& poly,float colourFade){
+ colourPolyline tempPoly;
+ for (int i=0;i<poly.size();i++){
+ tempPoly.addVertex(ofVec3f(poly[i])*xform,poly.getColourAt(i)*colourFade);
+ }
+ return tempPoly;
+}
+
+ofPolyline lineTransformer::makePolygon(int num,float diam){
+ ofPolyline poly;
+ float step=PI*2/num;
+ for (int i=0;i<=num;i++){
+ poly.addVertex(cos(step*i)*diam,sin(step*i)*diam);
+ }
+ return poly;
+}
+
+void lineTransformer::drawPoly(ofPolyline poly,float x,float y){
+ glPushMatrix();
+ ofTranslate(x,y);
+ poly.draw();
+ for (int i=0;i<poly.size();i++){
+ ofDrawBitmapString(poly.getDegreesAtIndex(i),poly[i].x+10,poly[i].y+10,0);
+ }
+ glPopMatrix();
+}
diff --git a/passadesgui/src/lineTransformer.h b/passadesgui/src/lineTransformer.h
new file mode 100644
index 0000000..f5ec22f
--- /dev/null
+++ b/passadesgui/src/lineTransformer.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "ofMain.h"
+#include "colourPolyline.h"
+
+class lineTransformer {
+
+ public:
+ lineTransformer(){
+ }
+ void static drawWarpFrame(glm::vec2 warpframe[4]);
+ void static gaussianElimination(float * input, int n);
+ glm::mat4 static getPerspectiveTransformMatrix(const glm::vec2 src[4], const glm::vec2 dst[4]);
+ ofPolyline static polyLineTransform(const ofMatrix4x4 xform,const ofPolyline& poly);
+ ofPolyline static polyLineTransform(ofPoint (*transferFunction)(const ofPoint),const ofPolyline& poly);
+ colourPolyline static polyLineTransform(const ofMatrix4x4 xform,colourPolyline& poly,float colourFade=1.0f);
+ colourPolyline static polyLineTransform(ofPoint (*transferFunction)(const ofPoint),colourPolyline& poly,float colourFade=1.0f);
+ ofPolyline static makePolygon(int num,float diam);
+ void static drawPoly(ofPolyline poly,float x,float y);
+
+}; \ No newline at end of file
diff --git a/passadesgui/src/main.cpp b/passadesgui/src/main.cpp
new file mode 100644
index 0000000..b1a8365
--- /dev/null
+++ b/passadesgui/src/main.cpp
@@ -0,0 +1,48 @@
+#include "ofMain.h"
+#include "ofApp.h"
+
+
+//========================================================================
+int main(int argc, char *argv[]){
+
+
+ ofGLFWWindowSettings settings;
+
+
+
+ settings.decorated = true;
+
+ settings.setSize(1200,900);
+ settings.setPosition(ofVec2f(1700,0));
+ settings.resizable = false;
+
+ shared_ptr<ofAppBaseWindow> mainWindow = ofCreateWindow(settings);
+ mainWindow->setVerticalSync(false);
+
+ settings.setSize(700,1100);
+ settings.setPosition(ofVec2f(0,0));
+ settings.resizable = true;
+
+ // share OpenGL resources with other windows
+ settings.shareContextWith = mainWindow;
+
+ //settings.decorated = false; //doesn't suppress FS title bar
+ shared_ptr<ofAppBaseWindow> guiWindow = ofCreateWindow(settings);
+ guiWindow->setVerticalSync(false);
+
+ shared_ptr<ofApp> mainApp(new ofApp);
+ //mainApp->setupGui();
+
+ ofAddListener(mainWindow->events().update,mainApp.get(),&ofApp::updateOutput);
+ ofAddListener(mainWindow->events().draw,mainApp.get(),&ofApp::drawOutput);
+ ofAddListener(mainWindow->events().windowResized,mainApp.get(),&ofApp::outputWindowResized);
+ ofAddListener(mainWindow->events().keyPressed,mainApp.get(),&ofApp::outputKeyPressed);
+ ofAddListener(mainWindow->events().keyReleased,mainApp.get(),&ofApp::outputKeyReleased);
+ ofAddListener(mainWindow->events().mouseDragged,mainApp.get(),&ofApp::outputMouseDragged);
+ ofAddListener(mainWindow->events().mousePressed,mainApp.get(),&ofApp::outputMousePressed);
+ ofAddListener(mainWindow->events().mouseReleased,mainApp.get(),&ofApp::outputMouseReleased);
+
+ ofRunApp(guiWindow, mainApp);
+ ofRunMainLoop();
+}
+ \ No newline at end of file
diff --git a/passadesgui/src/ofApp.cpp b/passadesgui/src/ofApp.cpp
new file mode 100644
index 0000000..36e0785
--- /dev/null
+++ b/passadesgui/src/ofApp.cpp
@@ -0,0 +1,1294 @@
+#include "ofApp.h"
+#include "glew.h"
+
+const ofPoint outputWindowSize=ofPoint(1200,900);
+const float guiScale=560.0f/4096.0f;
+
+
+string sourcenames[5]={
+ "TEST",
+// "NDI",
+ "Player",
+ "SVG outlines",
+ "SVG segmenters",
+ "Audio"
+};
+
+//--------------------------------------------------------------
+void ofApp::setup(){
+
+ source=Audio;
+/*
+ //==================================================== ofxNDI
+
+ senderName[0] = 0; // The sender name used for display
+ nSenders = 0; // Total number of NDI senders
+ senderWidth = 0; // Sender width
+ senderHeight = 0; // Sender height
+ bNDIreceiver = false; // Receiver creation
+
+ // Create an intial receiving image
+ ndiImage.allocate(ofGetWidth(), ofGetHeight(), OF_IMAGE_COLOR_ALPHA);
+
+ // For received frame fps calculations - independent of the rendering rate
+ startTime = lastTime = frameTime = 0;
+ fps = frameRate = 1; // starting value
+*/
+
+ //============================= Audio
+
+ plotter.setup(5,true);
+
+ bufferSize = 2048;
+ sampleRate = 44100;
+
+ gist.setUseForOnsetDetection(GIST_PEAK_ENERGY);
+ gist.setThreshold(GIST_PEAK_ENERGY, .05);//
+
+ ofAddListener(GistEvent::ON,this,&ofApp::onNoteOn);
+ ofAddListener(GistEvent::OFF,this,&ofApp::onNoteOff);
+
+ // ofAddListener(ofApp::useMic->Value,this,&ofApp::useMicChanged);
+
+ soundStream.setup(this,0, 1, sampleRate, bufferSize, 1);
+
+ //loadSong("12 Ferric Appetite.aiff");
+ loadSong("passades/1 The Ninth Set-sector1:sector2pt.1.aiff");
+
+ //============================ gui
+
+ lasergui.setup("laser","",230,820);
+ lasergui.add(laser_power.set("power", false));
+ lasergui.add(laser_intensity.set("intensity", 30, 0, 255));
+ lasergui.add(laser_points.set("points", 30000, 0, 40000));
+ lasergui.add(laser_subdivide.set("subdivide", 15, 1, 100));
+ lasergui.add(laser_blank_num.set("blank points", 8, 0, 32));
+ lasergui.add(laser_max_angle.set("max angle", 15.0f, 1.0f, 90.0f));
+
+ responsegui.setup("audio response","",230,970);
+ responsegui.add(onset_threshold.set("onset threshold", 0.05f, 0.0f, 1.0f ));
+ responsegui.add(use_onset.set("trigger onset",false));
+ responsegui.add(onset_duration.set("duration", 10, 1, 100));
+
+ onset_frame=0;
+
+ chaosgui.setup("chaos","",460,820);
+ chaosgui.add(plotter.usechaos.set("use",false));
+ chaosgui.add(plotter.chaosamount.set("amount", 0.0f, -0.1f, 0.1f));
+ chaosgui.add(plotter.chaosscale.set("scale", 100.0f, 1.0f, 1000.0f));
+ chaosgui.add(plotter.chaos_a.set("a", 0.85f, 0.0f, 10.0f));
+ chaosgui.add(plotter.chaos_b.set("b", 0.9f, 0.0f, 10.0f));
+ chaosgui.add(plotter.chaos_k.set("k", 0.4f, 0.0f, 10.0f));
+ chaosgui.add(plotter.chaos_p.set("p", 7.7f, 0.0f, 10.0f));
+
+ drawgui.setup("drawing","",10,0);
+ drawgui.add(contour_threshold.setup("threshold", 140, 0, 255));
+ drawgui.add(contour_simplify.setup("simplify", 0.8, 0.0, 10.0));
+ drawgui.add(contour_useColour.setup("use colour", true));
+ drawgui.add(laser_R.setup("red", 140, 0, 255));
+ drawgui.add(laser_G.setup("green", 140, 0, 255));
+ drawgui.add(laser_B.setup("blue", 140, 0, 255));
+ drawgui.add(video_speed.setup("playback speed", 1.0, 0.0, 3.0));
+ drawgui.add(shapes_randomise.setup("randomise shapes", true));
+ drawgui.add(shapes_amount.setup("shapes amount", 0.2, 0.0, 0.8));
+ drawgui.add(shapes_duration.setup("shape duration", 5, 0, 25));
+ drawgui.add(use_mask.setup("use mask", true));
+ drawgui.add(invert_mask.setup("invert mask", false));
+ drawgui.add(use_segmenter.setup("use segmenter", false));
+ drawgui.add(segmenter_speed.setup("segmenter speed", 0.2, -1.0, 1.0));
+ drawgui.add(segmenter_length.setup("segmenter length", 0.2, 0.0, 1.0));
+ drawgui.add(segmenter_number.setup("segmenter number", 1, 1, 8));
+ drawgui.add(use_rotate.setup("XF rotate", false));
+ drawgui.add(xf_rotate.setup("rotate speed", 0.0, -1.0, 1.0));
+ drawgui.add(use_scale.setup("XF scale", false));
+ drawgui.add(xf_scale_speed.setup("scale speed", 1.0, 0.0, 10.0));
+ drawgui.add(xf_scale_min.setup("scale min", 1.0, 0.0, 3.0));
+ drawgui.add(xf_scale_max.setup("scale max", 2.0, 0.0, 3.0));
+
+ audiogui.setup("audio","",10,450);
+ audiogui.add(useMic.set("mic",false));
+ audiogui.add(useFft.set("fft",false));
+ audiogui.add(scalePlot.set("plotscale", 0.1f, 0.0f, 1.0f )); //parameters are recognised by name only?
+ audiogui.add(decayPlot.set("decay", 0.9f, 0.0f, 1.0f ));
+ audiogui.add(plotter.random.set("random",true));
+ audiogui.add(plotter.joined.set("joined",true));
+ audiogui.add(plotter.bars.set("bars",true));
+ audiogui.add(plotter.mirror.set("mirror",false));
+ audiogui.add(plotter.width.set("point width", 2, 1, 256));
+ audiogui.add(plotter.num_points.set("num points", 50, 1, 64));
+ audiogui.add(plotter.history_size.set("num plots", 5, 1, 64));
+ audiogui.add(plotter.translate.set("translate",ofVec2f(0,0),ofVec2f(-50,-50),ofVec2f(50,50)));
+ audiogui.add(plotter.rotate.set("rotate",0.0f,-10.0f,10.0f));
+ audiogui.add(plotter.scale.set("scale",ofVec2f(1.0f,1.0f),ofVec2f(0.5f,0.5f),ofVec2f(2.0f,2.0f)));
+ audiogui.add(plotter.startColour.set("start",ofColor(255,255,255)));
+ audiogui.add(plotter.endColour.set("end",ofColor(0,0,0)));
+
+ framecounter=0;
+
+ //============================ MIDI
+
+ midiIn.listInPorts();
+ midiIn.openPort(0);
+ midiIn.addListener(this);
+
+ //======================================= //positioning interface
+
+ safety_frame.addVertex(0,0); //etc
+
+ bShowPositionInterface=false;
+ bOutputSelected=false;
+
+ outputOffsetScale=1.0f;
+ commandPressed=false;
+
+
+ select_warpframe=-1;
+ bDrawFrame=false;
+
+ if( XML.loadFile("settings.xml") ){
+ cout << "settings.xml loaded!" <<std::endl;
+
+ }
+ else{
+ cout << "unable to load settings.xml"<<std::endl;
+ }
+
+ warpframe[0]=glm::vec2(
+ XML.getValue("WARP:p0:X", 0),
+ XML.getValue("WARP:p0:Y", 0)
+ );
+ warpframe[1]=glm::vec2(
+ XML.getValue("WARP:p1:X", outputWindowSize.x),
+ XML.getValue("WARP:p1:Y", 0)
+ );
+ warpframe[2]=glm::vec2(
+ XML.getValue("WARP:p2:X", outputWindowSize.x),
+ XML.getValue("WARP:p2:Y", outputWindowSize.y)
+ );
+ warpframe[3]=glm::vec2(
+ XML.getValue("WARP:p3:X", 0),
+ XML.getValue("WARP:p3:Y", outputWindowSize.y)
+ );
+
+ outputPosition=ofPoint(
+ XML.getValue("POSITION:X", 0),
+ XML.getValue("POSITION:Y", 0)
+ );
+
+ outputScale=XML.getValue("SCALE", 1.0f);
+
+}
+
+//====================== audio functions
+
+void ofApp::loadSong(string str){
+
+ cout<<"loadSong "<<str<<endl;
+
+ player.stop();
+ player.loadSound(str);
+ player.setLoop(true);
+ player.play();
+ useMic = 0;
+ gist.clearHistory();
+}
+
+void ofApp::onNoteOn(GistEvent &e){
+ //ofLog() << "<<NOTE ON>>";
+ onset_frame=0;
+ onset_number++;
+ //noteOnRadius = 100;
+};
+
+
+void ofApp::onNoteOff(GistEvent &e){
+ //ofLog() << "<<NOTE OFF>>";
+ //turn off?
+ //noteOnRadius = 0;
+};
+
+void ofApp::processAudio(float * input, int bufferSize, int nChannels){
+ //convert float array to vector
+
+
+ left.resize(bufferSize/nChannels);
+ right.resize(bufferSize/nChannels);
+ centre.resize(bufferSize/nChannels);
+
+ float max=0.0f;
+
+
+ if (nChannels==2){
+
+
+ for (int i = 0; i < bufferSize/nChannels; i++){
+ left[i] = input[i*nChannels];
+ right[i] = input[i*nChannels+1];
+ centre[i] = (left[i]+right[i])*0.5;
+ if (centre[i]>max){
+ max=centre[i];
+ }
+ }
+
+
+ }
+ else if (nChannels==1){
+ for (int i = 0; i < bufferSize;i++){
+ left[i] = input[i];
+ right[i] = input[i];
+ centre[i] = input[i];
+ if (centre[i]>max){
+ max=centre[i];
+ }
+ }
+ }
+
+ //ofLog()<<"audio data max "<<max;
+ //float audio data is +- 0.5
+
+
+ vector<float>buffer;
+ buffer.assign(&input[0],&input[bufferSize]);
+
+ gist.processAudio(buffer, bufferSize, nChannels,sampleRate);
+}
+
+
+void ofApp::audioIn(float * input, int bufferSize, int nChannels){
+ if(!useMic){
+ return;
+ }
+
+ //ofLog() << "processing "<<bufferSize<<" samples from mic";
+
+ processAudio(input, bufferSize, nChannels);
+
+}
+
+void ofApp::useMicChanged(){
+ if (useMic) {
+ player.stop();
+ }
+ else {
+ player.play();
+ }
+}
+
+//====================== settings
+
+void ofApp::default_settings(){
+ warpframe[0]=glm::vec2(0,0);
+ warpframe[1]=glm::vec2(outputWindowSize.x,0);
+ warpframe[2]=glm::vec2(outputWindowSize.x,outputWindowSize.y);
+ warpframe[3]=glm::vec2(0,outputWindowSize.y);
+ outputPosition=ofPoint(0,0);
+ outputScale=1.0f;
+}
+
+void ofApp::save_settings(){
+ XML.setValue("WARP:p0:X", warpframe[0].x);
+ XML.setValue("WARP:p0:Y", warpframe[0].y);
+ XML.setValue("WARP:p1:X", warpframe[1].x);
+ XML.setValue("WARP:p1:Y", warpframe[1].y);
+ XML.setValue("WARP:p2:X", warpframe[2].x);
+ XML.setValue("WARP:p2:Y", warpframe[2].y);
+ XML.setValue("WARP:p3:X", warpframe[3].x);
+ XML.setValue("WARP:p3:Y", warpframe[3].y);
+
+ XML.setValue("POSITION:X", outputPosition.x);
+ XML.setValue("POSITION:Y", outputPosition.y);
+
+ XML.setValue("SCALE", outputScale);
+
+ XML.saveFile("settings.xml");
+ cout << "settings.xml saved!" <<std::endl;
+}
+
+//--------------------------------------------------------------
+void ofApp::updateOutput(ofEventArgs & args){
+//todo: set up listener for laser params or move them into a lasergui class
+ laser.set_pts(laser_points);
+ laser.set_subdivide(laser_subdivide);
+ laser.set_blanknum(laser_blank_num);
+ laser.set_maxangle(laser_max_angle);
+
+ laser.set_intensity(laser_intensity);
+
+ movie.setSpeed(video_speed);
+
+ if(!useMic){
+ if(player.isLoaded()){
+ if (player.isPlaying()){
+ vector<float> output = player.getCurrentBuffer(bufferSize);
+ processAudio(&output[0], bufferSize, 2);
+ //
+ if (useFft){
+ fftSmoothed = player.getFFT();
+ /*
+ float * fft = ofSoundGetSpectrum(plotter.num_points);
+ fftSmoothed.resize(plotter.num_points);
+ //printf("fft: ");
+ for (int i=0;i<plotter.num_points;i++){
+ fftSmoothed[i]=fft[i];
+ //printf("%f,",fftSmoothed[i]);
+ }
+ //printf("\r\n");
+ */
+ plotter.addpoints(fftSmoothed);
+ }
+ else {
+ plotter.addpoints(centre);
+ }
+ }
+ else {
+ player.play();
+ }
+ }
+
+ }
+ else {
+ if (player.isPlaying()){
+ player.stop();
+ }
+ if (centre.size()){
+ if (useFft){
+ float * fft = ofSoundGetSpectrum(plotter.num_points);
+ fftSmoothed.resize(plotter.num_points);
+ printf("fft: ");
+ for (int i=0;i<plotter.num_points;i++){
+ fftSmoothed[i]=fft[i];
+ printf("%f,",fftSmoothed[i]);
+ }
+ printf("\r\n");
+ plotter.addpoints(fftSmoothed);
+ }
+ else {
+ if (!use_onset||onset_frame<onset_duration){
+ plotter.addpoints(centre);
+ }
+ else {
+ plotter.blankframe();
+ }
+ }
+ }
+ }
+
+}
+
+void ofApp::update(){
+/*
+ // Update the NDI sender list to find new senders
+ // There is no delay if no new senders are found
+ nSenders = ndiReceiver.FindSenders();
+
+ if(nSenders > 0) {
+
+ // Has the user changed the sender index ?
+ if(ndiReceiver.SenderSelected()) {
+ // Release the current receiver.
+ // A new one is then created from the selected sender index.
+ ndiReceiver.ReleaseReceiver();
+ bNDIreceiver = false;
+ }
+
+ // Create a new receiver if one does not exist.
+ // We don't know the sender dimensions until a frame is received.
+ if(!bNDIreceiver) {
+
+ // The receiver will detect which format a sender is using and convert
+ // the pixel buffer to BGRA for return. However, we can specify here
+ // that RGBA is the preferred format.
+ bNDIreceiver = ndiReceiver.CreateReceiver(NDIlib_recv_color_format_e_RGBX_RGBA);
+ // bNDIreceiver = ndiReceiver.CreateReceiver(); // default is BRRA
+
+ //
+ // A receiver is created from an index into a list of sender names.
+ // The current user selected index is saved in the NDIreceiver class
+ // and is used to create the receiver unless you specify a particular index.
+ //
+ // The name of the sender can also be retrieved if you need it.
+ // If you specified a particular sender index to create the receiver
+ // use that index to retrieve it's name.
+ //
+ // In this application we use it to display the sender name.
+ //
+
+ ndiReceiver.GetSenderName(senderName);
+ if(bNDIreceiver)
+ cout << "Created NDI receiver for " << senderName << endl;
+
+ // Reset the starting values for frame rate calulations
+ fps = frameRate = 1;
+
+ }
+
+ }
+
+ if(bNDIreceiver) {
+
+ unsigned int width = 0;
+ unsigned int height = 0;
+
+ // If the NDI sender uses BGRA format, the received buffer is converted to rgba by ReceiveImage.
+ // Optionally you can flip the image if necessary.
+
+ // !CHECKME MACOS
+ // ReceiveImage needs an unsigned char* so changed
+ // ndiImage.getPixels() to ndiImage.getPixels().getData()
+ // to avoid type error
+ if(ndiReceiver.ReceiveImage(ndiImage.getPixels().getData(), width, height, false)) { // receives as rgba
+
+ ndiImage.update();
+
+ // ----------------------------
+ // Calculate received frame fps
+ lastTime = startTime;
+ startTime = ofGetElapsedTimeMicros();
+ frameTime = (startTime - lastTime)/1000000; // seconds
+ if( frameTime > 0.01) {
+ frameRate = floor(1.0/frameTime + 0.5);
+ // damping from a starting fps value
+ fps *= 0.95;
+ fps += 0.05*frameRate;
+ }
+ // ----------------------------
+
+ // Have the NDI sender dimensions changed ?
+ if(senderWidth != width || senderHeight != height) {
+
+ // Update the sender dimensions
+ senderWidth = width;
+ senderHeight = height;
+
+ // Update the receiving image size
+ ndiImage.allocate(senderWidth, senderHeight, OF_IMAGE_COLOR_ALPHA);
+
+ }
+ }
+
+ }
+*/
+ if (movie.isLoaded()){
+ movie.update();
+ }
+
+ gist.setThreshold(GIST_PEAK_ENERGY,onset_threshold);
+
+ onset_frame++;
+}
+
+const ofPoint previewframesize=ofPoint(320,240);
+
+//-------------------------------------------------------------- GUI
+void ofApp::draw(){
+ ofBackground(0);
+
+ ofSetColor(255);
+ ofNoFill();
+
+ if (bShowPositionInterface){
+
+ glPushMatrix();
+
+ glTranslatef(20,20,0);
+
+ ofDrawRectangle(0,0,560,560);
+
+ glTranslatef(outputOffset.x,outputOffset.y,0);
+
+ glScalef(guiScale,guiScale,guiScale );
+
+ glTranslatef(2048.0f+outputPosition.x,2048.0f+outputPosition.y,0);
+
+ if (bOutputSelected) {
+ if (commandPressed) {
+ ofSetColor(0,255,0);
+ }
+ else {
+ ofSetColor(255,0,0);
+ }
+ }
+
+ ofDrawRectangle(
+ (-outputWindowSize.x/2)*outputScale*outputOffsetScale,
+ (-outputWindowSize.y/2)*outputScale*outputOffsetScale,
+ outputWindowSize.x*outputScale*outputOffsetScale,
+ outputWindowSize.y*outputScale*outputOffsetScale);
+
+ glPopMatrix();
+ }
+ else {
+ lasergui.draw();
+ drawgui.draw();
+ audiogui.draw();
+ chaosgui.draw();
+ responsegui.draw();
+
+ //================================== NDI
+/*
+ glPushMatrix();
+
+ glTranslatef(230,0,0);
+
+ ofDrawRectangle(20,20,previewframesize.x+4,previewframesize.y+4);
+
+ char str[256];
+
+ if(bNDIreceiver) {
+
+ ndiImage.draw(22, 22, previewframesize.x, previewframesize.y);
+
+ // Show fps etc.
+ if(nSenders > 0) {
+ if(bNDIreceiver) {
+ #ifdef _MSC_VER
+ sprintf_s(str, 256, "[%s] (%dx%d) - fps %2.0f", senderName, senderWidth, senderHeight, fps);
+ #else
+ // !CHECK MACOS
+ snprintf(str, 256, "[%s] (%dx%d) - fps %2.0f", senderName, senderWidth, senderHeight, fps);
+ str[255] = 0;
+ #endif
+ ofDrawBitmapString(str, 20, previewframesize.y+42);
+ }
+
+ if(nSenders == 1) {
+ ofDrawBitmapString("1 network source", 25, 32);
+ }
+ else {
+ #ifdef _MSC_VER
+ sprintf_s(str, 256, "%d network sources", nSenders);
+ #else
+ // !CHECK MACOS
+ snprintf(str, 256, "%d network sources", nSenders);
+ str[255] = 0;
+ #endif
+ ofDrawBitmapString(str, 25, 32);
+ //ofDrawBitmapString("'SPACE' to list senders or RH click to open sender dialog", 20, ofGetHeight()-20);
+ }
+ }
+ }
+ else {
+ ofDrawBitmapString("Connecting . . .", 25, 32);
+ }
+
+ glPopMatrix();
+
+ //================================== NDI
+
+ glPushMatrix();
+
+ glTranslatef(230,300,0);
+ */
+ //================================== video
+
+ glPushMatrix();
+
+ glTranslatef(230,0,0);
+
+ ofDrawRectangle(20,20,previewframesize.x+4,previewframesize.y+4);
+
+ if (movie.isLoaded()){
+ movie.draw(22, 22, previewframesize.x, previewframesize.y);
+ }
+
+ glPopMatrix();
+
+ //================================== SVG + mask
+
+ float scale=previewframesize.x/outputWindowSize.x;
+
+ //================================== SVG
+
+ glPushMatrix();
+
+
+ glTranslatef(230,270,0);
+
+ ofDrawRectangle(20,20,previewframesize.x+4,previewframesize.y+4);
+
+ glTranslatef(22,22,0);
+
+ glScalef(scale,scale,scale);
+
+ for (auto shape=segmenters.begin();shape!=segmenters.end();shape++){
+ shape->getPoly().draw();
+ }
+
+ glPopMatrix();
+
+ //================================== Mask
+
+ glPushMatrix();
+
+ glTranslatef(230,540,0);
+
+ ofDrawRectangle(20,20,previewframesize.x+4,previewframesize.y+4);
+
+ ofFill();
+
+ glTranslatef(22,22,0);
+
+ glScalef(scale,scale,scale);
+
+ for (auto& shape:mask){
+ shape.draw();
+ }
+
+ ofNoFill();
+
+ glPopMatrix();
+
+ }
+
+ ofSetColor(255);
+
+ ofDrawBitmapString(ofToString(onset_number)+":"+(onset_frame==0?"BEAT":ofToString(onset_frame)),10,ofGetHeight()-15);
+
+}
+
+void ofApp::drawOutput(ofEventArgs & args){
+ ofBackground(0);
+ //composite output window
+
+ ofSetColor(255,255,255);
+
+ vector <colourPolyline> polyOutput;
+
+ float interval=ofGetElapsedTimef()-prev_time;
+ rotate_amt+=interval*xf_rotate*5;
+
+ phase=fmod(phase+(interval*segmenter_speed),1);
+ prev_time=ofGetElapsedTimef();
+
+ while (phase<0.0f) {
+ phase+=1.0f;
+ }
+
+ scale_phase+=(interval*xf_scale_speed);
+
+ scale_amt=(((sin(scale_phase)*0.5)+0.5)*(xf_scale_max-xf_scale_min))+xf_scale_min;
+
+ switch (source){
+ case TEST:{
+ ofMatrix4x4 m = ofMatrix4x4::newIdentityMatrix();
+ m.rotateRad(ofGetElapsedTimef(),0,0,1);
+ m.translate(ofGetWidth()/2,ofGetHeight()/2,0);
+
+ glm::vec2 src[]={
+ glm::vec2(0,0),
+ glm::vec2(ofGetWidth(),0),
+ glm::vec2(ofGetWidth(),ofGetHeight()),
+ glm::vec2(0,ofGetHeight())
+ };
+
+ ofMatrix4x4 warp =lineTransformer::getPerspectiveTransformMatrix(src,warpframe);
+
+ //drawPoly(polyLineTransform(makePolygon(4,200),m),200,200);
+ //drawPoly(polyLineTransform(makePolygon(5,200),m),-200,200);
+ //drawPoly(polyLineTransform(makePolygon(6,200),m),-200,-200);
+
+ ofPolyline poly=lineTransformer::polyLineTransform(warp,
+ lineTransformer::polyLineTransform(m,
+ lineTransformer::makePolygon(6,200)
+ )
+ );
+ polyOutput.push_back(colourPolyline(poly,ofColor(laser_R,laser_G,laser_B)));
+
+ }
+/*
+ case NDI:{
+ ofPoint scale=ofPoint(outputWindowSize.x/ndiImage.getWidth(),outputWindowSize.x/ndiImage.getHeight());
+
+ //does not work no matter what the fuck you do
+
+ //grayImage.setFromPixels(pixels.getData(),ndiImage.getWidth(),ndiImage.getHeight());
+
+ //grayImage.draw(0,0,outputWindowSize.x,outputWindowSize.y);
+
+ grayImage = colorImg;
+
+ grayImage.threshold(contour_threshold);
+
+ //virtual int findContours( ofxCvGrayscaleImage& input,
+ // int minArea, int maxArea,
+ // int nConsidered, bool bFindHoles,
+ // bool bUseApproximation = true);
+ contourFinder.findContours(grayImage, 20, (340*240)/3, 10, true);
+
+ for (int i = 0; i < contourFinder.nBlobs; i++){
+ colourPolyline shape;
+ for (auto& point:contourFinder.blobs[i].pts){
+ ofVec3f p=point*scale;
+ ofColor c=colorImg.getPixels().getColor(point.x,point.y);
+ shape.addVertex(p,contour_useColour?c:ofColor(laser_R,laser_G,laser_B));
+ }
+ shape.simplify(contour_simplify);
+ polyOutput.push_back(shape);
+ }
+ break;
+
+ }
+*/
+ case Player:{
+ if (!use_onset||onset_frame<onset_duration){
+ if (movie.isLoaded()){
+ ofPoint scale=ofPoint(outputWindowSize.x/movie.getWidth(),outputWindowSize.y/movie.getHeight());
+ colorImg.setFromPixels(movie.getPixels());
+ if (grayImage.getWidth()!=colorImg.getWidth()||grayImage.getHeight()!=colorImg.getHeight()){
+ grayImage.clear();
+ }
+ grayImage=colorImg;
+ grayImage.threshold(contour_threshold);
+ contourFinder.findContours(grayImage, 20, (340*240)/3, 10, true);
+ for (int i = 0; i < contourFinder.nBlobs; i++){
+ colourPolyline shape;
+ for (auto& point:contourFinder.blobs[i].pts){
+ ofVec3f p=point*scale;
+ ofColor c=colorImg.getPixels().getColor(point.x,point.y);
+ shape.addVertex(p,contour_useColour?c:ofColor(laser_R,laser_G,laser_B));
+ }
+ shape.simplify(contour_simplify);
+ polyOutput.push_back(shape);
+ }
+ }
+ }
+ break;
+ }
+ case SVG_outlines:{
+ if (shapes_randomise){
+ if (framecounter==0){
+ select_random_shapes();
+ framecounter=shapes_duration;
+ }
+ for (auto s:shape_selection){
+ if (use_segmenter){
+ auto segments=segmenters[s].getSegments(segmenter_number,segmenter_length,phase);
+ for (auto segment=segments.begin();segment!=segments.end();segment++){
+ polyOutput.push_back(colourPolyline(*segment,ofColor(laser_R,laser_G,laser_B)));
+ }
+ }
+ else {
+ polyOutput.push_back(colourPolyline(segmenters[s].getPoly(),ofColor(laser_R,laser_G,laser_B)));
+ }
+ }
+ framecounter--;
+ }
+ else {
+ for (auto shape=segmenters.begin();shape!=segmenters.end();shape++){
+ if (use_segmenter){
+ auto segments=shape->getSegments(segmenter_number,segmenter_length,phase);
+ for (auto segment=segments.begin();segment!=segments.end();segment++){
+ polyOutput.push_back(colourPolyline(*segment,ofColor(laser_R,laser_G,laser_B)));
+ }
+ }
+ else {
+ polyOutput.push_back(colourPolyline(shape->getPoly(),ofColor(laser_R,laser_G,laser_B)));
+
+ }
+ }
+ }
+ break;
+ }
+ case Audio:{
+ ofMatrix4x4 x=ofMatrix4x4(1.0f,0.0f,0.0f,0.0f,
+ 0.0f,1.0f,0.0f,0.0f,
+ 0.0f,0.0f,1.0f,0.0f,
+ 0.0f,0.0f,0.0f,1.0f);
+ polyOutput=plotter.output(scalePlot,decayPlot);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ vector <colourPolyline> transformedOutput;
+
+ if (use_rotate||use_scale){
+ ofMatrix4x4 rm = ofMatrix4x4::newIdentityMatrix();
+ rm.translate(-outputWindowSize.x/2,-outputWindowSize.y/2,0);
+ if (use_rotate){
+ rm.rotateRad(rotate_amt,0,0,1);
+ }
+ if (use_scale){
+ rm.scale(scale_amt,scale_amt,scale_amt);
+ }
+ rm.translate(outputWindowSize.x/2,outputWindowSize.y/2,0);
+ for (auto& shape:polyOutput){
+ transformedOutput.push_back(lineTransformer::polyLineTransform(rm,shape));
+ }
+ }
+ else {
+ transformedOutput=polyOutput;
+ }
+
+ vector <colourPolyline> clippedOutput;
+
+ if (mask.size()&&use_mask){
+ clipper.Clear();
+ clipper.addPolylines(mask, ClipperLib::ptClip);
+ vector <ofPolyline> shapes; //TODO make clipper clip colourpolylines
+ for (auto& poly: transformedOutput)
+ {
+ shapes.push_back(poly);
+ }
+ clipper.addPolylines(shapes,ClipperLib::ptSubject);
+ vector <ofPolyline> clipped;
+ if (invert_mask){
+ clipped = clipper.getClippedLines(ClipperLib::ctDifference);
+ }else {
+ clipped = clipper.getClippedLines(ClipperLib::ctIntersection);
+ }
+ for (auto& clip: clipped)
+ {
+ clip.simplify(contour_simplify);
+ clippedOutput.push_back(colourPolyline(clip,ofColor(laser_R,laser_G,laser_B)));
+ }
+ }
+ else {
+ clippedOutput=transformedOutput;
+ }
+
+ glm::vec2 src[]={
+ glm::vec2(0,0),
+ glm::vec2(ofGetWidth(),0),
+ glm::vec2(ofGetWidth(),ofGetHeight()),
+ glm::vec2(0,ofGetHeight())
+ };
+
+ glm::vec2 mp=glm::vec2(outputWindowSize.x/2,outputWindowSize.y/2);
+
+ glm::vec2 scaled_dest[]={
+ ((warpframe[0]-mp)*outputScale*outputOffsetScale)+mp,
+ ((warpframe[1]-mp)*outputScale*outputOffsetScale)+mp,
+ ((warpframe[2]-mp)*outputScale*outputOffsetScale)+mp,
+ ((warpframe[3]-mp)*outputScale*outputOffsetScale)+mp
+ };
+
+ ofMatrix4x4 scaled_warp =lineTransformer::getPerspectiveTransformMatrix(src,scaled_dest);
+ ofMatrix4x4 warp =lineTransformer::getPerspectiveTransformMatrix(src,warpframe);
+
+ vector <colourPolyline> warpedOutput;
+ vector <colourPolyline> scaledWarpedOutput;
+
+ for (auto s:clippedOutput){
+ warpedOutput.push_back(lineTransformer::polyLineTransform(warp,s));
+ scaledWarpedOutput.push_back(lineTransformer::polyLineTransform(scaled_warp,s));
+ }
+
+ int num = 0;
+ int pnum=0;
+
+
+ if (laser_power&&polyOutput.size()) {
+ num=laser.draw(scaledWarpedOutput);
+ }
+ else {
+ colourPolyline blank;
+ for (int i=0;i<100;i++){
+ blank.addVertex(ofGetWidth()/2,ofGetHeight()/2,0,0,0);
+ }
+ laser.draw(blank);
+ }
+ for (auto& shape:warpedOutput){
+ shape.draw();
+ pnum+=shape.size();
+ }
+
+
+ if (bDrawFrame){
+ lineTransformer::drawWarpFrame(warpframe);
+ }
+
+ if (num>0){
+ ofSetWindowTitle(sourcenames[source]+": "+ofToString(ofGetFrameRate(), 2)+" fps laser points: "+ofToString(num));
+ }
+ else {
+
+ ofSetWindowTitle(sourcenames[source]+": "+ofToString(ofGetFrameRate(), 2)+" fps laser error points: "+ofToString(pnum));
+ }
+
+}
+
+//--------------------------------------------------------------
+void ofApp::exit() {
+ audiogui.clear();
+
+}
+
+
+
+//--------------------------------------------------------------
+void ofApp::outputKeyPressed(ofKeyEventArgs &args){
+
+ keyPressed(args);
+
+
+}
+
+void ofApp::keyPressed(ofKeyEventArgs &args){
+ if (args.key==OF_KEY_COMMAND){
+ commandPressed=true;
+ }
+
+ switch(args.key){
+ case '`':{
+ bShowPositionInterface=!bShowPositionInterface;
+ break;
+ }
+ case 'q':{
+ source--;
+ if (source<0){
+ source=Source_end-1;
+ }
+ break;
+ }
+ case 'p':{
+ source=(source+1)%Source_end;
+ break;
+ }
+ case 'w':{
+ bDrawFrame=!bDrawFrame;
+ break;
+ }
+ case 'd':{
+ default_settings();
+ break;
+ }
+ case 's':{
+ save_settings();
+ break;
+ }
+ }
+}
+
+//--------------------------------------------------------------
+void ofApp::outputKeyReleased(ofKeyEventArgs &args){
+
+ outputKeyReleased(args);
+
+
+}
+
+void ofApp::keyReleased(ofKeyEventArgs &args){
+ if (args.key==OF_KEY_COMMAND){
+ commandPressed=false;
+ }
+}
+
+
+//--------------------------------------------------------------
+void ofApp::mouseMoved(int x, int y ){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::outputMouseDragged(ofMouseEventArgs & args){
+ if (select_warpframe>-1){
+ warpframe[select_warpframe]=glm::vec2(args.x,args.y);
+ }
+}
+
+void ofApp::mouseDragged(int x, int y, int button){
+ if (bOutputSelected){
+ if (commandPressed){
+ float startDistance=((outputPosition*guiScale)+ofPoint(300,300)).distance(outputSelectionPoint);
+ float currentDistance=((outputPosition*guiScale)+ofPoint(300,300)).distance(ofPoint(x,y));
+ outputOffsetScale=currentDistance/startDistance;
+ }
+ else {
+ outputOffset=ofPoint(x,y)-outputSelectionPoint;
+ laser.set_centre(ofPoint(
+ outputPosition.x+(outputOffset.x/guiScale),
+ outputPosition.y+(outputOffset.y/guiScale)
+ ));
+ }
+ }
+}
+
+//--------------------------------------------------------------
+void ofApp::outputMousePressed(ofMouseEventArgs & args){
+ for (int i=0;i<4;i++){
+ if (ofPoint(args.x,args.y).distance(warpframe[i])<25){
+ select_warpframe=i;
+ }
+ }
+}
+
+void ofApp::mousePressed(int x, int y, int button){
+ if (bShowPositionInterface){
+ if (x>(300+((outputPosition.x-((outputWindowSize.x/2)*outputScale))*guiScale))&&
+ x<(300+((outputPosition.x+((outputWindowSize.x/2)*outputScale))*guiScale))&&
+ y>(300+((outputPosition.y-((outputWindowSize.y/2)*outputScale))*guiScale))&&
+ y<(300+((outputPosition.y+((outputWindowSize.y/2)*outputScale))*guiScale))
+ ){
+ outputSelectionPoint=ofPoint(x,y);
+ bOutputSelected=true;
+ }
+ }
+
+}
+
+//--------------------------------------------------------------
+void ofApp::outputMouseReleased(ofMouseEventArgs & args){
+ select_warpframe=-1;
+}
+
+void ofApp::mouseReleased(int x, int y, int button){
+ if (bOutputSelected){
+ if (commandPressed){
+ outputScale*=outputOffsetScale;
+ }
+ else {
+ outputPosition+=outputOffset/guiScale;
+ }
+ bOutputSelected=false;
+ outputOffset=ofPoint(0,0);
+ outputOffsetScale=1.0f;
+ }
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseEntered(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseExited(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::outputWindowResized(ofResizeEventArgs &resizeargs){
+
+}
+
+void ofApp::windowResized(int w, int h){
+
+}
+
+void ofApp::select_random_shapes(){
+ shape_selection.clear();
+ while (shape_selection.size()<(segmenters.size()*shapes_amount)){
+ int selection=rand()%segmenters.size();
+ if (shape_selection.find(selection)==shape_selection.end()){
+ shape_selection.insert(selection);
+ }
+ }
+ std::stringstream strm;
+ for (auto& s:shape_selection){
+ strm << s <<" ";
+ }
+ //cout << "randomly selected (" << strm.str() <<") \n";
+}
+
+//--------------------------------------------------------------
+void ofApp::dragEvent(ofDragInfo dragInfo){
+
+ if (dragInfo.position.x>250&&dragInfo.position.x<570&&dragInfo.position.y>20&&dragInfo.position.y<266){
+ std::string filename= *dragInfo.files.begin();
+ if (movie.load(filename)){
+ cout << "loaded "<< filename<<std::endl;
+ movie.setLoopState(OF_LOOP_NORMAL);
+ movie.setVolume(0.0f);
+ movie.play();
+ }
+ else {
+ cout << "failed to load"<<std::endl;
+ }
+ }
+
+ if (dragInfo.position.x>250&&dragInfo.position.x<570&&dragInfo.position.y>320&&dragInfo.position.y<566){
+ std::string filename= *dragInfo.files.begin();
+ svg.load(filename);
+ vector <ofPath> imagepaths= svg.getPaths();
+
+ std::stringstream strm;
+
+ if (imagepaths.size()){
+ segmenters.clear();
+ for (auto& path:imagepaths){
+ path.setPolyWindingMode(OF_POLY_WINDING_ODD);
+
+ vector <ofPolyline> outlines= path.getOutline();
+ for (auto& outline:outlines){
+ strm << outline.size() << "->";
+ outline.simplify(contour_simplify);
+ strm << outline.size() << " ";
+ segmenters.push_back(lineSegmenter(outline));
+ }
+ strm << " , ";
+ }
+
+ cout << "SVG: found " << imagepaths.size() << " paths with " << segmenters.size() << " shapes [ " << strm.str() << " ]" <<std::endl;
+ select_random_shapes();
+ }
+
+ }
+
+ if (dragInfo.position.x>250&&dragInfo.position.x<570&&dragInfo.position.y>620&&dragInfo.position.y<866){
+ std::string filename= *dragInfo.files.begin();
+ svg.load(filename);
+ vector <ofPath> imagepaths= svg.getPaths();
+
+ std::stringstream strm;
+
+ if (imagepaths.size()){
+ mask.clear();
+ for (auto& path:imagepaths){
+ path.setPolyWindingMode(OF_POLY_WINDING_ODD);
+
+ vector <ofPolyline> outlines= path.getOutline();
+ for (auto& outline:outlines){
+ strm << outline.size() << "->";
+ outline.simplify(contour_simplify);
+ strm << outline.size() << " ";
+ mask.push_back(outline);
+ }
+ strm << " , ";
+ }
+
+ cout << "Mask: found " << imagepaths.size() << " paths with " << mask.size() << " shapes [ " << strm.str() << " ]" <<std::endl;
+
+ }
+
+ }
+
+}
+
+void ofApp::newMidiMessage(ofxMidiMessage& msg) {
+ //column 0 for general controls
+ //printf("Midi: %i %i %i\n",msg.channel,msg.control,msg.value);
+
+ int offset;
+
+ //===============================================
+ offset=0;
+
+ if (msg.channel==1&&msg.control==1+offset){
+ //pot
+ xf_rotate=(((float)msg.value)/64.0f)-1.0f;
+ }
+ if (msg.channel==1&&msg.control==33+offset){
+ //pot button
+ use_rotate=use_rotate?false:true;
+ }
+ if (msg.channel==1&&msg.control==65+offset){
+ //top button
+ rotate_amt=ofRandom(5.0f);
+ }
+ if (msg.channel==1&&msg.control==73+offset){
+ //bottom button
+ }
+ if (msg.channel==1&&msg.control==81+offset){
+ //fader
+ laser_intensity=msg.value*2;
+ }
+
+ //===============================================
+ offset=1;
+
+ if (msg.channel==1&&msg.control==1+offset){
+ //pot
+ xf_scale_speed=(((float)msg.value)/128.0f)*10.0f;
+ }
+ if (msg.channel==1&&msg.control==33+offset){
+ //pot button
+ use_scale=use_scale?false:true;
+ }
+ if (msg.channel==1&&msg.control==65+offset){
+ //top button
+ scale_phase=ofRandom(5.0f);
+ }
+ if (msg.channel==1&&msg.control==73+offset){
+ //bottom button
+ }
+ if (msg.channel==1&&msg.control==81+offset){
+ //fader
+ laser_R=msg.value*2;
+ }
+
+ //===============================================
+ offset=2;
+
+ if (msg.channel==1&&msg.control==1+offset){
+ //pot
+ xf_scale_min=(((float)msg.value)/128.0f);
+ }
+ if (msg.channel==1&&msg.control==33+offset){
+ //pot button
+ }
+ if (msg.channel==1&&msg.control==65+offset){
+ //top button
+ }
+ if (msg.channel==1&&msg.control==73+offset){
+ //bottom button
+ }
+ if (msg.channel==1&&msg.control==81+offset){
+ //fader
+ laser_G=msg.value*2;
+ }
+
+ //===============================================
+ offset=3;
+
+ if (msg.channel==1&&msg.control==1+offset){
+ //pot
+ xf_scale_max=(((float)msg.value)/128.0f);
+ }
+ if (msg.channel==1&&msg.control==33+offset){
+ //pot button
+ }
+ if (msg.channel==1&&msg.control==65+offset){
+ //top button
+ }
+ if (msg.channel==1&&msg.control==73+offset){
+ //bottom button
+ }
+ if (msg.channel==1&&msg.control==81+offset){
+ //fader
+ laser_B=msg.value*2;
+ }
+
+ //===============================================
+ offset=4;
+
+ if (msg.channel==1&&msg.control==1+offset){
+ //pot
+ }
+ if (msg.channel==1&&msg.control==33+offset){
+ //pot button
+ }
+ if (msg.channel==1&&msg.control==65+offset){
+ //top button
+ }
+ if (msg.channel==1&&msg.control==73+offset){
+ //bottom button
+ }
+ if (msg.channel==1&&msg.control==81+offset){
+ //fader
+ video_speed=((float)msg.value)/40.0f;
+ }
+
+ //===============================================
+ offset=5;
+
+ if (msg.channel==1&&msg.control==1+offset){
+ //pot
+ }
+ if (msg.channel==1&&msg.control==33+offset){
+ //pot button
+ }
+ if (msg.channel==1&&msg.control==65+offset){
+ //top button
+ }
+ if (msg.channel==1&&msg.control==73+offset){
+ //bottom button
+ }
+ if (msg.channel==1&&msg.control==81+offset){
+ //fader
+ segmenter_speed=(((float)msg.value)/64.0f)-1.0f;
+ }
+}
+
diff --git a/passadesgui/src/ofApp.h b/passadesgui/src/ofApp.h
new file mode 100644
index 0000000..24ab46e
--- /dev/null
+++ b/passadesgui/src/ofApp.h
@@ -0,0 +1,243 @@
+ #pragma once
+
+#include "ofMain.h"
+
+//#include "ofxNDI.h"
+#include "ofxGui.h"
+#include "ofxXmlSettings.h"
+#include "ofxOpenCv.h"
+#include "ofxSVG.h"
+#include "ofxClipper.h"
+#include "ofxMidi.h"
+
+#include "ofxHelios.h"
+#include "lineTransformer.h"
+#include "lineSegmenter.h"
+
+#include "ofxGist.h"
+//This is included only as a way of getting buffer out of loaded sound.
+//There are many other ways you can do that.
+//This player includes a version of kissFFT. You can remove the one included in Gist.
+//https://github.com/borg/ofxOpenALSoundPlayer
+#include "ofxOpenALSoundPlayer.h"
+//Slightly modified to add a dynamic getVariable method to be able to plot based on
+//gist feature list
+//https://github.com/local-projects/ofxHistoryPlot
+#include "ofxHistoryPlot.h"
+#include "Audioplotter.h"
+
+enum Source{
+ TEST,
+// NDI,
+ Player,
+ SVG_outlines,
+ SVG_segmenters,
+ Audio,
+ Source_end
+};
+
+
+class ofApp: public ofBaseApp, public ofxMidiListener {
+
+ public:
+ void setup();
+ void update();
+ void draw();
+ void exit();
+
+ void keyPressed(ofKeyEventArgs &keyargs);
+ void keyReleased(ofKeyEventArgs & args);
+ void mouseMoved(int x, int y );
+ void mouseDragged(int x, int y, int button);
+ void mousePressed(int x, int y, int button);
+ void mouseReleased(int x, int y, int button);
+ void mouseEntered(int x, int y);
+ void mouseExited(int x, int y);
+ void windowResized(int w, int h);
+ void dragEvent(ofDragInfo dragInfo);
+
+ void newMidiMessage(ofxMidiMessage& eventArgs);
+
+ void updateOutput(ofEventArgs & args);
+ void drawOutput(ofEventArgs & args);
+ void outputKeyPressed(ofKeyEventArgs & args);
+ void outputKeyReleased(ofKeyEventArgs & args);
+ void outputMouseDragged(ofMouseEventArgs & args);
+ void outputMousePressed(ofMouseEventArgs & args);
+ void outputMouseReleased(ofMouseEventArgs & args);
+ void outputWindowResized(ofResizeEventArgs &resizeargs);
+
+ void select_random_shapes();
+
+ void default_settings();
+ void save_settings();
+
+ bool commandPressed;
+
+ glm::vec2 warpframe[4];
+ int select_warpframe;
+ bool bDrawFrame;
+
+
+ ofxHelios laser;
+
+ int source;
+
+ //======================================== //audio
+
+ ofxGist gist;
+ void onNoteOn(GistEvent &e);
+ void onNoteOff(GistEvent &e);
+
+ ofSoundStream soundStream;
+ ofxOpenALSoundPlayer player;
+
+ void processAudio(float * input, int bufferSize, int nChannels);
+ void audioIn(float * input, int bufferSize, int nChannels);
+
+ void loadSong(string str);
+
+ int bufferSize;
+ int sampleRate;
+
+ vector<float>fftSmoothed;
+ vector <float> left;
+ vector <float> right;
+ vector <float> centre;
+
+ Audioplotter plotter;
+
+ //======== audio gui
+
+ ofxPanel audiogui;
+ ofParameter<bool> useMic;
+ ofParameter<bool> useFft;
+ ofParameter<float> scalePlot;
+ ofParameter<float> decayPlot;
+
+ void useMicChanged();
+
+ //======= audio response gui
+
+ ofxPanel responsegui;
+ ofParameter<float> onset_threshold;
+ ofParameter<bool> use_onset;
+ ofParameter<int> onset_duration;
+
+ int onset_frame;
+ int onset_number;
+
+ //======== chaos gui
+
+ ofxPanel chaosgui;
+
+
+ //======================================== //thresholding
+
+ ofxCvColorImage colorImg;
+ ofxCvGrayscaleImage grayImage;
+ ofxCvContourFinder contourFinder;
+
+ //======================================== //ofxNDI
+/*
+ ofxNDIreceiver ndiReceiver;
+ ofImage ndiImage;
+ char senderName[256];
+ int nSenders;
+ unsigned int senderWidth;
+ unsigned int senderHeight;
+ bool bNDIreceiver;
+
+ // For received frame fps calculations
+ double startTime, lastTime, frameTime, frameRate, fps;
+*/
+ //====== drawing gui
+
+ ofxPanel drawgui;
+
+ ofxIntSlider contour_threshold;
+ ofxFloatSlider contour_simplify;
+ ofxToggle contour_useColour;
+ ofxIntSlider laser_R;
+ ofxIntSlider laser_G;
+ ofxIntSlider laser_B;
+
+ //====== video
+
+ ofxFloatSlider video_speed;
+
+ //svg gui
+ ofxToggle shapes_randomise;
+ ofxFloatSlider shapes_amount;
+ ofxIntSlider shapes_duration;
+ ofxToggle use_mask;
+ ofxToggle invert_mask;
+
+ //segmenter
+ ofxToggle use_segmenter;
+ ofxFloatSlider segmenter_speed;
+ ofxFloatSlider segmenter_length;
+ ofxIntSlider segmenter_number;
+
+ //transforms
+ ofxToggle use_rotate;
+ ofxFloatSlider xf_rotate;
+ ofxToggle use_scale;
+ ofxFloatSlider xf_scale_speed;
+ ofxFloatSlider xf_scale_min;
+ ofxFloatSlider xf_scale_max;
+
+
+ //======= laser gui
+
+ ofxPanel lasergui;
+ ofParameter<bool> laser_power;
+ ofParameter<int> laser_intensity;
+ ofParameter<int> laser_points;
+ ofParameter<int> laser_subdivide;
+ ofParameter<int> laser_blank_num;
+ ofParameter<float> laser_max_angle;
+
+ //======================================= //MIDI
+
+ ofxMidiIn midiIn;
+ ofxMidiMessage midiMessage;
+
+ //======================================= //video player
+
+ ofVideoPlayer movie;
+
+ //======================================= //SVG player
+
+ ofxSVG svg;
+ vector <lineSegmenter> segmenters;
+ set <int> shape_selection;
+ int framecounter;
+ float phase,prev_time; //to calculate phase
+ float rotate_amt;
+ float scale_phase,scale_amt;
+
+ //======================================= //Mask clipping
+
+ ofPolyline safety_frame;
+
+ vector <ofPolyline> mask;
+ ofx::Clipper clipper;
+
+ //======================================= //positioning interface
+
+ bool bShowPositionInterface;
+ bool bOutputSelected;
+ ofPoint outputSelectionPoint;
+ ofPoint outputPosition;
+ ofPoint outputOffset;
+ float outputScale;
+ float outputOffsetScale;
+
+ //======================================= //saving settings
+
+ ofxXmlSettings XML;
+
+
+
+};