Mercurial > hg > screen-ui
view src/melodyTriangle.cpp @ 29:10afc9afb79d
Changed default font to LucidaGrande.ttc as HelveticaLight not present on Lion;
fixed Makefile.
author | samer |
---|---|
date | Sun, 05 Feb 2012 19:05:30 +0000 |
parents | f4ebb87adec1 |
children | 9e8c19c90986 |
line wrap: on
line source
#include "melodyTriangle.h" #include <GLUT/GLUT.h> #define BUFFER_ZONE 64 // have to drag this far to snap out of triange. static int tempi[]={20,30,45,60,90,120,150,180}; melodyTriangle::melodyTriangle(const char *host, int port, int numVoices, bool enableKeys,int voiceIdOffset,int receivePort): numVoices(numVoices), enableKeys(enableKeys), receivePort(receivePort), display_msg(""), display_frames(0), ratio(2), tempoIndex(4) { printf("in constructor: %s %i %i %i %i %i\n", host,port,numVoices,enableKeys,voiceIdOffset,receivePort); for (int i=0;i<numVoices;i++) voices[i]=new Voice(i+1+voiceIdOffset); sender.setup( host,port ); receiver.setup( receivePort ); display_font.loadFont("/System/Library/Fonts/LucidaGrande.ttc",24); } melodyTriangle::~melodyTriangle() { printf("Deleting voice objects...\n"); for (int i=0;i<numVoices;i++) delete voices[i]; } //-------------------------------------------------------------- void melodyTriangle::setup(){ ofSetCircleResolution(64); ofBackground(0,0,0); ofSetWindowTitle("Melody Triangle"); ofSetFrameRate(40); // caps framerate if vertical sync is off. ofEnableSmoothing(); // Set up triange coordinates. // NB. whatever happens here, the triangle must be // isosceles and left-right symmetric around x=x1. // Otherwise the clipping won't work fitTriangleIn(ofGetWidth(),ofGetHeight()); send("/tempo",tempi[tempoIndex]); sendCalibrate(); sendReplyTo(); voiceGrabbed=NULL; } //-------------------------------------------------------------- void melodyTriangle::update(){ bool sendStart=false; while( receiver.hasWaitingMessages() ) { // get the next message ofxOscMessage m; receiver.getNextMessage( &m ); handleMessage(m); } constrained=false; if (voiceGrabbed!=NULL){ Voice *vg=voiceGrabbed; if (mouseX!=vg->posx || mouseY!=vg->posy){ int clipx=mouseX, clipy=mouseY; bool clipped=clipToTriangle(&clipx,&clipy); if (vg->inTriangle) { if (clipped) { // check how far we clipped if (ofDist(clipx, clipy, mouseX, mouseY) > BUFFER_ZONE) { // if far enough, we pop out of triangle and send send("/death",vg->id); vg->posx=mouseX; vg->posy=mouseY; vg->inTriangle=false; vg->status=Voice::pending; } else { // otherwise, we move to clipped point constrained=true; vg->posx=clipx; vg->posy=clipy; } } else { // not clipped; normal move vg->posx=mouseX; vg->posy=mouseY; } } else { // token was outside triangle vg->posx=mouseX; vg->posy=mouseY; if (!clipped){ // ie mouse now in triangle send("/birth",vg->id); printf("sent /birth %i \n",vg->id); sendOctave(vg->id,vg->octave); sendAmplitude(vg->id,vg->amplitude); sendStart=true; vg->inTriangle=true; vg->truex=vg->truey=-1; // ie not known yet. } } if (vg->inTriangle){ sendPosition(vg); vg->status=Voice::moved; if (sendStart && vg->isActive) send("/start",vg->id); } } }; } //-------------------------------------------------------------- void melodyTriangle::draw(){ ofSetLineWidth(2); ofSetColor(60,60,60); ofFill(); ofTriangle(x1, y1, x2, y2, x3, y3); // draw smooth edge, brighter if a token is constrained if (constrained) ofSetColor(255,96,96); ofNoFill(); ofTriangle(x1, y1, x2, y2, x3, y3); for (int i=numVoices-1; i>=0; i--){ voices[i]->draw(voices[i]->isInVoice(mouseX,mouseY)); } // display message if any if (display_frames!=0) { ofRectangle bbox=display_font.getStringBoundingBox(display_msg,0,0); ofSetColor(220,220,220); display_font.drawString(display_msg, (ofGetWidth()-bbox.width)/2, (ofGetHeight()-bbox.height)/2); if (display_frames>0) display_frames--; } if (ratio!=2) { ofSetColor(160,160,160); display_font.drawString(ofToString(ratio),16,ofGetHeight()-16); } } bool melodyTriangle::clipToTriangle(int *x, int *y) { bool clipped; if (*y>y2) { // off the bottom clipped=true; *y=y2; if (*x<x2) *x=x2; else if (*x>x3) *x=x3; } else { // have to be a bit cleverer bool reflect=false; if (*x<x1) { // work in reflected coordinates reflect=true; *x=2*x1-*x; } int dx=(*x-x1), dy=(*y-y1); // deltas from top if (dx*DY13 > dy*DX13) { // (x,y) must be somewhere right of triangle now clipped=true; int dp=dx*DX13 + dy*DY13; if (dp<0) { *x=x1; *y=y1; } // off the top else if (dp>SQLEN13) { *x=x3; *y=y3; } // off the bottom right else { // project onto right edge *x=x1+dp*DX13/SQLEN13; *y=y1+dp*DY13/SQLEN13; } } else { clipped=false; } if (reflect) *x=2*x1 - *x; // reflect back if necessary } return clipped; } //- Keyboard ---------------------------------------------------------- void melodyTriangle::keyReleased(int key){ if (enableKeys && key>='2' && key<='9') { ratio=2; } } void melodyTriangle::keyPressed(int key){ if (enableKeys){ if (key>='2' && key<='9') { ratio=key-'0'; } else { printf("got key: %d.\n",key); switch (key) { case '{': if (tempoIndex>0) tempoIndex--; send("/tempo",tempi[tempoIndex]); break; case '}': if (tempoIndex<7) tempoIndex++; send("/tempo",tempi[tempoIndex]); break; case ' ': send("/marker"); break; case 'S': send("/save"); break; case 'C': sendReplyTo(); sendCalibrate(); break; case 'F': ofToggleFullscreen(); break; case 'R': reset(); break; case 'Q': ofAppGlutWindow::exitApp(); default: // otherwise, send key to all voices under mouse for (int i=0; i<numVoices; i++) if (voices[i]->isInVoice(mouseX,mouseY)) voiceKeypress(voices[i],key); } } } else send("/key",key); } void melodyTriangle::voiceKeypress(Voice *v, int key) { switch (key) { case 'a': send(v->isActive ? "/stop" : "/start", v->id); v->isActive=!v->isActive; break; case OF_KEY_LEFT: sendShift(v->id,-1,ratio); break; case OF_KEY_RIGHT: sendShift(v->id,1,ratio); break; case OF_KEY_UP: sendPeriod(v->id,1,ratio); break; case OF_KEY_DOWN: sendPeriod(v->id,ratio,1); break; case ']': sendOctave(v->id, ++v->octave); break; case '[': sendOctave(v->id, --v->octave); break; case '*': sendAmplitude(v->id, v->louder()); break; case '/': sendAmplitude(v->id, v->quieter()); break; case 'c': send("/change",v->id); v->status=Voice::pending; break; default: printf("unrecognised key: %d.\n",key); } } //- Mouse ------------------------------------------------------ void melodyTriangle::mouseDragged(int x, int y, int button){} void melodyTriangle::mouseMoved(int x, int y ){} void melodyTriangle::mousePressed(int x, int y, int button){ // pick up first token under mouse for (int i=0; i<numVoices;i++){ if (voices[i]->isInVoice(x,y)) { voiceGrabbed=voices[i]; break; } } } void melodyTriangle::mouseReleased(int x, int y, int button){ if (voiceGrabbed!=NULL) { Voice *v=voiceGrabbed; if (v->status==Voice::clear) { v->posx=v->truex; v->posy=v->truey; v->truex=v->truey=-1; } voiceGrabbed=NULL; } } //-------------------------------------------------------------- void melodyTriangle::windowResized(int w, int h){ fitTriangleIn(w,h); sendCalibrate(); reset(); } void melodyTriangle::reset() { voiceGrabbed=NULL; for (int i=0;i<numVoices;i++) { Voice *v=voices[i]; v->posx=x2+RADIUS+(numVoices-1-i)*36; v->posy=48; v->status=Voice::pending; v->isActive=true; if (v->inTriangle) { send("/death",voices[i]->id); v->inTriangle=false; } } } // OSC Message handling ----------------------------------------- Voice *melodyTriangle::get_voice(int id) throw(bad_voice_id) { if (id<1 || id>numVoices) throw bad_voice_id(id); return voices[id-1]; } void melodyTriangle::handleMessage(ofxOscMessage &m) { string msg_path=m.getAddress(); try { if (msg_path.compare(0,8,"/notify/")==0) { string msg=msg_path.substr(8); Voice *v=get_voice(m.getArgAsInt32(0)); if (msg=="requested") v->status=Voice::waiting; else if (msg=="pending") v->status=Voice::pending; else if (msg=="received") { float x=m.getArgAsFloat(1); float y=m.getArgAsFloat(2); v->status=Voice::clear; if (voiceGrabbed==v) { v->truex=x; v->truey=y; } else { v->posx=x; v->posy=y; v->truex=v->truey=-1; } printf("True position of %d: %4.1f, %4.1f\n",v->id,v->truex,v->truey); } else if (msg=="position") { int x=(int)m.getArgAsFloat(1); int y=(int)m.getArgAsFloat(2); v->posx=x; v->posy=y; v->inTriangle=!clipToTriangle(&x,&y); if (voiceGrabbed==v) voiceGrabbed=NULL; } else if (msg=="running") { v->isActive = m.getArgAsInt32(1) ? true : false; } else if (msg=="params") { v->octave = m.getArgAsInt32(1); v->amplitude = m.getArgAsFloat(2); } } else if (msg_path=="/display") { display_msg=m.getArgAsString(0); display_frames=m.getArgAsInt32(1); } else if (msg_path=="/font") { display_font.loadFont(m.getArgAsString(0),m.getArgAsInt32(1)); } else if (msg_path=="/keyboard") { enableKeys=m.getArgAsInt32(0); } else if (msg_path=="/reset") { reset(); } else if (msg_path=="/fullscreen") { ofSetFullscreen(m.getArgAsInt32(0)); } else if (msg_path=="/quit") { ofAppGlutWindow::exitApp(); } else { cout << m.getAddress(); for (int i=0; i<m.getNumArgs(); i++) { cout << " " << m.getArgTypeName(i) << ":"; switch (m.getArgType(i)) { case OFXOSC_TYPE_INT32: cout << m.getArgAsInt32(i); case OFXOSC_TYPE_FLOAT: cout << m.getArgAsFloat(i); case OFXOSC_TYPE_STRING: cout << m.getArgAsString(i); default: cout << "unknown"; } } cout<< "\n"; } } catch (std::exception &ex) { cout << "** Error processing OSC message: " << ex.what() << "\n"; } } // OSC Message sending ----------------------------------------- void melodyTriangle::sendPosition(Voice *v){ ofxOscMessage m; ///track id x y left right top bottom area m.setAddress( "/track2d" ); m.addIntArg( v->id ); m.addIntArg( v->posx ); m.addIntArg( v->posy ); sender.sendMessage( m ); // printf("sent - /track2d %i %i %i\n",v->id,v->posx,v->posy); } void melodyTriangle::sendCalibrate(){ ofxOscMessage m; m.setAddress( "/calibrate" ); m.addIntArg( x1 ); m.addIntArg( y1 ); m.addIntArg( x2 ); m.addIntArg( y2 ); m.addIntArg( x3 ); m.addIntArg( y3 ); sender.sendMessage( m ); printf("sent /calibrate %i %i %i %i %i %i\n",x1,y1,x2,y2,x3,y3); } void melodyTriangle::sendReplyTo(){ ofxOscMessage m; m.setAddress( "/reply_to" ); m.addIntArg( receivePort ); sender.sendMessage( m ); printf("sent /reply_to %i\n",receivePort); } void melodyTriangle::sendPeriod(int id, int num, int den){ ofxOscMessage m; m.setAddress("/period"); m.addIntArg(id); m.addIntArg(num); m.addIntArg(den); sender.sendMessage(m); printf("sent /period %i %i %i\n",id,num,den); } void melodyTriangle::sendShift(int id, int num, int den){ ofxOscMessage m; m.setAddress("/shift"); m.addIntArg(id); m.addIntArg(num); m.addIntArg(den); sender.sendMessage(m); printf("sent /shift %i %i %i\n",id,num,den); } void melodyTriangle::sendOctave(int id, int oct){ ofxOscMessage m; m.setAddress("/octave"); m.addIntArg(id); m.addIntArg(oct); sender.sendMessage(m); printf("sent /octave %i %i\n",id,oct); } void melodyTriangle::sendAmplitude(int id, float amp){ ofxOscMessage m; m.setAddress("/amplitude"); m.addIntArg(id); m.addFloatArg(amp); sender.sendMessage(m); printf("sent /amplitude %i %1.3f\n",id,amp); } void melodyTriangle::send(const char *addr, int a) { ofxOscMessage m; m.setAddress(addr); m.addIntArg(a); sender.sendMessage(m); printf("sent %s %i\n",addr,a); } void melodyTriangle::send(const char *addr) { ofxOscMessage m; m.setAddress(addr); sender.sendMessage(m); printf("sent %s\n",addr); } void melodyTriangle::fitTriangleIn(int width, int height) { int triHeight = height*0.75; int triHalfWidth = triHeight/sqrt(3); x1=width/2; x2=x1-triHalfWidth; x3=x1+triHalfWidth; y1=(height-triHeight)/2; y2=y3=height - (height-triHeight)/2; // used for clipping DX13=x3-x1; DY13=y3-y1; SQLEN13=DX13*DX13+DY13*DY13; } const char *melodyTriangle::bad_voice_id::what() const throw() { std::stringstream out; printf("bad_voice_id(%d).\n",id); out << "Voice id " << id <<" out of range."; return out.str().c_str(); }