diff src/melodyTriangle.cpp @ 23:460c05dd74d0

Various enhancements and code refactorings: * added some compiler warnings. * text display is centred and with settable TrueType font * removed highlight member from Voice state - value is derived from other data and passed to Voice::draw() * Changed voice radius from member to defined constant * default number of voices is now 4 * adjusted some colours and buffer zone width * new keyboard commands: reset, quit. * when keyboard disabled, keys are now passed to server via OSC * added handlers for various new OSC messages: - fullscreen, reset, quit, keyboard enable - notify (voice state) : several sub-messages * call reset and calibrate on window resize (fits triangle to window)
author samer
date Sat, 04 Feb 2012 23:14:38 +0000
parents 4dcc4312b5fa
children f4ebb87adec1
line wrap: on
line diff
--- a/src/melodyTriangle.cpp	Thu Feb 02 18:17:24 2012 +0000
+++ b/src/melodyTriangle.cpp	Sat Feb 04 23:14:38 2012 +0000
@@ -1,30 +1,20 @@
 #include "melodyTriangle.h"
 #include <GLUT/GLUT.h> 
 
-#define BUFFER_ZONE 50 // have to drag this far to snap out of triange.
-/*
- /birth id
- /death id
- /start id
- /stop id
- /track id x y left right top bottom area  
- /tempo 
- 
- 
- */
+#define BUFFER_ZONE 64 // have to drag this far to snap out of triange.
 
 melodyTriangle::melodyTriangle(const char *host, int port, int numVoices, 
 							   bool enableKeys,int voiceIdOffset,int receivePort): 
 	numVoices(numVoices), enableKeys(enableKeys), receivePort(receivePort),
-	display_msg(""),
-	display_frames(0)
+	display_msg(""), display_frames(0)
 {
-	printf("in constructor: %s %i %i %i %i %i\n",host,port,numVoices,enableKeys,voiceIdOffset,receivePort);
+	printf("in constructor: %s %i %i %i %i %i\n",
+		   host,port,numVoices,enableKeys,voiceIdOffset,receivePort);
 	for (int i=0;i<numVoices;i++) voices[i]=new Voice(i+1+voiceIdOffset);
 	
 	sender.setup( host,port );
 	receiver.setup( receivePort );
-	display_font.loadFont("/System/Library/Fonts/HelveticaLight.ttf",32);
+	display_font.loadFont("/System/Library/Fonts/HelveticaLight.ttf",24);
 }
 
 melodyTriangle::~melodyTriangle() {
@@ -37,22 +27,17 @@
 	ofSetCircleResolution(64);
 	ofBackground(0,0,0);
 	ofSetWindowTitle("Melody Triangle");
-	
-	// if vertical sync is off, we can go a bit fast... 
-	// this caps the framerate at 40fps.
-	ofSetFrameRate(40); 
+	ofSetFrameRate(40); // caps framerate if vertical sync is off.
 	ofEnableSmoothing();
 
-	sendReplyTo();
-	
 	// Set up triange coordinates. 
 	// NB. whatever happens here, the triangle must be
 	// isosceles and left-right symmetric around x=x1.
 	// Otherwise the clipping won't work
 	fitTriangleIn(ofGetWidth(),ofGetHeight());
 	sendCalibrate();
+	sendReplyTo();
 	
-	for (int i=0;i<numVoices;i++) voices[i]->setPos(x2+voices[i]->radius+i*30,48);
 	voiceGrabbed=-1;
 }
 
