annotate src/melodyTriangle.cpp @ 30:9e8c19c90986

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