annotate src/melodyTriangle.cpp @ 32:06a2fdb333ca

Added visibilty control for tokens; removed token highlight when keyboard disabled.
author samer
date Wed, 15 Feb 2012 10:14:04 +0000
parents 417deb31dd4e
children 260cc4f4d70a
rev   line source
hekeus@6 1 #include "melodyTriangle.h"
hekeus@6 2 #include <GLUT/GLUT.h>
hekeus@6 3
samer@23 4 #define BUFFER_ZONE 64 // have to drag this far to snap out of triange.
samer@32 5 #define NUM_TEMPI 9
samer@22 6
samer@32 7 static int tempi[]={20,30,45,60,90,120,150,180,240};
samer@32 8
samer@25 9
samer@22 10 melodyTriangle::melodyTriangle(const char *host, int port, int numVoices,
samer@22 11 bool enableKeys,int voiceIdOffset,int receivePort):
samer@31 12 numVoices(numVoices), receivePort(receivePort), snapTruePos(enableKeys),
samer@30 13 enableKeys(enableKeys), allowExit(true), ratio(2), tempoIndex(4),
samer@30 14 display_msg(""), display_frames(0)
samer@22 15 {
samer@22 16 for (int i=0;i<numVoices;i++) voices[i]=new Voice(i+1+voiceIdOffset);
samer@22 17
hekeus@6 18 sender.setup( host,port );
hekeus@8 19 receiver.setup( receivePort );
samer@29 20 display_font.loadFont("/System/Library/Fonts/LucidaGrande.ttc",24);
hekeus@6 21 }
hekeus@6 22
samer@22 23 melodyTriangle::~melodyTriangle() {
samer@22 24 printf("Deleting voice objects...\n");
samer@22 25 for (int i=0;i<numVoices;i++) delete voices[i];
samer@22 26 }
samer@22 27
hekeus@6 28 //--------------------------------------------------------------
hekeus@6 29 void melodyTriangle::setup(){
samer@22 30 ofSetCircleResolution(64);
hekeus@6 31 ofBackground(0,0,0);
hekeus@6 32 ofSetWindowTitle("Melody Triangle");
samer@23 33 ofSetFrameRate(40); // caps framerate if vertical sync is off.
samer@18 34 ofEnableSmoothing();
samer@18 35
samer@18 36 // Set up triange coordinates.
samer@18 37 // NB. whatever happens here, the triangle must be
samer@18 38 // isosceles and left-right symmetric around x=x1.
samer@18 39 // Otherwise the clipping won't work
samer@22 40 fitTriangleIn(ofGetWidth(),ofGetHeight());
samer@25 41 send("/tempo",tempi[tempoIndex]);
samer@22 42 sendCalibrate();
samer@23 43 sendReplyTo();
hekeus@6 44
samer@25 45 voiceGrabbed=NULL;
samer@12 46 }
samer@12 47
hekeus@6 48 //--------------------------------------------------------------
hekeus@6 49 void melodyTriangle::update(){
samer@22 50 bool sendStart=false;
samer@22 51
samer@22 52 while( receiver.hasWaitingMessages() ) {
hekeus@8 53 // get the next message
hekeus@8 54 ofxOscMessage m;
hekeus@8 55 receiver.getNextMessage( &m );
samer@22 56 handleMessage(m);
hekeus@8 57 }
samer@15 58
samer@22 59 constrained=false;
samer@25 60 if (voiceGrabbed!=NULL){
samer@25 61 Voice *vg=voiceGrabbed;
samer@14 62 if (mouseX!=vg->posx || mouseY!=vg->posy){
samer@15 63 int clipx=mouseX, clipy=mouseY;
samer@15 64 bool clipped=clipToTriangle(&clipx,&clipy);
samer@15 65
samer@15 66 if (vg->inTriangle) {
hekeus@6 67
samer@15 68 if (clipped) {
samer@15 69 // check how far we clipped
samer@30 70 if (ofDist(clipx, clipy, mouseX, mouseY)>BUFFER_ZONE && allowExit) {
samer@15 71 // if far enough, we pop out of triangle and send
samer@25 72 send("/death",vg->id);
samer@15 73 vg->posx=mouseX;
samer@15 74 vg->posy=mouseY;
samer@15 75 vg->inTriangle=false;
samer@23 76 vg->status=Voice::pending;
samer@15 77 } else {
samer@15 78 // otherwise, we move to clipped point
samer@15 79 constrained=true;
samer@15 80 vg->posx=clipx;
samer@15 81 vg->posy=clipy;
samer@15 82 }
samer@15 83 } else { // not clipped; normal move
samer@15 84 vg->posx=mouseX;
samer@15 85 vg->posy=mouseY;
samer@15 86 }
samer@15 87 } else { // token was outside triangle
samer@15 88 vg->posx=mouseX;
samer@15 89 vg->posy=mouseY;
samer@15 90 if (!clipped){ // ie mouse now in triangle
samer@25 91 send("/birth",vg->id);
samer@15 92
samer@15 93 printf("sent /birth %i \n",vg->id);
samer@15 94 sendOctave(vg->id,vg->octave);
samer@15 95 sendAmplitude(vg->id,vg->amplitude);
samer@15 96 sendStart=true;
samer@15 97 vg->inTriangle=true;
samer@25 98 vg->truex=vg->truey=-1; // ie not known yet.
hekeus@6 99 }
hekeus@6 100 }
hekeus@6 101
samer@14 102 if (vg->inTriangle){
samer@23 103 sendPosition(vg);
samer@18 104 vg->status=Voice::moved;
samer@25 105 if (sendStart && vg->isActive) send("/start",vg->id);
hekeus@6 106 }
hekeus@6 107 }
hekeus@6 108 };
samer@22 109 }
samer@10 110
samer@22 111
samer@22 112
samer@22 113 //--------------------------------------------------------------
samer@22 114 void melodyTriangle::draw(){
samer@11 115 ofSetLineWidth(2);
samer@23 116 ofSetColor(60,60,60);
samer@10 117 ofFill();
samer@10 118 ofTriangle(x1, y1, x2, y2, x3, y3);
samer@10 119
samer@10 120 // draw smooth edge, brighter if a token is constrained
samer@23 121 if (constrained) ofSetColor(255,96,96);
samer@10 122 ofNoFill();
samer@10 123 ofTriangle(x1, y1, x2, y2, x3, y3);
samer@10 124
samer@25 125 for (int i=numVoices-1; i>=0; i--){
samer@32 126 voices[i]->draw(enableKeys && voices[i]->isInVoice(mouseX,mouseY));
hekeus@6 127 }
hekeus@6 128
samer@22 129 // display message if any
samer@23 130 if (display_frames!=0) {
samer@23 131 ofRectangle bbox=display_font.getStringBoundingBox(display_msg,0,0);
samer@22 132 ofSetColor(220,220,220);
samer@23 133 display_font.drawString(display_msg,
samer@23 134 (ofGetWidth()-bbox.width)/2, (ofGetHeight()-bbox.height)/2);
samer@23 135 if (display_frames>0) display_frames--;
samer@22 136 }
samer@25 137 if (ratio!=2) {
samer@25 138 ofSetColor(160,160,160);
samer@25 139 display_font.drawString(ofToString(ratio),16,ofGetHeight()-16);
samer@25 140 }
hekeus@6 141 }
hekeus@6 142
samer@22 143 bool melodyTriangle::clipToTriangle(int *x, int *y) {
samer@22 144 bool clipped;
samer@22 145
samer@22 146 if (*y>y2) { // off the bottom
samer@22 147 clipped=true;
samer@22 148 *y=y2;
samer@22 149 if (*x<x2) *x=x2;
samer@22 150 else if (*x>x3) *x=x3;
samer@22 151 } else { // have to be a bit cleverer
samer@22 152 bool reflect=false;
samer@22 153 if (*x<x1) { // work in reflected coordinates
samer@22 154 reflect=true;
samer@22 155 *x=2*x1-*x;
samer@22 156 }
samer@22 157
samer@22 158 int dx=(*x-x1), dy=(*y-y1); // deltas from top
samer@22 159 if (dx*DY13 > dy*DX13) {
samer@22 160 // (x,y) must be somewhere right of triangle now
samer@22 161 clipped=true;
samer@22 162 int dp=dx*DX13 + dy*DY13;
samer@22 163 if (dp<0) { *x=x1; *y=y1; } // off the top
samer@22 164 else if (dp>SQLEN13) { *x=x3; *y=y3; } // off the bottom right
samer@22 165 else { // project onto right edge
samer@22 166 *x=x1+dp*DX13/SQLEN13;
samer@22 167 *y=y1+dp*DY13/SQLEN13;
samer@22 168 }
samer@22 169 } else {
samer@22 170 clipped=false;
samer@22 171 }
samer@22 172
samer@22 173 if (reflect) *x=2*x1 - *x; // reflect back if necessary
samer@22 174 }
samer@22 175 return clipped;
samer@22 176 }
samer@22 177
samer@22 178
samer@22 179
samer@22 180 //- Keyboard ----------------------------------------------------------
samer@22 181
samer@25 182 void melodyTriangle::keyReleased(int key){
samer@25 183 if (enableKeys && key>='2' && key<='9') {
samer@25 184 ratio=2;
samer@25 185 }
samer@25 186 }
samer@23 187 void melodyTriangle::keyPressed(int key){
hekeus@6 188 if (enableKeys){
samer@25 189 if (key>='2' && key<='9') {
samer@25 190 ratio=key-'0';
samer@25 191 } else {
samer@25 192 printf("got key: %d.\n",key);
samer@25 193 switch (key) {
samer@25 194
samer@25 195 case '{':
samer@25 196 if (tempoIndex>0) tempoIndex--;
samer@25 197 send("/tempo",tempi[tempoIndex]);
samer@25 198 break;
samer@25 199 case '}':
samer@32 200 if (tempoIndex<NUM_TEMPI-1) tempoIndex++;
samer@25 201 send("/tempo",tempi[tempoIndex]);
samer@25 202 break;
samer@25 203
samer@25 204 case ' ': send("/marker"); break;
samer@25 205 case 'S': send("/save"); break;
samer@31 206 case 'r': send("/report"); break;
samer@25 207 case 'C': sendReplyTo(); sendCalibrate(); break;
samer@25 208 case 'F': ofToggleFullscreen(); break;
samer@25 209 case 'R': reset(); break;
samer@25 210 case 'Q': ofAppGlutWindow::exitApp();
samer@30 211 case OF_KEY_ESC: setKeyboardEnable(false); break;
samer@25 212
samer@25 213 default: // otherwise, send key to all voices under mouse
samer@25 214 for (int i=0; i<numVoices; i++)
samer@25 215 if (voices[i]->isInVoice(mouseX,mouseY))
samer@25 216 voiceKeypress(voices[i],key);
samer@12 217 }
hekeus@6 218 }
samer@30 219 } else {
samer@30 220 if (key==OF_KEY_ESC) setKeyboardEnable(true);
samer@30 221 else send("/key",key);
samer@30 222 }
samer@23 223 }
samer@23 224
samer@23 225 void melodyTriangle::voiceKeypress(Voice *v, int key) {
samer@23 226 switch (key) {
samer@25 227 case 'a':
samer@32 228 if (v->inTriangle) {
samer@32 229 send(v->isActive ? "/stop" : "/start", v->id);
samer@32 230 }
samer@23 231 v->isActive=!v->isActive;
samer@23 232 break;
samer@25 233 case OF_KEY_LEFT: sendShift(v->id,-1,ratio); break;
samer@25 234 case OF_KEY_RIGHT: sendShift(v->id,1,ratio); break;
samer@25 235 case OF_KEY_UP: sendPeriod(v->id,1,ratio); break;
samer@25 236 case OF_KEY_DOWN: sendPeriod(v->id,ratio,1); break;
samer@25 237 case ']': sendOctave(v->id, ++v->octave); break;
samer@25 238 case '[': sendOctave(v->id, --v->octave); break;
samer@23 239 case '*': sendAmplitude(v->id, v->louder()); break;
samer@23 240 case '/': sendAmplitude(v->id, v->quieter()); break;
samer@25 241 case 'c': send("/change",v->id); v->status=Voice::pending; break;
samer@23 242 default: printf("unrecognised key: %d.\n",key);
hekeus@6 243 }
hekeus@6 244 }
hekeus@6 245
samer@22 246 //- Mouse ------------------------------------------------------
hekeus@6 247
samer@22 248 void melodyTriangle::mouseDragged(int x, int y, int button){}
samer@23 249 void melodyTriangle::mouseMoved(int x, int y ){}
samer@23 250 void melodyTriangle::mousePressed(int x, int y, int button){
samer@25 251 // pick up first token under mouse
hekeus@6 252 for (int i=0; i<numVoices;i++){
samer@25 253 if (voices[i]->isInVoice(x,y)) {
samer@25 254 voiceGrabbed=voices[i];
samer@25 255 break;
samer@25 256 }
hekeus@6 257 }
hekeus@6 258 }
hekeus@6 259
samer@22 260 void melodyTriangle::mouseReleased(int x, int y, int button){
samer@25 261 if (voiceGrabbed!=NULL) {
samer@25 262 Voice *v=voiceGrabbed;
samer@31 263 if (v->status==Voice::clear && v->truex>=0) {
samer@25 264 v->posx=v->truex;
samer@25 265 v->posy=v->truey;
samer@25 266 v->truex=v->truey=-1;
samer@25 267 }
samer@25 268 voiceGrabbed=NULL;
samer@25 269 }
hekeus@6 270 }
hekeus@6 271
hekeus@6 272 //--------------------------------------------------------------
samer@22 273
hekeus@6 274 void melodyTriangle::windowResized(int w, int h){
samer@22 275 fitTriangleIn(w,h);
samer@22 276 sendCalibrate();
samer@23 277 reset();
samer@22 278 }
hekeus@6 279
samer@23 280 void melodyTriangle::reset() {
samer@25 281 voiceGrabbed=NULL;
samer@23 282 for (int i=0;i<numVoices;i++) {
samer@23 283 Voice *v=voices[i];
samer@23 284 v->posx=x2+RADIUS+(numVoices-1-i)*36;
samer@23 285 v->posy=48;
samer@23 286 v->status=Voice::pending;
samer@25 287 v->isActive=true;
samer@23 288 if (v->inTriangle) {
samer@30 289 send("/death",v->id);
samer@23 290 v->inTriangle=false;
samer@23 291 }
samer@23 292 }
samer@23 293 }
samer@22 294
samer@22 295 // OSC Message handling -----------------------------------------
samer@22 296
samer@23 297 Voice *melodyTriangle::get_voice(int id) throw(bad_voice_id) {
samer@30 298 for (int i=0; i<numVoices; i++) {
samer@30 299 if (voices[i]->id==id) return voices[i];
samer@30 300 }
samer@30 301 throw bad_voice_id(id);
samer@23 302 }
samer@23 303
samer@22 304 void melodyTriangle::handleMessage(ofxOscMessage &m) {
samer@22 305 string msg_path=m.getAddress();
samer@23 306
samer@23 307 try {
samer@23 308 if (msg_path.compare(0,8,"/notify/")==0) {
samer@25 309 string msg=msg_path.substr(8);
samer@23 310 Voice *v=get_voice(m.getArgAsInt32(0));
samer@25 311
samer@32 312 cout << "Received " << msg_path << "\n";
samer@25 313 if (msg=="requested") v->status=Voice::waiting;
samer@25 314 else if (msg=="pending") v->status=Voice::pending;
samer@25 315 else if (msg=="received") {
samer@25 316 float x=m.getArgAsFloat(1);
samer@25 317 float y=m.getArgAsFloat(2);
samer@25 318 v->status=Voice::clear;
samer@31 319 if (snapTruePos) {
samer@31 320 if (voiceGrabbed==v) {
samer@31 321 v->truex=x;
samer@31 322 v->truey=y;
samer@31 323 } else {
samer@31 324 v->posx=x;
samer@31 325 v->posy=y;
samer@31 326 v->truex=v->truey=-1;
samer@31 327 }
samer@25 328 }
samer@31 329 printf("True position of %d: %4.1f, %4.1f\n",v->id,x,y);
samer@25 330 } else if (msg=="position") {
samer@23 331 int x=(int)m.getArgAsFloat(1);
samer@23 332 int y=(int)m.getArgAsFloat(2);
samer@23 333 v->posx=x;
samer@23 334 v->posy=y;
samer@23 335 v->inTriangle=!clipToTriangle(&x,&y);
samer@31 336 v->status=Voice::clear;
samer@25 337 if (voiceGrabbed==v) voiceGrabbed=NULL;
samer@25 338 } else if (msg=="running") {
samer@23 339 v->isActive = m.getArgAsInt32(1) ? true : false;
samer@25 340 } else if (msg=="params") {
samer@23 341 v->octave = m.getArgAsInt32(1);
samer@23 342 v->amplitude = m.getArgAsFloat(2);
samer@32 343 } else if (msg=="visible") {
samer@32 344 v->isVisible = m.getArgAsInt32(1);
samer@32 345 if (voiceGrabbed==v && !v->isVisible)
samer@32 346 voiceGrabbed=NULL;
samer@23 347 }
samer@23 348 } else if (msg_path=="/display") {
samer@23 349 display_msg=m.getArgAsString(0);
samer@23 350 display_frames=m.getArgAsInt32(1);
samer@23 351 } else if (msg_path=="/font") {
samer@23 352 display_font.loadFont(m.getArgAsString(0),m.getArgAsInt32(1));
samer@30 353 } else if (msg_path=="/keyboard") { setKeyboardEnable(m.getArgAsInt32(0)); }
samer@23 354 else if (msg_path=="/reset") { reset(); }
samer@23 355 else if (msg_path=="/fullscreen") { ofSetFullscreen(m.getArgAsInt32(0)); }
samer@23 356 else if (msg_path=="/quit") { ofAppGlutWindow::exitApp(); }
samer@30 357 else if (msg_path=="/allowExit") { allowExit=m.getArgAsInt32(0); }
samer@23 358 else {
samer@23 359 cout << m.getAddress();
samer@23 360 for (int i=0; i<m.getNumArgs(); i++) {
samer@23 361 cout << " " << m.getArgTypeName(i) << ":";
samer@23 362 switch (m.getArgType(i)) {
samer@23 363 case OFXOSC_TYPE_INT32: cout << m.getArgAsInt32(i);
samer@23 364 case OFXOSC_TYPE_FLOAT: cout << m.getArgAsFloat(i);
samer@23 365 case OFXOSC_TYPE_STRING: cout << m.getArgAsString(i);
samer@23 366 default: cout << "unknown";
samer@23 367 }
samer@23 368 }
samer@23 369 cout<< "\n";
samer@22 370 }
samer@23 371 } catch (std::exception &ex) {
samer@23 372 cout << "** Error processing OSC message: " << ex.what() << "\n";
samer@22 373 }
hekeus@6 374 }
samer@22 375
samer@30 376 void melodyTriangle::setKeyboardEnable(bool en) {
samer@30 377 enableKeys=en;
samer@31 378 snapTruePos=en;
samer@30 379 display_msg=en ? "Keyboard enabled" : "Keybard disabled";
samer@30 380 display_frames=40;
samer@30 381 }
samer@30 382
samer@22 383 // OSC Message sending -----------------------------------------
samer@22 384
samer@23 385 void melodyTriangle::sendPosition(Voice *v){
samer@22 386
samer@22 387 ofxOscMessage m;
samer@22 388 ///track id x y left right top bottom area
samer@22 389 m.setAddress( "/track2d" );
samer@23 390 m.addIntArg( v->id );
samer@23 391 m.addIntArg( v->posx );
samer@23 392 m.addIntArg( v->posy );
samer@22 393 sender.sendMessage( m );
samer@25 394 // printf("sent - /track2d %i %i %i\n",v->id,v->posx,v->posy);
samer@22 395 }
samer@22 396 void melodyTriangle::sendCalibrate(){
samer@22 397 ofxOscMessage m;
samer@22 398 m.setAddress( "/calibrate" );
samer@22 399 m.addIntArg( x1 );
samer@22 400 m.addIntArg( y1 );
samer@22 401 m.addIntArg( x2 );
samer@22 402 m.addIntArg( y2 );
samer@22 403 m.addIntArg( x3 );
samer@22 404 m.addIntArg( y3 );
samer@22 405 sender.sendMessage( m );
samer@22 406 printf("sent /calibrate %i %i %i %i %i %i\n",x1,y1,x2,y2,x3,y3);
samer@22 407 }
samer@22 408
samer@22 409 void melodyTriangle::sendReplyTo(){
samer@22 410 ofxOscMessage m;
samer@22 411 m.setAddress( "/reply_to" );
samer@22 412 m.addIntArg( receivePort );
samer@22 413 sender.sendMessage( m );
samer@22 414 printf("sent /reply_to %i\n",receivePort);
samer@22 415 }
samer@22 416
samer@32 417 void melodyTriangle::sendPeriod(int id, int num, int den){ send("/period",id,num,den); }
samer@32 418 void melodyTriangle::sendShift(int id, int num, int den) { send("/shift",id,num,den); }
samer@32 419 void melodyTriangle::sendOctave(int id, int oct){ send("/octave",id,oct); }
samer@22 420
samer@22 421 void melodyTriangle::sendAmplitude(int id, float amp){
samer@22 422 ofxOscMessage m;
samer@22 423 m.setAddress("/amplitude");
samer@22 424 m.addIntArg(id);
samer@22 425 m.addFloatArg(amp);
samer@22 426 sender.sendMessage(m);
samer@22 427 printf("sent /amplitude %i %1.3f\n",id,amp);
samer@22 428 }
samer@22 429
samer@32 430 void melodyTriangle::send(const char *addr, int a, int b, int c) {
samer@32 431 ofxOscMessage m;
samer@32 432 m.setAddress(addr);
samer@32 433 m.addIntArg(a);
samer@32 434 m.addIntArg(b);
samer@32 435 m.addIntArg(c);
samer@32 436 sender.sendMessage(m);
samer@32 437 printf("sent %s %i\n",addr,a);
samer@32 438 }
samer@32 439
samer@32 440
samer@32 441 void melodyTriangle::send(const char *addr, int a, int b) {
samer@32 442 ofxOscMessage m;
samer@32 443 m.setAddress(addr);
samer@32 444 m.addIntArg(a);
samer@32 445 m.addIntArg(b);
samer@32 446 sender.sendMessage(m);
samer@32 447 printf("sent %s %i\n",addr,a);
samer@32 448 }
samer@32 449
samer@32 450
samer@25 451 void melodyTriangle::send(const char *addr, int a) {
samer@25 452 ofxOscMessage m;
samer@25 453 m.setAddress(addr);
samer@25 454 m.addIntArg(a);
samer@25 455 sender.sendMessage(m);
samer@25 456 printf("sent %s %i\n",addr,a);
samer@25 457 }
samer@25 458
samer@25 459 void melodyTriangle::send(const char *addr) {
samer@25 460 ofxOscMessage m;
samer@25 461 m.setAddress(addr);
samer@25 462 sender.sendMessage(m);
samer@25 463 printf("sent %s\n",addr);
samer@25 464 }
samer@22 465
samer@22 466 void melodyTriangle::fitTriangleIn(int width, int height) {
samer@22 467 int triHeight = height*0.75;
samer@22 468 int triHalfWidth = triHeight/sqrt(3);
samer@22 469
samer@22 470 x1=width/2;
samer@22 471 x2=x1-triHalfWidth;
samer@22 472 x3=x1+triHalfWidth;
samer@22 473 y1=(height-triHeight)/2;
samer@22 474 y2=y3=height - (height-triHeight)/2;
samer@22 475
samer@22 476 // used for clipping
samer@22 477 DX13=x3-x1; DY13=y3-y1;
samer@22 478 SQLEN13=DX13*DX13+DY13*DY13;
samer@22 479 }
samer@23 480
samer@23 481 const char *melodyTriangle::bad_voice_id::what() const throw() {
samer@23 482 std::stringstream out;
samer@23 483 printf("bad_voice_id(%d).\n",id);
samer@30 484 out << "Voice id " << id <<" not present.";
samer@23 485 return out.str().c_str();
samer@23 486 }