hekeus@6: #include "melodyTriangle.h" hekeus@6: #include hekeus@6: samer@23: #define BUFFER_ZONE 64 // have to drag this far to snap out of triange. samer@22: samer@25: static int tempi[]={20,30,45,60,90,120,150,180}; samer@25: samer@22: melodyTriangle::melodyTriangle(const char *host, int port, int numVoices, samer@22: bool enableKeys,int voiceIdOffset,int receivePort): samer@31: numVoices(numVoices), receivePort(receivePort), snapTruePos(enableKeys), samer@30: enableKeys(enableKeys), allowExit(true), ratio(2), tempoIndex(4), samer@30: display_msg(""), display_frames(0) samer@22: { samer@22: for (int i=0;iposx || mouseY!=vg->posy){ samer@15: int clipx=mouseX, clipy=mouseY; samer@15: bool clipped=clipToTriangle(&clipx,&clipy); samer@15: samer@15: if (vg->inTriangle) { hekeus@6: samer@15: if (clipped) { samer@15: // check how far we clipped samer@30: if (ofDist(clipx, clipy, mouseX, mouseY)>BUFFER_ZONE && allowExit) { samer@15: // if far enough, we pop out of triangle and send samer@25: send("/death",vg->id); samer@15: vg->posx=mouseX; samer@15: vg->posy=mouseY; samer@15: vg->inTriangle=false; samer@23: vg->status=Voice::pending; samer@15: } else { samer@15: // otherwise, we move to clipped point samer@15: constrained=true; samer@15: vg->posx=clipx; samer@15: vg->posy=clipy; samer@15: } samer@15: } else { // not clipped; normal move samer@15: vg->posx=mouseX; samer@15: vg->posy=mouseY; samer@15: } samer@15: } else { // token was outside triangle samer@15: vg->posx=mouseX; samer@15: vg->posy=mouseY; samer@15: if (!clipped){ // ie mouse now in triangle samer@25: send("/birth",vg->id); samer@15: samer@15: printf("sent /birth %i \n",vg->id); samer@15: sendOctave(vg->id,vg->octave); samer@15: sendAmplitude(vg->id,vg->amplitude); samer@15: sendStart=true; samer@15: vg->inTriangle=true; samer@25: vg->truex=vg->truey=-1; // ie not known yet. hekeus@6: } hekeus@6: } hekeus@6: samer@14: if (vg->inTriangle){ samer@23: sendPosition(vg); samer@18: vg->status=Voice::moved; samer@25: if (sendStart && vg->isActive) send("/start",vg->id); hekeus@6: } hekeus@6: } hekeus@6: }; samer@22: } samer@10: samer@22: samer@22: samer@22: //-------------------------------------------------------------- samer@22: void melodyTriangle::draw(){ samer@11: ofSetLineWidth(2); samer@23: ofSetColor(60,60,60); samer@10: ofFill(); samer@10: ofTriangle(x1, y1, x2, y2, x3, y3); samer@10: samer@10: // draw smooth edge, brighter if a token is constrained samer@23: if (constrained) ofSetColor(255,96,96); samer@10: ofNoFill(); samer@10: ofTriangle(x1, y1, x2, y2, x3, y3); samer@10: samer@25: for (int i=numVoices-1; i>=0; i--){ samer@23: voices[i]->draw(voices[i]->isInVoice(mouseX,mouseY)); hekeus@6: } hekeus@6: samer@22: // display message if any samer@23: if (display_frames!=0) { samer@23: ofRectangle bbox=display_font.getStringBoundingBox(display_msg,0,0); samer@22: ofSetColor(220,220,220); samer@23: display_font.drawString(display_msg, samer@23: (ofGetWidth()-bbox.width)/2, (ofGetHeight()-bbox.height)/2); samer@23: if (display_frames>0) display_frames--; samer@22: } samer@25: if (ratio!=2) { samer@25: ofSetColor(160,160,160); samer@25: display_font.drawString(ofToString(ratio),16,ofGetHeight()-16); samer@25: } hekeus@6: } hekeus@6: samer@22: bool melodyTriangle::clipToTriangle(int *x, int *y) { samer@22: bool clipped; samer@22: samer@22: if (*y>y2) { // off the bottom samer@22: clipped=true; samer@22: *y=y2; samer@22: if (*xx3) *x=x3; samer@22: } else { // have to be a bit cleverer samer@22: bool reflect=false; samer@22: if (*x dy*DX13) { samer@22: // (x,y) must be somewhere right of triangle now samer@22: clipped=true; samer@22: int dp=dx*DX13 + dy*DY13; samer@22: if (dp<0) { *x=x1; *y=y1; } // off the top samer@22: else if (dp>SQLEN13) { *x=x3; *y=y3; } // off the bottom right samer@22: else { // project onto right edge samer@22: *x=x1+dp*DX13/SQLEN13; samer@22: *y=y1+dp*DY13/SQLEN13; samer@22: } samer@22: } else { samer@22: clipped=false; samer@22: } samer@22: samer@22: if (reflect) *x=2*x1 - *x; // reflect back if necessary samer@22: } samer@22: return clipped; samer@22: } samer@22: samer@22: samer@22: samer@22: //- Keyboard ---------------------------------------------------------- samer@22: samer@25: void melodyTriangle::keyReleased(int key){ samer@25: if (enableKeys && key>='2' && key<='9') { samer@25: ratio=2; samer@25: } samer@25: } samer@23: void melodyTriangle::keyPressed(int key){ hekeus@6: if (enableKeys){ samer@25: if (key>='2' && key<='9') { samer@25: ratio=key-'0'; samer@25: } else { samer@25: printf("got key: %d.\n",key); samer@25: switch (key) { samer@25: samer@25: case '{': samer@25: if (tempoIndex>0) tempoIndex--; samer@25: send("/tempo",tempi[tempoIndex]); samer@25: break; samer@25: case '}': samer@25: if (tempoIndex<7) tempoIndex++; samer@25: send("/tempo",tempi[tempoIndex]); samer@25: break; samer@25: samer@25: case ' ': send("/marker"); break; samer@25: case 'S': send("/save"); break; samer@31: case 'r': send("/report"); break; samer@25: case 'C': sendReplyTo(); sendCalibrate(); break; samer@25: case 'F': ofToggleFullscreen(); break; samer@25: case 'R': reset(); break; samer@25: case 'Q': ofAppGlutWindow::exitApp(); samer@30: case OF_KEY_ESC: setKeyboardEnable(false); break; samer@25: samer@25: default: // otherwise, send key to all voices under mouse samer@25: for (int i=0; iisInVoice(mouseX,mouseY)) samer@25: voiceKeypress(voices[i],key); samer@12: } hekeus@6: } samer@30: } else { samer@30: if (key==OF_KEY_ESC) setKeyboardEnable(true); samer@30: else send("/key",key); samer@30: } samer@23: } samer@23: samer@23: void melodyTriangle::voiceKeypress(Voice *v, int key) { samer@23: switch (key) { samer@25: case 'a': samer@25: send(v->isActive ? "/stop" : "/start", v->id); samer@23: v->isActive=!v->isActive; samer@23: break; samer@25: case OF_KEY_LEFT: sendShift(v->id,-1,ratio); break; samer@25: case OF_KEY_RIGHT: sendShift(v->id,1,ratio); break; samer@25: case OF_KEY_UP: sendPeriod(v->id,1,ratio); break; samer@25: case OF_KEY_DOWN: sendPeriod(v->id,ratio,1); break; samer@25: case ']': sendOctave(v->id, ++v->octave); break; samer@25: case '[': sendOctave(v->id, --v->octave); break; samer@23: case '*': sendAmplitude(v->id, v->louder()); break; samer@23: case '/': sendAmplitude(v->id, v->quieter()); break; samer@25: case 'c': send("/change",v->id); v->status=Voice::pending; break; samer@23: default: printf("unrecognised key: %d.\n",key); hekeus@6: } hekeus@6: } hekeus@6: samer@22: //- Mouse ------------------------------------------------------ hekeus@6: samer@22: void melodyTriangle::mouseDragged(int x, int y, int button){} samer@23: void melodyTriangle::mouseMoved(int x, int y ){} samer@23: void melodyTriangle::mousePressed(int x, int y, int button){ samer@25: // pick up first token under mouse hekeus@6: for (int i=0; iisInVoice(x,y)) { samer@25: voiceGrabbed=voices[i]; samer@25: break; samer@25: } hekeus@6: } hekeus@6: } hekeus@6: samer@22: void melodyTriangle::mouseReleased(int x, int y, int button){ samer@25: if (voiceGrabbed!=NULL) { samer@25: Voice *v=voiceGrabbed; samer@31: if (v->status==Voice::clear && v->truex>=0) { samer@25: v->posx=v->truex; samer@25: v->posy=v->truey; samer@25: v->truex=v->truey=-1; samer@25: } samer@25: voiceGrabbed=NULL; samer@25: } hekeus@6: } hekeus@6: hekeus@6: //-------------------------------------------------------------- samer@22: hekeus@6: void melodyTriangle::windowResized(int w, int h){ samer@22: fitTriangleIn(w,h); samer@22: sendCalibrate(); samer@23: reset(); samer@22: } hekeus@6: samer@23: void melodyTriangle::reset() { samer@25: voiceGrabbed=NULL; samer@23: for (int i=0;iposx=x2+RADIUS+(numVoices-1-i)*36; samer@23: v->posy=48; samer@23: v->status=Voice::pending; samer@25: v->isActive=true; samer@23: if (v->inTriangle) { samer@30: send("/death",v->id); samer@23: v->inTriangle=false; samer@23: } samer@23: } samer@23: } samer@22: samer@22: // OSC Message handling ----------------------------------------- samer@22: samer@23: Voice *melodyTriangle::get_voice(int id) throw(bad_voice_id) { samer@30: for (int i=0; iid==id) return voices[i]; samer@30: } samer@30: throw bad_voice_id(id); samer@23: } samer@23: samer@22: void melodyTriangle::handleMessage(ofxOscMessage &m) { samer@22: string msg_path=m.getAddress(); samer@23: samer@23: try { samer@23: if (msg_path.compare(0,8,"/notify/")==0) { samer@25: string msg=msg_path.substr(8); samer@23: Voice *v=get_voice(m.getArgAsInt32(0)); samer@25: samer@25: if (msg=="requested") v->status=Voice::waiting; samer@25: else if (msg=="pending") v->status=Voice::pending; samer@25: else if (msg=="received") { samer@25: float x=m.getArgAsFloat(1); samer@25: float y=m.getArgAsFloat(2); samer@25: v->status=Voice::clear; samer@31: if (snapTruePos) { samer@31: if (voiceGrabbed==v) { samer@31: v->truex=x; samer@31: v->truey=y; samer@31: } else { samer@31: v->posx=x; samer@31: v->posy=y; samer@31: v->truex=v->truey=-1; samer@31: } samer@25: } samer@31: printf("True position of %d: %4.1f, %4.1f\n",v->id,x,y); samer@25: } else if (msg=="position") { samer@23: int x=(int)m.getArgAsFloat(1); samer@23: int y=(int)m.getArgAsFloat(2); samer@23: v->posx=x; samer@23: v->posy=y; samer@23: v->inTriangle=!clipToTriangle(&x,&y); samer@31: v->status=Voice::clear; samer@25: if (voiceGrabbed==v) voiceGrabbed=NULL; samer@25: } else if (msg=="running") { samer@23: v->isActive = m.getArgAsInt32(1) ? true : false; samer@25: } else if (msg=="params") { samer@23: v->octave = m.getArgAsInt32(1); samer@23: v->amplitude = m.getArgAsFloat(2); samer@23: } samer@23: } else if (msg_path=="/display") { samer@23: display_msg=m.getArgAsString(0); samer@23: display_frames=m.getArgAsInt32(1); samer@23: } else if (msg_path=="/font") { samer@23: display_font.loadFont(m.getArgAsString(0),m.getArgAsInt32(1)); samer@30: } else if (msg_path=="/keyboard") { setKeyboardEnable(m.getArgAsInt32(0)); } samer@23: else if (msg_path=="/reset") { reset(); } samer@23: else if (msg_path=="/fullscreen") { ofSetFullscreen(m.getArgAsInt32(0)); } samer@23: else if (msg_path=="/quit") { ofAppGlutWindow::exitApp(); } samer@30: else if (msg_path=="/allowExit") { allowExit=m.getArgAsInt32(0); } samer@23: else { samer@23: cout << m.getAddress(); samer@23: for (int i=0; iid ); samer@23: m.addIntArg( v->posx ); samer@23: m.addIntArg( v->posy ); samer@22: sender.sendMessage( m ); samer@25: // printf("sent - /track2d %i %i %i\n",v->id,v->posx,v->posy); samer@22: } samer@22: void melodyTriangle::sendCalibrate(){ samer@22: ofxOscMessage m; samer@22: m.setAddress( "/calibrate" ); samer@22: m.addIntArg( x1 ); samer@22: m.addIntArg( y1 ); samer@22: m.addIntArg( x2 ); samer@22: m.addIntArg( y2 ); samer@22: m.addIntArg( x3 ); samer@22: m.addIntArg( y3 ); samer@22: sender.sendMessage( m ); samer@22: printf("sent /calibrate %i %i %i %i %i %i\n",x1,y1,x2,y2,x3,y3); samer@22: } samer@22: samer@22: void melodyTriangle::sendReplyTo(){ samer@22: ofxOscMessage m; samer@22: m.setAddress( "/reply_to" ); samer@22: m.addIntArg( receivePort ); samer@22: sender.sendMessage( m ); samer@22: printf("sent /reply_to %i\n",receivePort); samer@22: } samer@22: samer@22: void melodyTriangle::sendPeriod(int id, int num, int den){ samer@22: ofxOscMessage m; samer@22: m.setAddress("/period"); samer@22: m.addIntArg(id); samer@22: m.addIntArg(num); samer@22: m.addIntArg(den); samer@22: sender.sendMessage(m); samer@22: printf("sent /period %i %i %i\n",id,num,den); samer@22: } samer@22: samer@22: void melodyTriangle::sendShift(int id, int num, int den){ samer@22: ofxOscMessage m; samer@22: m.setAddress("/shift"); samer@22: m.addIntArg(id); samer@22: m.addIntArg(num); samer@22: m.addIntArg(den); samer@22: sender.sendMessage(m); samer@22: printf("sent /shift %i %i %i\n",id,num,den); samer@22: } samer@22: samer@22: void melodyTriangle::sendOctave(int id, int oct){ samer@22: ofxOscMessage m; samer@22: m.setAddress("/octave"); samer@22: m.addIntArg(id); samer@22: m.addIntArg(oct); samer@22: sender.sendMessage(m); samer@22: printf("sent /octave %i %i\n",id,oct); samer@22: } samer@22: samer@22: void melodyTriangle::sendAmplitude(int id, float amp){ samer@22: ofxOscMessage m; samer@22: m.setAddress("/amplitude"); samer@22: m.addIntArg(id); samer@22: m.addFloatArg(amp); samer@22: sender.sendMessage(m); samer@22: printf("sent /amplitude %i %1.3f\n",id,amp); samer@22: } samer@22: samer@25: void melodyTriangle::send(const char *addr, int a) { samer@25: ofxOscMessage m; samer@25: m.setAddress(addr); samer@25: m.addIntArg(a); samer@25: sender.sendMessage(m); samer@25: printf("sent %s %i\n",addr,a); samer@25: } samer@25: samer@25: void melodyTriangle::send(const char *addr) { samer@25: ofxOscMessage m; samer@25: m.setAddress(addr); samer@25: sender.sendMessage(m); samer@25: printf("sent %s\n",addr); samer@25: } samer@22: samer@22: void melodyTriangle::fitTriangleIn(int width, int height) { samer@22: int triHeight = height*0.75; samer@22: int triHalfWidth = triHeight/sqrt(3); samer@22: samer@22: x1=width/2; samer@22: x2=x1-triHalfWidth; samer@22: x3=x1+triHalfWidth; samer@22: y1=(height-triHeight)/2; samer@22: y2=y3=height - (height-triHeight)/2; samer@22: samer@22: // used for clipping samer@22: DX13=x3-x1; DY13=y3-y1; samer@22: SQLEN13=DX13*DX13+DY13*DY13; samer@22: } samer@23: samer@23: const char *melodyTriangle::bad_voice_id::what() const throw() { samer@23: std::stringstream out; samer@23: printf("bad_voice_id(%d).\n",id); samer@30: out << "Voice id " << id <<" not present."; samer@23: return out.str().c_str(); samer@23: }