view src/melodyTriangle.cpp @ 22:4dcc4312b5fa

Bit of a tidy up, adding text display, added full screen toggle and window resize handler.
author samer
date Thu, 02 Feb 2012 18:17:24 +0000
parents 055d7524bae4
children 460c05dd74d0
line wrap: on
line source
#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 
 
 
 */

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)
{
	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);
}

melodyTriangle::~melodyTriangle() {
	printf("Deleting voice objects...\n");
	for (int i=0;i<numVoices;i++) delete voices[i];
}
	
//--------------------------------------------------------------
void melodyTriangle::setup(){
	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); 
	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();
	
	for (int i=0;i<numVoices;i++) voices[i]->setPos(x2+voices[i]->radius+i*30,48);
	voiceGrabbed=-1;
}

//--------------------------------------------------------------
void melodyTriangle::update(){
	bool sendStart=false;

	while( receiver.hasWaitingMessages() ) {
		// get the next message
		ofxOscMessage m;
		receiver.getNextMessage( &m );
		handleMessage(m);
	}
	
	constrained=false;
	if (voiceGrabbed!=-1){
		Voice *vg=voices[voiceGrabbed];
		if (mouseX!=vg->posx || mouseY!=vg->posy){
			int clipx=mouseX, clipy=mouseY;
			bool clipped=clipToTriangle(&clipx,&clipy);
			
			if (vg->inTriangle) {
				
				if (clipped) {
					// 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);
						vg->posx=mouseX;
						vg->posy=mouseY;
						vg->inTriangle=false;
						vg->status=Voice::clear;
					} else {
						// otherwise, we move to clipped point
						constrained=true;
						vg->posx=clipx;
						vg->posy=clipy;
					}
				} else { // not clipped; normal move
					vg->posx=mouseX;
					vg->posy=mouseY;
				}
			} else { // token was outside triangle
				vg->posx=mouseX;
				vg->posy=mouseY;
				if (!clipped){ // ie mouse now in triangle
					//birth id
					
					ofxOscMessage m;
					m.setAddress( "/birth" );
					m.addIntArg( vg->id );
					sender.sendMessage( m );
					
					printf("sent /birth %i \n",vg->id);
					sendOctave(vg->id,vg->octave);
					sendAmplitude(vg->id,vg->amplitude);
					sendStart=true;
					vg->inTriangle=true;
				}
			}
			
			if (vg->inTriangle){
				sendPosition(*vg);
				vg->status=Voice::moved;
				if (sendStart && vg->isActive){
					ofxOscMessage m;
					///track id x y left right top bottom area  
					m.setAddress( "/start" );
					m.addIntArg( vg->id );
					sender.sendMessage( m );
					printf("sent /start %i \n",vg->id);
				}
			}
		}
	};
}



//--------------------------------------------------------------
void melodyTriangle::draw(){
	ofSetLineWidth(2);
	ofSetColor(80,80,80);
	ofFill();		
	ofTriangle(x1, y1, x2, y2, x3, y3);
	if (constrained) ofSetColor(255,96,96);

	// draw smooth edge, brighter if a token is constrained
	ofNoFill();		
	ofTriangle(x1, y1, x2, y2, x3, y3);
	
	for (int i=0; i<numVoices; i++){
		(*voices[i]).draw();
	}
	
	// display message if any
	if (display_frames>0) {
		ofSetColor(220,220,220);
		display_font.drawString(display_msg,x2,y1+32);
		display_frames--;
	}
}

bool melodyTriangle::clipToTriangle(int *x, int *y) {
	bool clipped;
	
	if (*y>y2) { // off the bottom
		clipped=true;
		*y=y2;
		if (*x<x2) *x=x2; 
		else if (*x>x3) *x=x3;
	} else { // have to be a bit cleverer
		bool reflect=false;
		if (*x<x1) { // work in reflected coordinates
			reflect=true;
			*x=2*x1-*x;
		}
		
		int dx=(*x-x1), dy=(*y-y1); // deltas from top
		if (dx*DY13 > dy*DX13) {
			// (x,y) must be somewhere right of triangle now
			clipped=true;
			int dp=dx*DX13 + dy*DY13;
			if (dp<0) { *x=x1; *y=y1; } // off the top
			else if (dp>SQLEN13) { *x=x3; *y=y3; } // off the bottom right
			else { // project onto right edge
				*x=x1+dp*DX13/SQLEN13;
				*y=y1+dp*DY13/SQLEN13;
			}
		} else {
			clipped=false;
		}
		
		if (reflect) *x=2*x1 - *x; // reflect back if necessary
	}
	return clipped;
}



