Mercurial > hg > screen-ui
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 } |