Mercurial > hg > screen-ui
changeset 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 | 328822cd308c |
files | MelodyTriangle.xcodeproj/project.pbxproj src/Voice.cpp src/Voice.h src/main.cpp src/melodyTriangle.cpp src/melodyTriangle.h |
diffstat | 6 files changed, 222 insertions(+), 173 deletions(-) [+] |
line wrap: on
line diff
--- a/MelodyTriangle.xcodeproj/project.pbxproj Thu Feb 02 18:17:24 2012 +0000 +++ b/MelodyTriangle.xcodeproj/project.pbxproj Sat Feb 04 23:14:38 2012 +0000 @@ -766,12 +766,14 @@ GCC_INLINES_ARE_PRIVATE_EXTERN = NO; GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; GCC_WARN_UNINITIALIZED_AUTOS = NO; - GCC_WARN_UNUSED_VALUE = NO; - GCC_WARN_UNUSED_VARIABLE = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "../../../libs/openFrameworks/**", ../../../libs/freetype/include/,
--- a/src/Voice.cpp Thu Feb 02 18:17:24 2012 +0000 +++ b/src/Voice.cpp Sat Feb 04 23:14:38 2012 +0000 @@ -12,38 +12,53 @@ inline static double min(double x,double y) { return (x<y) ? x : y; } Voice::Voice(int id): - radius(12), isActive(true), - inTriangle(false), octave(0), highlight(false), - amplitude(0.6), status(clear), id(id), posx(0), posy(0) {} + isActive(true), inTriangle(false), status(pending), + octave(0), amplitude(0.6), id(id), posx(0), posy(0) {} -void Voice::draw(){ - int r,g,b; - switch (status) { - case clear: r=1; g=0; b=0; break; - default: r=1; g=1; b=0; break; -// case pending: r=1; g=1; b=0; break; -// case waiting: r=1; g=0; b=0; break; -// case moved: r=1; g=0; b=1; break; -// default: r=0; g=1; b=0; - } +void Voice::draw(bool highlight){ +// if (inTriangle) { + int r,g,b; + switch (status) { + case clear: r=1; g=1; b=0; break; + default: r=1; g=0; b=0; break; + // case pending: r=1; g=1; b=0; break; + // case waiting: r=1; g=0; b=0; break; + // case moved: r=1; g=0; b=1; break; + // default: r=0; g=1; b=0; + } + if (isActive) { r=2*r; g=2*g; b=2*b; } + ofSetColor(100*r,60*g,60*b); +// } else { +// if (isActive) ofSetColor(128,128,128); +// else ofSetColor(64,64,64); +// } - if (isActive) { r=2*r; g=2*g; b=2*b; } - ofSetColor(100*r,60*g,60*b); ofFill(); - ofCircle(posx, posy, radius); + ofCircle(posx, posy, RADIUS); //ofNoFill(); - //ofCircle(posx, posy, radius); + //ofCircle(posx, posy, RADIUS); if (highlight) { ofSetColor(230, 230, 230); ofNoFill(); - ofCircle(posx, posy, radius); + ofCircle(posx, posy, RADIUS); } else { ofNoFill(); - ofCircle(posx, posy, radius); + ofCircle(posx, posy, RADIUS); } } double Voice::louder() { return amplitude=min(1,amplitude*1.0625); } double Voice::quieter() { return amplitude/=1.0625; } +enum Voice::status Voice::stringToStatus(string s) throw(bad_status) { + if (s=="received") return clear; + else if (s=="pending") return pending; + else if (s=="requested") return waiting; + else throw bad_status(s); +} + +const char *Voice::bad_status::what() const throw() { + return ("Unrecognised voice status: "+str+".").c_str(); +} +
--- a/src/Voice.h Thu Feb 02 18:17:24 2012 +0000 +++ b/src/Voice.h Sat Feb 04 23:14:38 2012 +0000 @@ -7,6 +7,8 @@ * */ #include "ofMain.h" +#define RADIUS 12 + class Voice { public: Voice(int id); @@ -17,20 +19,27 @@ waiting, moved }; - - bool isInVoice(int x, int y) { return (ofDist(x, y, posx, posy)<=radius); }; - void setPos(int x, int y) { posx=x; posy=y; } + + class bad_status : public std::exception { + string str; + public: + bad_status(string s): str(s) {} + ~bad_status() throw() {} + const char *what() const throw(); + }; + + static status stringToStatus(string str) throw(bad_status); + bool isInVoice(int x, int y) { return (ofDist(x, y, posx, posy)<=RADIUS); }; + //void setPos(int x, int y) { posx=x; posy=y; status=pending; } double louder(); double quieter(); - void draw(); + void draw(bool highlight); int id; int posx,posy; bool isActive; bool inTriangle; - int radius; int octave; - bool highlight; double amplitude; enum status status; };
--- a/src/main.cpp Thu Feb 02 18:17:24 2012 +0000 +++ b/src/main.cpp Sat Feb 04 23:14:38 2012 +0000 @@ -4,7 +4,7 @@ #define HOST "localhost" #define PORT 7770 -#define NUMVOICES 3 +#define NUMVOICES 4 #define RECEIVEPORT 7771 //========================================================================
--- 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(); +}
--- a/src/melodyTriangle.h Thu Feb 02 18:17:24 2012 +0000 +++ b/src/melodyTriangle.h Sat Feb 04 23:14:38 2012 +0000 @@ -1,18 +1,15 @@ - - #include "ofMain.h" +#include "ofxOsc.h" #include "Voice.h" -#include "ofxOsc.h" - #define MAX_VOICES 10 - class melodyTriangle : public ofBaseApp{ public: - melodyTriangle(const char *host, int port, int numVoices, bool enableKeys, int voiceIdOffset, int receivePort); ~melodyTriangle(); + melodyTriangle(const char *host, int port, int numVoices, + bool enableKeys, int voiceIdOffset, int receivePort); void setup(); void update(); @@ -27,10 +24,22 @@ void windowResized(int w, int h); private: + class bad_voice_id : public std::exception { + int id; + public: + bad_voice_id(int id): id(id) {} + ~bad_voice_id() throw() {} + const char *what() const throw(); + }; + // private methods + Voice *get_voice(int id) throw(bad_voice_id); + + void voiceKeypress(Voice *v, int key); void sendReplyTo(); void sendCalibrate(); - void sendPosition(Voice v); + void sendDeath(int id); + void sendPosition(Voice *v); void sendPeriod(int id, int num, int den); void sendShift(int id, int num, int den); void sendOctave(int id, int oct); @@ -38,23 +47,24 @@ bool clipToTriangle(int *cx, int *cy); void fitTriangleIn(int w, int h); void handleMessage(ofxOscMessage &m); + void reset(); // Immutable after construction and setup ofxOscSender sender; ofxOscReceiver receiver; int receivePort; // for sending /reply_to message int numVoices; - bool enableKeys; // somewhat mutable (on window resize) int x1,y1,x2,y2,x3,y3; // Triangle Coords int DX13, DY13, SQLEN13; // to optimise clipping // mutable state - Voice *voices[MAX_VOICES]; - int voiceGrabbed; + Voice *voices[MAX_VOICES]; + int voiceGrabbed; + bool constrained; + bool enableKeys; + int display_frames; + string display_msg; ofTrueTypeFont display_font; - string display_msg; - int display_frames; - bool constrained; };