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@32: #define NUM_TEMPI 9 samer@22: samer@32: static int tempi[]={20,30,45,60,90,120,150,180,240}; samer@32: samer@37: static ofxOscBundle& operator<<(ofxOscBundle &bundle, ofxOscMessage msg) { samer@37: bundle.addMessage(msg); return bundle; samer@37: } samer@37: samer@37: static ofxOscSender& operator<<(ofxOscSender &sender, ofxOscMessage msg) { samer@37: sender.sendMessage(msg); samer@37: return sender; samer@37: } samer@37: samer@37: static ofxOscSender& operator<<(ofxOscSender &sender, ofxOscBundle bundle) { samer@37: sender.sendBundle(bundle); samer@37: return sender; samer@37: } samer@37: 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@37: enableKeys(enableKeys), randInit(false),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@37: ofxOscBundle bundle; 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@37: bundle << msg("/death",vg->id); samer@37: sender << bundle; 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@37: bundle << msg("/birth",vg->id) samer@37: << msgAmplitude(vg->id,vg->amplitude) samer@37: << ( randInit samer@37: ? msg("/randinit",vg->id) samer@37: : msgOctave(vg->id,vg->octave) samer@37: ); 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@37: bundle << msgPosition(vg); samer@18: vg->status=Voice::moved; samer@37: if (sendStart && vg->isActive) bundle << msg("/start",vg->id); samer@37: sender << bundle; 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@32: voices[i]->draw(enableKeys && 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@37: sender << msg("/tempo",tempi[tempoIndex]); samer@25: break; samer@25: case '}': samer@32: if (tempoIndexisInVoice(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@37: else sender << msg("/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@32: if (v->inTriangle) { samer@37: sender << msg(v->isActive ? "/stop" : "/start", v->id); samer@32: } samer@23: v->isActive=!v->isActive; samer@23: break; samer@37: case OF_KEY_LEFT: sender << msgShift(v->id,-1,ratio); break; samer@37: case OF_KEY_RIGHT: sender << msgShift(v->id,1,ratio); break; samer@37: case OF_KEY_UP: sender << msgPeriod(v->id,1,ratio); break; samer@37: case OF_KEY_DOWN: sender << msgPeriod(v->id,ratio,1); break; samer@37: case ']': sender << msgOctave(v->id, ++v->octave); break; samer@37: case '[': sender << msgOctave(v->id, --v->octave); break; samer@37: case '*': sender << msgAmplitude(v->id, v->louder()); break; samer@37: case '/': sender << msgAmplitude(v->id, v->quieter()); break; samer@37: case 'c': sender << msg("/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@37: sender << msgCalibrate(); 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@37: sender << msg("/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@32: cout << "Received " << msg_path << "\n"; 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@32: } else if (msg=="visible") { samer@32: v->isVisible = m.getArgAsInt32(1); samer@32: if (voiceGrabbed==v && !v->isVisible) samer@32: voiceGrabbed=NULL; 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@37: // printf("/track2d %i %i %i\n",v->id,v->posx,v->posy); samer@37: return m; samer@22: } samer@37: samer@37: ofxOscMessage melodyTriangle::msgCalibrate(){ 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@37: printf("/calibrate %i %i %i %i %i %i\n",x1,y1,x2,y2,x3,y3); samer@37: return m; samer@22: } samer@22: samer@37: ofxOscMessage melodyTriangle::msgReplyTo(){ samer@22: ofxOscMessage m; samer@22: m.setAddress( "/reply_to" ); samer@22: m.addIntArg( receivePort ); samer@22: printf("sent /reply_to %i\n",receivePort); samer@37: return m; samer@22: } samer@22: samer@37: ofxOscMessage melodyTriangle::msgPeriod(int id, int num, int den) { return msg("/period",id,num,den); } samer@37: ofxOscMessage melodyTriangle::msgShift(int id, int num, int den) { return msg("/shift",id,num,den); } samer@37: ofxOscMessage melodyTriangle::msgOctave(int id, int oct) { return msg("/octave",id,oct); } samer@22: samer@37: ofxOscMessage melodyTriangle::msgAmplitude(int id, float amp){ samer@22: ofxOscMessage m; samer@22: m.setAddress("/amplitude"); samer@22: m.addIntArg(id); samer@22: m.addFloatArg(amp); samer@37: printf("/amplitude %i %1.3f\n",id,amp); samer@37: return m; samer@22: } samer@22: samer@37: ofxOscMessage melodyTriangle::msg(const char *addr, int a, int b, int c) { samer@32: ofxOscMessage m; samer@32: m.setAddress(addr); samer@32: m.addIntArg(a); samer@32: m.addIntArg(b); samer@32: m.addIntArg(c); samer@37: printf("%s %i %i %i\n",addr,a,b,c); samer@37: return m; samer@32: } samer@32: samer@37: ofxOscMessage melodyTriangle::msg(const char *addr, int a, int b) { samer@32: ofxOscMessage m; samer@32: m.setAddress(addr); samer@32: m.addIntArg(a); samer@32: m.addIntArg(b); samer@37: printf("%s %i %i\n",addr,a,b); samer@37: return m; samer@32: } samer@32: samer@37: ofxOscMessage melodyTriangle::msg(const char *addr, int a) { samer@25: ofxOscMessage m; samer@25: m.setAddress(addr); samer@25: m.addIntArg(a); samer@37: printf("%s %i\n",addr,a); samer@37: return m; samer@25: } samer@25: samer@37: ofxOscMessage melodyTriangle::msg(const char *addr) { samer@25: ofxOscMessage m; samer@25: m.setAddress(addr); samer@37: printf("%s\n",addr); samer@37: return m; 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: }