@@ -80,17 +65,11 @@
 					// check how far we clipped
 					if (ofDist(clipx, clipy, mouseX, mouseY) > BUFFER_ZONE) { 
 						// if far enough, we pop out of triangle and send
-						// /death <id>
-						ofxOscMessage m;
-						m.setAddress( "/death" );
-						m.addIntArg( vg->id );
-						sender.sendMessage( m );
-						
-						printf("sent /death %i \n",vg->id);
+						sendDeath(vg->id);
 						vg->posx=mouseX;
 						vg->posy=mouseY;
 						vg->inTriangle=false;
-						vg->status=Voice::clear;
+						vg->status=Voice::pending;
 					} else {
 						// otherwise, we move to clipped point
 						constrained=true;
@@ -121,7 +100,7 @@
 			}
 			
 			if (vg->inTriangle){
-				sendPosition(*vg);
+				sendPosition(vg);
 				vg->status=Voice::moved;
 				if (sendStart && vg->isActive){
 					ofxOscMessage m;
@@ -141,24 +120,26 @@
 //--------------------------------------------------------------
 void melodyTriangle::draw(){
 	ofSetLineWidth(2);
-	ofSetColor(80,80,80);
+	ofSetColor(60,60,60);
 	ofFill();		
 	ofTriangle(x1, y1, x2, y2, x3, y3);
-	if (constrained) ofSetColor(255,96,96);
 
 	// draw smooth edge, brighter if a token is constrained
+	if (constrained) ofSetColor(255,96,96);
 	ofNoFill();		
 	ofTriangle(x1, y1, x2, y2, x3, y3);
 	
 	for (int i=0; i<numVoices; i++){
-		(*voices[i]).draw();
+		voices[i]->draw(voices[i]->isInVoice(mouseX,mouseY));
 	}
 	
 	// display message if any
-	if (display_frames>0) {
+	if (display_frames!=0) {
+		ofRectangle bbox=display_font.getStringBoundingBox(display_msg,0,0);
 		ofSetColor(220,220,220);
-		display_font.drawString(display_msg,x2,y1+32);
-		display_frames--;
+		display_font.drawString(display_msg,
+			(ofGetWidth()-bbox.width)/2, (ofGetHeight()-bbox.height)/2);
+		if (display_frames>0) display_frames--;
 	}
 }
 
@@ -202,7 +183,7 @@
 //- Keyboard ----------------------------------------------------------
 
 void melodyTriangle::keyReleased(int key){}
-void melodyTriangle::keyPressed  (int key){
+void melodyTriangle::keyPressed(int key){
 	//printf("key %i",key);
 	if (enableKeys){
 		switch (key) {
@@ -214,74 +195,69 @@
 				break;
 			}
 				
-			case '1':
-			case '2':
-			case '3':
-			case '4': {
+			case '1': case '2':
+			case '3': case '4': {
 				int tempo=30 + 30*(key-'1');
 				ofxOscMessage m;
 				m.setAddress( "/tempo" );
 				m.addIntArg(tempo);
 				sender.sendMessage( m );
 				printf("sent /tempo %d\n",tempo);
+				break;
 			}
-				break;
 				
 			case 'c': sendReplyTo(); sendCalibrate(); break;
-			case 'f': ofToggleFullscreen(); break;
+			case 'F': ofToggleFullscreen(); break;
+			case 'R': reset(); break;
+			case 'Q': ofAppGlutWindow::exitApp();
 
-			default: { // otherwise, send key to all active voices
-				for (int i=0; i<numVoices; i++){
-					if (voices[i]->isInVoice(mouseX,mouseY)){
-						Voice *v=voices[i];
-						switch (key) {
-							case 'a': {
-								ofxOscMessage m;
-								const char *addr = v->isActive ? "/stop" : "/start";
-								v->isActive=!v->isActive;
-								m.setAddress(addr);
-								m.addIntArg(v->id );
-								sender.sendMessage( m );
-								printf("sent %s %i \n",addr,v->id);
-								break;
-							}
-							case OF_KEY_LEFT:  sendShift(v->id,-1,2); break;
-							case OF_KEY_RIGHT: sendShift(v->id,1,2); break;
-							case OF_KEY_UP:    sendPeriod(v->id,1,2); break;
-							case OF_KEY_DOWN:  sendPeriod(v->id,2,1); break;
-							case '.': sendPeriod(v->id,1,3); break;
-							case ',': sendPeriod(v->id,3,1); break;
-							case '+': sendOctave(v->id, ++v->octave); break;
-							case '-': sendOctave(v->id, --v->octave); break;
-							case '*': sendAmplitude(v->id, v->louder()); break;
-							case '/': sendAmplitude(v->id, v->quieter()); break;
-							default:  printf("unrecognised key: %d.\n",key);
-						}
-					}
-				}
-			}
+			default: // otherwise, send key to all voices under mouse
+				for (int i=0; i<numVoices; i++)
+					if (voices[i]->isInVoice(mouseX,mouseY)) 
+						voiceKeypress(voices[i],key);
 		}
+	} else {
+		ofxOscMessage m;
+		m.setAddress( "/key" );
+		m.addIntArg(key);
+		sender.sendMessage(m);
+		printf("sent /key %d\n", key);
+	}
+}
+
+void melodyTriangle::voiceKeypress(Voice *v, int key) {
+	switch (key) {
+		case 'a': {
+			ofxOscMessage m;
+			const char *addr = v->isActive ? "/stop" : "/start";
+			v->isActive=!v->isActive;
+			m.setAddress(addr);
+			m.addIntArg(v->id );
+			sender.sendMessage( m );
+			printf("sent %s %i \n",addr,v->id);
+			break;
+		}
+		case OF_KEY_LEFT:  sendShift(v->id,-1,2); break;
+		case OF_KEY_RIGHT: sendShift(v->id,1,2); break;
+		case OF_KEY_UP:    sendPeriod(v->id,1,2); break;
+		case OF_KEY_DOWN:  sendPeriod(v->id,2,1); break;
+		case '.': sendPeriod(v->id,1,3); break;
+		case ',': sendPeriod(v->id,3,1); break;
+		case '+': sendOctave(v->id, ++v->octave); break;
+		case '-': sendOctave(v->id, --v->octave); break;
+		case '*': sendAmplitude(v->id, v->louder()); break;
+		case '/': sendAmplitude(v->id, v->quieter()); break;
+		default:  printf("unrecognised key: %d.\n",key);
 	}
 }
 
 //- Mouse ------------------------------------------------------
 
 void melodyTriangle::mouseDragged(int x, int y, int button){}
-void melodyTriangle::mouseMoved(int x, int y ){
+void melodyTriangle::mouseMoved(int x, int y ){}
+void melodyTriangle::mousePressed(int x, int y, int button){
 	for (int i=0; i<numVoices;i++){
-		voices[i]->highlight = voices[i]->isInVoice(x,y);
-	}
-}
-
-void melodyTriangle::mousePressed(int x, int y, int button){
-	
-	for (int i=0; i<numVoices;i++){
-		if (voices[i]->isInVoice(x,y)){
-			voiceGrabbed=i;
-			//printf("grabbed %i",voiceGrabbed);
-		}else{
-			//printf("didnt grab %i",i);
-		}
+		if (voices[i]->isInVoice(x,y)) voiceGrabbed=i;
 	}
 }
 
@@ -294,67 +270,97 @@
 void melodyTriangle::windowResized(int w, int h){
 	fitTriangleIn(w,h);
 	sendCalibrate();
+	reset();
 }
 
+void melodyTriangle::reset() {
+	voiceGrabbed=-1;
+	for (int i=0;i<numVoices;i++) {
+		Voice *v=voices[i];
+		v->posx=x2+RADIUS+(numVoices-1-i)*36;
+		v->posy=48;
+		v->status=Voice::pending;
+		if (v->inTriangle) {
+			sendDeath(voices[i]->id);
+			v->inTriangle=false;
+		}
+	}
+}
 
 // OSC Message handling -----------------------------------------
 
+Voice *melodyTriangle::get_voice(int id) throw(bad_voice_id) {
+	if (id<1 || id>numVoices) throw bad_voice_id(id);
+	return voices[id-1];
+}
+
 void melodyTriangle::handleMessage(ofxOscMessage &m) {
 	string msg_path=m.getAddress();
-	
-	if (msg_path=="/notify") {
-		int id=m.getArgAsInt32(0)-1;
-		string st=m.getArgAsString(1);
-		
-		if (id>=0 && id<numVoices) {
-			Voice *v=voices[id];
-			
-			if (st=="received") v->status=Voice::clear;
-			else if (st=="pending") v->status=Voice::pending;
-			else if (st=="requested") v->status=Voice::waiting;
-			else cout << "** unrecognised voice status: " << st << ".\n";
-		} else {
-			cout << "** voice id "<<id<<" out of range.\n";
+
+	try {
+		if (msg_path.compare(0,8,"/notify/")==0) {
+			Voice *v=get_voice(m.getArgAsInt32(0));
+			if (msg_path=="/notify/status") {
+				v->status=Voice::stringToStatus(m.getArgAsString(1));
+			} else if (msg_path=="/notify/position") {
+				int x=(int)m.getArgAsFloat(1);
+				int y=(int)m.getArgAsFloat(2);
+				v->posx=x;
+				v->posy=y;
+				v->inTriangle=!clipToTriangle(&x,&y);
+				if (voiceGrabbed==v->id) voiceGrabbed=-1;
+			} else if (msg_path=="/notify/running") {
+				v->isActive  = m.getArgAsInt32(1) ? true : false;
+			} else if (msg_path=="/notify/params") {
+				v->octave    = m.getArgAsInt32(1);
+				v->amplitude = m.getArgAsFloat(2);
+			}
+		} else if (msg_path=="/display") {
+			display_msg=m.getArgAsString(0);
+			display_frames=m.getArgAsInt32(1);
+		} else if (msg_path=="/font") {
+			display_font.loadFont(m.getArgAsString(0),m.getArgAsInt32(1));
+		} else if (msg_path=="/keyboard") { enableKeys=m.getArgAsInt32(0); }
+		else if (msg_path=="/reset")	  { reset(); } 
+		else if (msg_path=="/fullscreen") { ofSetFullscreen(m.getArgAsInt32(0)); }
+		else if (msg_path=="/quit")		  { ofAppGlutWindow::exitApp(); } 
+		else {
+			cout << m.getAddress();
+			for (int i=0; i<m.getNumArgs(); i++) {
+				cout << " " << m.getArgTypeName(i) << ":";
+				switch (m.getArgType(i)) {
+					case OFXOSC_TYPE_INT32:  cout << m.getArgAsInt32(i);
+					case OFXOSC_TYPE_FLOAT:  cout << m.getArgAsFloat(i);
+					case OFXOSC_TYPE_STRING: cout << m.getArgAsString(i);
+					default: cout << "unknown";
+				}
+			}
+			cout<< "\n";
 		}
-	} else if (msg_path=="/display") {
-		display_msg=m.getArgAsString(0);
-		display_frames=m.getArgAsInt32(1);
-		if (display_frames<0) display_frames=0;
-	} else {
-		string msg_string;
-		msg_string = m.getAddress();
-		msg_string += ": ";
-		for ( int i=0; i<m.getNumArgs(); i++ )
-		{
-			// get the argument type
-			msg_string += m.getArgTypeName( i );
-			msg_string += ":";
-			// display the argument - make sure we get the right type
-			if( m.getArgType( i ) == OFXOSC_TYPE_INT32 )
-				msg_string += ofToString( m.getArgAsInt32( i ) );
-			else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
-				msg_string += ofToString( m.getArgAsFloat( i ) );
-			else if( m.getArgType( i ) == OFXOSC_TYPE_STRING )
-				msg_string += m.getArgAsString( i );
-			else
-				msg_string += "unknown";
-		}
-		cout<< msg_string << "\n";
+	} catch (std::exception &ex) {
+		cout << "** Error processing OSC message: " << ex.what() << "\n";
 	}
 }
 
 // OSC Message sending -----------------------------------------
 
-void melodyTriangle::sendPosition(Voice v){
+void melodyTriangle::sendDeath(int id) {
+	ofxOscMessage m;
+	m.setAddress( "/death" );	
+	m.addIntArg( id );
+	sender.sendMessage( m );
+}
+
+void melodyTriangle::sendPosition(Voice *v){
 	
 	ofxOscMessage m;
 	///track id x y left right top bottom area  
 	m.setAddress( "/track2d" );
-	m.addIntArg( v.id );
-	m.addIntArg( v.posx );
-	m.addIntArg( v.posy );
+	m.addIntArg( v->id );
+	m.addIntArg( v->posx );
+	m.addIntArg( v->posy );
 	sender.sendMessage( m );
-	printf("sent - /track2d %i %i %i\n",v.id,v.posx,v.posy);
+	printf("sent - /track2d %i %i %i\n",v->id,v->posx,v->posy);
 	
 }
 void melodyTriangle::sendCalibrate(){
@@ -431,3 +437,10 @@
 	DX13=x3-x1; DY13=y3-y1;
 	SQLEN13=DX13*DX13+DY13*DY13;
 }
+
+const char *melodyTriangle::bad_voice_id::what() const throw() { 
+	std::stringstream out;
+	printf("bad_voice_id(%d).\n",id);
+	out << "Voice id " << id <<" out of range.";
+	return out.str().c_str();
+}