//- Keyboard ----------------------------------------------------------

void melodyTriangle::keyReleased(int key){}
void melodyTriangle::keyPressed  (int key){
	//printf("key %i",key);
	if (enableKeys){
		switch (key) {
			case ' ': {
				ofxOscMessage m;
				m.setAddress( "/marker" );
				sender.sendMessage(m);
				printf("sent /marker\n");
				break;
			}
				
			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;
				
			case 'c': sendReplyTo(); sendCalibrate(); break;
			case 'f': ofToggleFullscreen(); break;

			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);
						}
					}
				}
			}
		}
	}
}

//- Mouse ------------------------------------------------------

void melodyTriangle::mouseDragged(int x, int y, int button){}
void melodyTriangle::mouseMoved(int x, int y ){
	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);
		}
	}
}

void melodyTriangle::mouseReleased(int x, int y, int button){ 
	voiceGrabbed=-1; 
}

//--------------------------------------------------------------

void melodyTriangle::windowResized(int w, int h){
	fitTriangleIn(w,h);
	sendCalibrate();
}


// OSC Message handling -----------------------------------------

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";
		}
	} 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";
	}
}

// OSC Message sending -----------------------------------------

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 );
	sender.sendMessage( m );
	printf("sent - /track2d %i %i %i\n",v.id,v.posx,v.posy);
	
}
void melodyTriangle::sendCalibrate(){
	ofxOscMessage m;
	m.setAddress( "/calibrate" );
	m.addIntArg( x1 );
	m.addIntArg( y1 );
	m.addIntArg( x2 );
	m.addIntArg( y2 );
	m.addIntArg( x3 );
	m.addIntArg( y3 );
	sender.sendMessage( m );
	printf("sent /calibrate %i %i %i %i %i %i\n",x1,y1,x2,y2,x3,y3);
}

void melodyTriangle::sendReplyTo(){	
	ofxOscMessage m;
	m.setAddress( "/reply_to" );
	m.addIntArg( receivePort );
	sender.sendMessage( m );
	printf("sent /reply_to %i\n",receivePort);
}

void melodyTriangle::sendPeriod(int id, int num, int den){
	ofxOscMessage m;
	m.setAddress("/period");
	m.addIntArg(id);
	m.addIntArg(num);
	m.addIntArg(den);
	sender.sendMessage(m);
	printf("sent /period %i %i %i\n",id,num,den);
}

void melodyTriangle::sendShift(int id, int num, int den){
	ofxOscMessage m;
	m.setAddress("/shift");
	m.addIntArg(id);
	m.addIntArg(num);
	m.addIntArg(den);
	sender.sendMessage(m);
	printf("sent /shift %i %i %i\n",id,num,den);
}

void melodyTriangle::sendOctave(int id, int oct){
	ofxOscMessage m;
	m.setAddress("/octave");
	m.addIntArg(id);
	m.addIntArg(oct);
	sender.sendMessage(m);
	printf("sent /octave %i %i\n",id,oct);
}

void melodyTriangle::sendAmplitude(int id, float amp){
	ofxOscMessage m;
	m.setAddress("/amplitude");
	m.addIntArg(id);
	m.addFloatArg(amp);
	sender.sendMessage(m);
	printf("sent /amplitude %i %1.3f\n",id,amp);
}


void melodyTriangle::fitTriangleIn(int width, int height) {
	int triHeight    = height*0.75;
	int triHalfWidth = triHeight/sqrt(3);
	
	x1=width/2;
	x2=x1-triHalfWidth;
	x3=x1+triHalfWidth;
	y1=(height-triHeight)/2;
	y2=y3=height - (height-triHeight)/2;
	
	// used for clipping
	DX13=x3-x1; DY13=y3-y1;
	SQLEN13=DX13*DX13+DY13*DY13;
}