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