comparison 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
comparison
equal deleted inserted replaced
22:4dcc4312b5fa 23:460c05dd74d0
1 #include "melodyTriangle.h" 1 #include "melodyTriangle.h"
2 #include <GLUT/GLUT.h> 2 #include <GLUT/GLUT.h>
3 3
4 #define BUFFER_ZONE 50 // have to drag this far to snap out of triange. 4 #define BUFFER_ZONE 64 // have to drag this far to snap out of triange.
5 /*
6 /birth id
7 /death id
8 /start id
9 /stop id
10 /track id x y left right top bottom area
11 /tempo
12
13
14 */
15 5
16 melodyTriangle::melodyTriangle(const char *host, int port, int numVoices, 6 melodyTriangle::melodyTriangle(const char *host, int port, int numVoices,
17 bool enableKeys,int voiceIdOffset,int receivePort): 7 bool enableKeys,int voiceIdOffset,int receivePort):
18 numVoices(numVoices), enableKeys(enableKeys), receivePort(receivePort), 8 numVoices(numVoices), enableKeys(enableKeys), receivePort(receivePort),
19 display_msg(""), 9 display_msg(""), display_frames(0)
20 display_frames(0)
21 { 10 {
22 printf("in constructor: %s %i %i %i %i %i\n",host,port,numVoices,enableKeys,voiceIdOffset,receivePort); 11 printf("in constructor: %s %i %i %i %i %i\n",
12 host,port,numVoices,enableKeys,voiceIdOffset,receivePort);
23 for (int i=0;i<numVoices;i++) voices[i]=new Voice(i+1+voiceIdOffset); 13 for (int i=0;i<numVoices;i++) voices[i]=new Voice(i+1+voiceIdOffset);
24 14
25 sender.setup( host,port ); 15 sender.setup( host,port );
26 receiver.setup( receivePort ); 16 receiver.setup( receivePort );
27 display_font.loadFont("/System/Library/Fonts/HelveticaLight.ttf",32); 17 display_font.loadFont("/System/Library/Fonts/HelveticaLight.ttf",24);
28 } 18 }
29 19
30 melodyTriangle::~melodyTriangle() { 20 melodyTriangle::~melodyTriangle() {
31 printf("Deleting voice objects...\n"); 21 printf("Deleting voice objects...\n");
32 for (int i=0;i<numVoices;i++) delete voices[i]; 22 for (int i=0;i<numVoices;i++) delete voices[i];
35 //-------------------------------------------------------------- 25 //--------------------------------------------------------------
36 void melodyTriangle::setup(){ 26 void melodyTriangle::setup(){
37 ofSetCircleResolution(64); 27 ofSetCircleResolution(64);
38 ofBackground(0,0,0); 28 ofBackground(0,0,0);
39 ofSetWindowTitle("Melody Triangle"); 29 ofSetWindowTitle("Melody Triangle");
40 30 ofSetFrameRate(40); // caps framerate if vertical sync is off.
41 // if vertical sync is off, we can go a bit fast...
42 // this caps the framerate at 40fps.
43 ofSetFrameRate(40);
44 ofEnableSmoothing(); 31 ofEnableSmoothing();
45 32
46 sendReplyTo();
47
48 // Set up triange coordinates. 33 // Set up triange coordinates.
49 // NB. whatever happens here, the triangle must be 34 // NB. whatever happens here, the triangle must be
50 // isosceles and left-right symmetric around x=x1. 35 // isosceles and left-right symmetric around x=x1.
51 // Otherwise the clipping won't work 36 // Otherwise the clipping won't work
52 fitTriangleIn(ofGetWidth(),ofGetHeight()); 37 fitTriangleIn(ofGetWidth(),ofGetHeight());
53 sendCalibrate(); 38 sendCalibrate();
54 39 sendReplyTo();
55 for (int i=0;i<numVoices;i++) voices[i]->setPos(x2+voices[i]->radius+i*30,48); 40
56 voiceGrabbed=-1; 41 voiceGrabbed=-1;
57 } 42 }
58 43
59 //-------------------------------------------------------------- 44 //--------------------------------------------------------------
60 void melodyTriangle::update(){ 45 void melodyTriangle::update(){
78 63
79 if (clipped) { 64 if (clipped) {
80 // check how far we clipped 65 // check how far we clipped
81 if (ofDist(clipx, clipy, mouseX, mouseY) > BUFFER_ZONE) { 66 if (ofDist(clipx, clipy, mouseX, mouseY) > BUFFER_ZONE) {
82 // if far enough, we pop out of triangle and send 67 // if far enough, we pop out of triangle and send
83 // /death <id> 68 sendDeath(vg->id);
84 ofxOscMessage m;
85 m.setAddress( "/death" );
86 m.addIntArg( vg->id );
87 sender.sendMessage( m );
88
89 printf("sent /death %i \n",vg->id);
90 vg->posx=mouseX; 69 vg->posx=mouseX;
91 vg->posy=mouseY; 70 vg->posy=mouseY;
92 vg->inTriangle=false; 71 vg->inTriangle=false;
93 vg->status=Voice::clear; 72 vg->status=Voice::pending;
94 } else { 73 } else {
95 // otherwise, we move to clipped point 74 // otherwise, we move to clipped point
96 constrained=true; 75 constrained=true;
97 vg->posx=clipx; 76 vg->posx=clipx;
98 vg->posy=clipy; 77 vg->posy=clipy;
119 vg->inTriangle=true; 98 vg->inTriangle=true;
120 } 99 }
121 } 100 }
122 101
123 if (vg->inTriangle){ 102 if (vg->inTriangle){
124 sendPosition(*vg); 103 sendPosition(vg);
125 vg->status=Voice::moved; 104 vg->status=Voice::moved;
126 if (sendStart && vg->isActive){ 105 if (sendStart && vg->isActive){
127 ofxOscMessage m; 106 ofxOscMessage m;
128 ///track id x y left right top bottom area 107 ///track id x y left right top bottom area
129 m.setAddress( "/start" ); 108 m.setAddress( "/start" );
139 118
140 119
141 //-------------------------------------------------------------- 120 //--------------------------------------------------------------
142 void melodyTriangle::draw(){ 121 void melodyTriangle::draw(){
143 ofSetLineWidth(2); 122 ofSetLineWidth(2);
144 ofSetColor(80,80,80); 123 ofSetColor(60,60,60);
145 ofFill(); 124 ofFill();
146 ofTriangle(x1, y1, x2, y2, x3, y3); 125 ofTriangle(x1, y1, x2, y2, x3, y3);
126
127 // draw smooth edge, brighter if a token is constrained
147 if (constrained) ofSetColor(255,96,96); 128 if (constrained) ofSetColor(255,96,96);
148
149 // draw smooth edge, brighter if a token is constrained
150 ofNoFill(); 129 ofNoFill();
151 ofTriangle(x1, y1, x2, y2, x3, y3); 130 ofTriangle(x1, y1, x2, y2, x3, y3);
152 131
153 for (int i=0; i<numVoices; i++){ 132 for (int i=0; i<numVoices; i++){
154 (*voices[i]).draw(); 133 voices[i]->draw(voices[i]->isInVoice(mouseX,mouseY));
155 } 134 }
156 135
157 // display message if any 136 // display message if any
158 if (display_frames>0) { 137 if (display_frames!=0) {
138 ofRectangle bbox=display_font.getStringBoundingBox(display_msg,0,0);
159 ofSetColor(220,220,220); 139 ofSetColor(220,220,220);
160 display_font.drawString(display_msg,x2,y1+32); 140 display_font.drawString(display_msg,
161 display_frames--; 141 (ofGetWidth()-bbox.width)/2, (ofGetHeight()-bbox.height)/2);
142 if (display_frames>0) display_frames--;
162 } 143 }
163 } 144 }
164 145
165 bool melodyTriangle::clipToTriangle(int *x, int *y) { 146 bool melodyTriangle::clipToTriangle(int *x, int *y) {
166 bool clipped; 147 bool clipped;
200 181
201 182
202 //- Keyboard ---------------------------------------------------------- 183 //- Keyboard ----------------------------------------------------------
203 184
204 void melodyTriangle::keyReleased(int key){} 185 void melodyTriangle::keyReleased(int key){}
205 void melodyTriangle::keyPressed (int key){ 186 void melodyTriangle::keyPressed(int key){
206 //printf("key %i",key); 187 //printf("key %i",key);
207 if (enableKeys){ 188 if (enableKeys){
208 switch (key) { 189 switch (key) {
209 case ' ': { 190 case ' ': {
210 ofxOscMessage m; 191 ofxOscMessage m;
212 sender.sendMessage(m); 193 sender.sendMessage(m);
213 printf("sent /marker\n"); 194 printf("sent /marker\n");
214 break; 195 break;
215 } 196 }
216 197
217 case '1': 198 case '1': case '2':
218 case '2': 199 case '3': case '4': {
219 case '3':
220 case '4': {
221 int tempo=30 + 30*(key-'1'); 200 int tempo=30 + 30*(key-'1');
222 ofxOscMessage m; 201 ofxOscMessage m;
223 m.setAddress( "/tempo" ); 202 m.setAddress( "/tempo" );
224 m.addIntArg(tempo); 203 m.addIntArg(tempo);
225 sender.sendMessage( m ); 204 sender.sendMessage( m );
226 printf("sent /tempo %d\n",tempo); 205 printf("sent /tempo %d\n",tempo);
227 }
228 break; 206 break;
207 }
229 208
230 case 'c': sendReplyTo(); sendCalibrate(); break; 209 case 'c': sendReplyTo(); sendCalibrate(); break;
231 case 'f': ofToggleFullscreen(); break; 210 case 'F': ofToggleFullscreen(); break;
232 211 case 'R': reset(); break;
233 default: { // otherwise, send key to all active voices 212 case 'Q': ofAppGlutWindow::exitApp();
234 for (int i=0; i<numVoices; i++){ 213
235 if (voices[i]->isInVoice(mouseX,mouseY)){ 214 default: // otherwise, send key to all voices under mouse
236 Voice *v=voices[i]; 215 for (int i=0; i<numVoices; i++)
237 switch (key) { 216 if (voices[i]->isInVoice(mouseX,mouseY))
238 case 'a': { 217 voiceKeypress(voices[i],key);
239 ofxOscMessage m; 218 }
240 const char *addr = v->isActive ? "/stop" : "/start"; 219 } else {
241 v->isActive=!v->isActive; 220 ofxOscMessage m;
242 m.setAddress(addr); 221 m.setAddress( "/key" );
243 m.addIntArg(v->id ); 222 m.addIntArg(key);
244 sender.sendMessage( m ); 223 sender.sendMessage(m);
245 printf("sent %s %i \n",addr,v->id); 224 printf("sent /key %d\n", key);
246 break; 225 }
247 } 226 }
248 case OF_KEY_LEFT: sendShift(v->id,-1,2); break; 227
249 case OF_KEY_RIGHT: sendShift(v->id,1,2); break; 228 void melodyTriangle::voiceKeypress(Voice *v, int key) {
250 case OF_KEY_UP: sendPeriod(v->id,1,2); break; 229 switch (key) {
251 case OF_KEY_DOWN: sendPeriod(v->id,2,1); break; 230 case 'a': {
252 case '.': sendPeriod(v->id,1,3); break; 231 ofxOscMessage m;
253 case ',': sendPeriod(v->id,3,1); break; 232 const char *addr = v->isActive ? "/stop" : "/start";
254 case '+': sendOctave(v->id, ++v->octave); break; 233 v->isActive=!v->isActive;
255 case '-': sendOctave(v->id, --v->octave); break; 234 m.setAddress(addr);
256 case '*': sendAmplitude(v->id, v->louder()); break; 235 m.addIntArg(v->id );
257 case '/': sendAmplitude(v->id, v->quieter()); break; 236 sender.sendMessage( m );
258 default: printf("unrecognised key: %d.\n",key); 237 printf("sent %s %i \n",addr,v->id);
259 } 238 break;
260 } 239 }
261 } 240 case OF_KEY_LEFT: sendShift(v->id,-1,2); break;
262 } 241 case OF_KEY_RIGHT: sendShift(v->id,1,2); break;
263 } 242 case OF_KEY_UP: sendPeriod(v->id,1,2); break;
243 case OF_KEY_DOWN: sendPeriod(v->id,2,1); break;
244 case '.': sendPeriod(v->id,1,3); break;
245 case ',': sendPeriod(v->id,3,1); break;
246 case '+': sendOctave(v->id, ++v->octave); break;
247 case '-': sendOctave(v->id, --v->octave); break;
248 case '*': sendAmplitude(v->id, v->louder()); break;
249 case '/': sendAmplitude(v->id, v->quieter()); break;
250 default: printf("unrecognised key: %d.\n",key);
264 } 251 }
265 } 252 }
266 253
267 //- Mouse ------------------------------------------------------ 254 //- Mouse ------------------------------------------------------
268 255
269 void melodyTriangle::mouseDragged(int x, int y, int button){} 256 void melodyTriangle::mouseDragged(int x, int y, int button){}
270 void melodyTriangle::mouseMoved(int x, int y ){ 257 void melodyTriangle::mouseMoved(int x, int y ){}
258 void melodyTriangle::mousePressed(int x, int y, int button){
271 for (int i=0; i<numVoices;i++){ 259 for (int i=0; i<numVoices;i++){
272 voices[i]->highlight = voices[i]->isInVoice(x,y); 260 if (voices[i]->isInVoice(x,y)) voiceGrabbed=i;
273 }
274 }
275
276 void melodyTriangle::mousePressed(int x, int y, int button){
277
278 for (int i=0; i<numVoices;i++){
279 if (voices[i]->isInVoice(x,y)){
280 voiceGrabbed=i;
281 //printf("grabbed %i",voiceGrabbed);
282 }else{
283 //printf("didnt grab %i",i);
284 }
285 } 261 }
286 } 262 }
287 263
288 void melodyTriangle::mouseReleased(int x, int y, int button){ 264 void melodyTriangle::mouseReleased(int x, int y, int button){
289 voiceGrabbed=-1; 265 voiceGrabbed=-1;
292 //-------------------------------------------------------------- 268 //--------------------------------------------------------------
293 269
294 void melodyTriangle::windowResized(int w, int h){ 270 void melodyTriangle::windowResized(int w, int h){
295 fitTriangleIn(w,h); 271 fitTriangleIn(w,h);
296 sendCalibrate(); 272 sendCalibrate();
297 } 273 reset();
298 274 }
275
276 void melodyTriangle::reset() {
277 voiceGrabbed=-1;
278 for (int i=0;i<numVoices;i++) {
279 Voice *v=voices[i];
280 v->posx=x2+RADIUS+(numVoices-1-i)*36;
281 v->posy=48;
282 v->status=Voice::pending;
283 if (v->inTriangle) {
284 sendDeath(voices[i]->id);
285 v->inTriangle=false;
286 }
287 }
288 }
299 289
300 // OSC Message handling ----------------------------------------- 290 // OSC Message handling -----------------------------------------
291
292 Voice *melodyTriangle::get_voice(int id) throw(bad_voice_id) {
293 if (id<1 || id>numVoices) throw bad_voice_id(id);
294 return voices[id-1];
295 }
301 296
302 void melodyTriangle::handleMessage(ofxOscMessage &m) { 297 void melodyTriangle::handleMessage(ofxOscMessage &m) {
303 string msg_path=m.getAddress(); 298 string msg_path=m.getAddress();
304 299
305 if (msg_path=="/notify") { 300 try {
306 int id=m.getArgAsInt32(0)-1; 301 if (msg_path.compare(0,8,"/notify/")==0) {
307 string st=m.getArgAsString(1); 302 Voice *v=get_voice(m.getArgAsInt32(0));
308 303 if (msg_path=="/notify/status") {
309 if (id>=0 && id<numVoices) { 304 v->status=Voice::stringToStatus(m.getArgAsString(1));
310 Voice *v=voices[id]; 305 } else if (msg_path=="/notify/position") {
311 306 int x=(int)m.getArgAsFloat(1);
312 if (st=="received") v->status=Voice::clear; 307 int y=(int)m.getArgAsFloat(2);
313 else if (st=="pending") v->status=Voice::pending; 308 v->posx=x;
314 else if (st=="requested") v->status=Voice::waiting; 309 v->posy=y;
315 else cout << "** unrecognised voice status: " << st << ".\n"; 310 v->inTriangle=!clipToTriangle(&x,&y);
316 } else { 311 if (voiceGrabbed==v->id) voiceGrabbed=-1;
317 cout << "** voice id "<<id<<" out of range.\n"; 312 } else if (msg_path=="/notify/running") {
318 } 313 v->isActive = m.getArgAsInt32(1) ? true : false;
319 } else if (msg_path=="/display") { 314 } else if (msg_path=="/notify/params") {
320 display_msg=m.getArgAsString(0); 315 v->octave = m.getArgAsInt32(1);
321 display_frames=m.getArgAsInt32(1); 316 v->amplitude = m.getArgAsFloat(2);
322 if (display_frames<0) display_frames=0; 317 }
323 } else { 318 } else if (msg_path=="/display") {
324 string msg_string; 319 display_msg=m.getArgAsString(0);
325 msg_string = m.getAddress(); 320 display_frames=m.getArgAsInt32(1);
326 msg_string += ": "; 321 } else if (msg_path=="/font") {
327 for ( int i=0; i<m.getNumArgs(); i++ ) 322 display_font.loadFont(m.getArgAsString(0),m.getArgAsInt32(1));
328 { 323 } else if (msg_path=="/keyboard") { enableKeys=m.getArgAsInt32(0); }
329 // get the argument type 324 else if (msg_path=="/reset") { reset(); }
330 msg_string += m.getArgTypeName( i ); 325 else if (msg_path=="/fullscreen") { ofSetFullscreen(m.getArgAsInt32(0)); }
331 msg_string += ":"; 326 else if (msg_path=="/quit") { ofAppGlutWindow::exitApp(); }
332 // display the argument - make sure we get the right type 327 else {
333 if( m.getArgType( i ) == OFXOSC_TYPE_INT32 ) 328 cout << m.getAddress();
334 msg_string += ofToString( m.getArgAsInt32( i ) ); 329 for (int i=0; i<m.getNumArgs(); i++) {
335 else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ) 330 cout << " " << m.getArgTypeName(i) << ":";
336 msg_string += ofToString( m.getArgAsFloat( i ) ); 331 switch (m.getArgType(i)) {
337 else if( m.getArgType( i ) == OFXOSC_TYPE_STRING ) 332 case OFXOSC_TYPE_INT32: cout << m.getArgAsInt32(i);
338 msg_string += m.getArgAsString( i ); 333 case OFXOSC_TYPE_FLOAT: cout << m.getArgAsFloat(i);
339 else 334 case OFXOSC_TYPE_STRING: cout << m.getArgAsString(i);
340 msg_string += "unknown"; 335 default: cout << "unknown";
341 } 336 }
342 cout<< msg_string << "\n"; 337 }
338 cout<< "\n";
339 }
340 } catch (std::exception &ex) {
341 cout << "** Error processing OSC message: " << ex.what() << "\n";
343 } 342 }
344 } 343 }
345 344
346 // OSC Message sending ----------------------------------------- 345 // OSC Message sending -----------------------------------------
347 346
348 void melodyTriangle::sendPosition(Voice v){ 347 void melodyTriangle::sendDeath(int id) {
348 ofxOscMessage m;
349 m.setAddress( "/death" );
350 m.addIntArg( id );
351 sender.sendMessage( m );
352 }
353
354 void melodyTriangle::sendPosition(Voice *v){
349 355
350 ofxOscMessage m; 356 ofxOscMessage m;
351 ///track id x y left right top bottom area 357 ///track id x y left right top bottom area
352 m.setAddress( "/track2d" ); 358 m.setAddress( "/track2d" );
353 m.addIntArg( v.id ); 359 m.addIntArg( v->id );
354 m.addIntArg( v.posx ); 360 m.addIntArg( v->posx );
355 m.addIntArg( v.posy ); 361 m.addIntArg( v->posy );
356 sender.sendMessage( m ); 362 sender.sendMessage( m );
357 printf("sent - /track2d %i %i %i\n",v.id,v.posx,v.posy); 363 printf("sent - /track2d %i %i %i\n",v->id,v->posx,v->posy);
358 364
359 } 365 }
360 void melodyTriangle::sendCalibrate(){ 366 void melodyTriangle::sendCalibrate(){
361 ofxOscMessage m; 367 ofxOscMessage m;
362 m.setAddress( "/calibrate" ); 368 m.setAddress( "/calibrate" );
429 435
430 // used for clipping 436 // used for clipping
431 DX13=x3-x1; DY13=y3-y1; 437 DX13=x3-x1; DY13=y3-y1;
432 SQLEN13=DX13*DX13+DY13*DY13; 438 SQLEN13=DX13*DX13+DY13*DY13;
433 } 439 }
440
441 const char *melodyTriangle::bad_voice_id::what() const throw() {
442 std::stringstream out;
443 printf("bad_voice_id(%d).\n",id);
444 out << "Voice id " << id <<" out of range.";
445 return out.str().c_str();
446 }