hekeus@6
|
1 #include "melodyTriangle.h"
|
hekeus@6
|
2 #include <GLUT/GLUT.h>
|
hekeus@6
|
3
|
samer@19
|
4 #define BUFFER_ZONE 50 // have to drag this far to snap out of triange.
|
hekeus@6
|
5 /*
|
hekeus@6
|
6 /birth id
|
hekeus@6
|
7 /death id
|
hekeus@6
|
8 /start id
|
hekeus@6
|
9 /stop id
|
hekeus@6
|
10 /track id x y left right top bottom area
|
hekeus@6
|
11 /tempo
|
hekeus@6
|
12
|
hekeus@6
|
13
|
hekeus@6
|
14 */
|
samer@22
|
15
|
samer@22
|
16 melodyTriangle::melodyTriangle(const char *host, int port, int numVoices,
|
samer@22
|
17 bool enableKeys,int voiceIdOffset,int receivePort):
|
samer@22
|
18 numVoices(numVoices), enableKeys(enableKeys), receivePort(receivePort),
|
samer@22
|
19 display_msg(""),
|
samer@22
|
20 display_frames(0)
|
samer@22
|
21 {
|
hekeus@8
|
22 printf("in constructor: %s %i %i %i %i %i\n",host,port,numVoices,enableKeys,voiceIdOffset,receivePort);
|
samer@22
|
23 for (int i=0;i<numVoices;i++) voices[i]=new Voice(i+1+voiceIdOffset);
|
samer@22
|
24
|
hekeus@6
|
25 sender.setup( host,port );
|
hekeus@8
|
26 receiver.setup( receivePort );
|
samer@22
|
27 display_font.loadFont("/System/Library/Fonts/HelveticaLight.ttf",32);
|
hekeus@6
|
28 }
|
hekeus@6
|
29
|
samer@22
|
30 melodyTriangle::~melodyTriangle() {
|
samer@22
|
31 printf("Deleting voice objects...\n");
|
samer@22
|
32 for (int i=0;i<numVoices;i++) delete voices[i];
|
samer@22
|
33 }
|
samer@22
|
34
|
hekeus@6
|
35 //--------------------------------------------------------------
|
hekeus@6
|
36 void melodyTriangle::setup(){
|
samer@22
|
37 ofSetCircleResolution(64);
|
hekeus@6
|
38 ofBackground(0,0,0);
|
hekeus@6
|
39 ofSetWindowTitle("Melody Triangle");
|
samer@22
|
40
|
samer@18
|
41 // if vertical sync is off, we can go a bit fast...
|
samer@18
|
42 // this caps the framerate at 40fps.
|
samer@18
|
43 ofSetFrameRate(40);
|
samer@18
|
44 ofEnableSmoothing();
|
samer@18
|
45
|
samer@22
|
46 sendReplyTo();
|
samer@22
|
47
|
samer@18
|
48 // Set up triange coordinates.
|
samer@18
|
49 // NB. whatever happens here, the triangle must be
|
samer@18
|
50 // isosceles and left-right symmetric around x=x1.
|
samer@18
|
51 // Otherwise the clipping won't work
|
samer@22
|
52 fitTriangleIn(ofGetWidth(),ofGetHeight());
|
samer@22
|
53 sendCalibrate();
|
hekeus@6
|
54
|
samer@22
|
55 for (int i=0;i<numVoices;i++) voices[i]->setPos(x2+voices[i]->radius+i*30,48);
|
samer@12
|
56 voiceGrabbed=-1;
|
samer@12
|
57 }
|
samer@12
|
58
|
hekeus@6
|
59 //--------------------------------------------------------------
|
hekeus@6
|
60 void melodyTriangle::update(){
|
samer@22
|
61 bool sendStart=false;
|
samer@22
|
62
|
samer@22
|
63 while( receiver.hasWaitingMessages() ) {
|
hekeus@8
|
64 // get the next message
|
hekeus@8
|
65 ofxOscMessage m;
|
hekeus@8
|
66 receiver.getNextMessage( &m );
|
samer@22
|
67 handleMessage(m);
|
hekeus@8
|
68 }
|
samer@15
|
69
|
samer@22
|
70 constrained=false;
|
hekeus@6
|
71 if (voiceGrabbed!=-1){
|
samer@14
|
72 Voice *vg=voices[voiceGrabbed];
|
samer@14
|
73 if (mouseX!=vg->posx || mouseY!=vg->posy){
|
samer@15
|
74 int clipx=mouseX, clipy=mouseY;
|
samer@15
|
75 bool clipped=clipToTriangle(&clipx,&clipy);
|
samer@15
|
76
|
samer@15
|
77 if (vg->inTriangle) {
|
hekeus@6
|
78
|
samer@15
|
79 if (clipped) {
|
samer@15
|
80 // check how far we clipped
|
samer@15
|
81 if (ofDist(clipx, clipy, mouseX, mouseY) > BUFFER_ZONE) {
|
samer@15
|
82 // if far enough, we pop out of triangle and send
|
samer@15
|
83 // /death <id>
|
samer@15
|
84 ofxOscMessage m;
|
samer@15
|
85 m.setAddress( "/death" );
|
samer@15
|
86 m.addIntArg( vg->id );
|
samer@15
|
87 sender.sendMessage( m );
|
samer@22
|
88
|
samer@15
|
89 printf("sent /death %i \n",vg->id);
|
samer@15
|
90 vg->posx=mouseX;
|
samer@15
|
91 vg->posy=mouseY;
|
samer@15
|
92 vg->inTriangle=false;
|
samer@18
|
93 vg->status=Voice::clear;
|
samer@15
|
94 } else {
|
samer@15
|
95 // otherwise, we move to clipped point
|
samer@15
|
96 constrained=true;
|
samer@15
|
97 vg->posx=clipx;
|
samer@15
|
98 vg->posy=clipy;
|
samer@15
|
99 }
|
samer@15
|
100 } else { // not clipped; normal move
|
samer@15
|
101 vg->posx=mouseX;
|
samer@15
|
102 vg->posy=mouseY;
|
samer@15
|
103 }
|
samer@15
|
104 } else { // token was outside triangle
|
samer@15
|
105 vg->posx=mouseX;
|
samer@15
|
106 vg->posy=mouseY;
|
samer@15
|
107 if (!clipped){ // ie mouse now in triangle
|
samer@15
|
108 //birth id
|
hekeus@7
|
109
|
samer@10
|
110 ofxOscMessage m;
|
samer@15
|
111 m.setAddress( "/birth" );
|
samer@14
|
112 m.addIntArg( vg->id );
|
samer@10
|
113 sender.sendMessage( m );
|
samer@15
|
114
|
samer@15
|
115 printf("sent /birth %i \n",vg->id);
|
samer@15
|
116 sendOctave(vg->id,vg->octave);
|
samer@15
|
117 sendAmplitude(vg->id,vg->amplitude);
|
samer@15
|
118 sendStart=true;
|
samer@15
|
119 vg->inTriangle=true;
|
hekeus@6
|
120 }
|
hekeus@6
|
121 }
|
hekeus@6
|
122
|
samer@14
|
123 if (vg->inTriangle){
|
samer@14
|
124 sendPosition(*vg);
|
samer@18
|
125 vg->status=Voice::moved;
|
samer@15
|
126 if (sendStart && vg->isActive){
|
samer@15
|
127 ofxOscMessage m;
|
samer@15
|
128 ///track id x y left right top bottom area
|
samer@15
|
129 m.setAddress( "/start" );
|
samer@15
|
130 m.addIntArg( vg->id );
|
samer@15
|
131 sender.sendMessage( m );
|
samer@15
|
132 printf("sent /start %i \n",vg->id);
|
hekeus@6
|
133 }
|
hekeus@6
|
134 }
|
hekeus@6
|
135 }
|
hekeus@6
|
136 };
|
samer@22
|
137 }
|
samer@10
|
138
|
samer@22
|
139
|
samer@22
|
140
|
samer@22
|
141 //--------------------------------------------------------------
|
samer@22
|
142 void melodyTriangle::draw(){
|
samer@11
|
143 ofSetLineWidth(2);
|
samer@19
|
144 ofSetColor(80,80,80);
|
samer@10
|
145 ofFill();
|
samer@10
|
146 ofTriangle(x1, y1, x2, y2, x3, y3);
|
samer@12
|
147 if (constrained) ofSetColor(255,96,96);
|
samer@10
|
148
|
samer@10
|
149 // draw smooth edge, brighter if a token is constrained
|
samer@10
|
150 ofNoFill();
|
samer@10
|
151 ofTriangle(x1, y1, x2, y2, x3, y3);
|
samer@10
|
152
|
hekeus@6
|
153 for (int i=0; i<numVoices; i++){
|
hekeus@6
|
154 (*voices[i]).draw();
|
hekeus@6
|
155 }
|
hekeus@6
|
156
|
samer@22
|
157 // display message if any
|
samer@22
|
158 if (display_frames>0) {
|
samer@22
|
159 ofSetColor(220,220,220);
|
samer@22
|
160 display_font.drawString(display_msg,x2,y1+32);
|
samer@22
|
161 display_frames--;
|
samer@22
|
162 }
|
hekeus@6
|
163 }
|
hekeus@6
|
164
|
samer@22
|
165 bool melodyTriangle::clipToTriangle(int *x, int *y) {
|
samer@22
|
166 bool clipped;
|
samer@22
|
167
|
samer@22
|
168 if (*y>y2) { // off the bottom
|
samer@22
|
169 clipped=true;
|
samer@22
|
170 *y=y2;
|
samer@22
|
171 if (*x<x2) *x=x2;
|
samer@22
|
172 else if (*x>x3) *x=x3;
|
samer@22
|
173 } else { // have to be a bit cleverer
|
samer@22
|
174 bool reflect=false;
|
samer@22
|
175 if (*x<x1) { // work in reflected coordinates
|
samer@22
|
176 reflect=true;
|
samer@22
|
177 *x=2*x1-*x;
|
samer@22
|
178 }
|
samer@22
|
179
|
samer@22
|
180 int dx=(*x-x1), dy=(*y-y1); // deltas from top
|
samer@22
|
181 if (dx*DY13 > dy*DX13) {
|
samer@22
|
182 // (x,y) must be somewhere right of triangle now
|
samer@22
|
183 clipped=true;
|
samer@22
|
184 int dp=dx*DX13 + dy*DY13;
|
samer@22
|
185 if (dp<0) { *x=x1; *y=y1; } // off the top
|
samer@22
|
186 else if (dp>SQLEN13) { *x=x3; *y=y3; } // off the bottom right
|
samer@22
|
187 else { // project onto right edge
|
samer@22
|
188 *x=x1+dp*DX13/SQLEN13;
|
samer@22
|
189 *y=y1+dp*DY13/SQLEN13;
|
samer@22
|
190 }
|
samer@22
|
191 } else {
|
samer@22
|
192 clipped=false;
|
samer@22
|
193 }
|
samer@22
|
194
|
samer@22
|
195 if (reflect) *x=2*x1 - *x; // reflect back if necessary
|
samer@22
|
196 }
|
samer@22
|
197 return clipped;
|
samer@22
|
198 }
|
samer@22
|
199
|
samer@22
|
200
|
samer@22
|
201
|
samer@22
|
202 //- Keyboard ----------------------------------------------------------
|
samer@22
|
203
|
samer@22
|
204 void melodyTriangle::keyReleased(int key){}
|
hekeus@6
|
205 void melodyTriangle::keyPressed (int key){
|
hekeus@6
|
206 //printf("key %i",key);
|
hekeus@6
|
207 if (enableKeys){
|
samer@12
|
208 switch (key) {
|
samer@12
|
209 case ' ': {
|
samer@12
|
210 ofxOscMessage m;
|
samer@12
|
211 m.setAddress( "/marker" );
|
samer@12
|
212 sender.sendMessage(m);
|
samer@12
|
213 printf("sent /marker\n");
|
samer@12
|
214 break;
|
samer@12
|
215 }
|
samer@12
|
216
|
samer@12
|
217 case '1':
|
samer@12
|
218 case '2':
|
samer@12
|
219 case '3':
|
samer@12
|
220 case '4': {
|
samer@12
|
221 int tempo=30 + 30*(key-'1');
|
samer@12
|
222 ofxOscMessage m;
|
samer@12
|
223 m.setAddress( "/tempo" );
|
samer@12
|
224 m.addIntArg(tempo);
|
samer@12
|
225 sender.sendMessage( m );
|
samer@12
|
226 printf("sent /tempo %d\n",tempo);
|
samer@12
|
227 }
|
samer@12
|
228 break;
|
samer@12
|
229
|
samer@22
|
230 case 'c': sendReplyTo(); sendCalibrate(); break;
|
samer@22
|
231 case 'f': ofToggleFullscreen(); break;
|
samer@12
|
232
|
samer@12
|
233 default: { // otherwise, send key to all active voices
|
samer@12
|
234 for (int i=0; i<numVoices; i++){
|
samer@12
|
235 if (voices[i]->isInVoice(mouseX,mouseY)){
|
samer@12
|
236 Voice *v=voices[i];
|
samer@12
|
237 switch (key) {
|
samer@12
|
238 case 'a': {
|
samer@12
|
239 ofxOscMessage m;
|
samer@12
|
240 const char *addr = v->isActive ? "/stop" : "/start";
|
samer@12
|
241 v->isActive=!v->isActive;
|
samer@12
|
242 m.setAddress(addr);
|
samer@12
|
243 m.addIntArg(v->id );
|
samer@12
|
244 sender.sendMessage( m );
|
samer@12
|
245 printf("sent %s %i \n",addr,v->id);
|
samer@12
|
246 break;
|
samer@12
|
247 }
|
samer@12
|
248 case OF_KEY_LEFT: sendShift(v->id,-1,2); break;
|
samer@12
|
249 case OF_KEY_RIGHT: sendShift(v->id,1,2); break;
|
samer@12
|
250 case OF_KEY_UP: sendPeriod(v->id,1,2); break;
|
samer@12
|
251 case OF_KEY_DOWN: sendPeriod(v->id,2,1); break;
|
samer@12
|
252 case '.': sendPeriod(v->id,1,3); break;
|
samer@12
|
253 case ',': sendPeriod(v->id,3,1); break;
|
samer@12
|
254 case '+': sendOctave(v->id, ++v->octave); break;
|
samer@12
|
255 case '-': sendOctave(v->id, --v->octave); break;
|
samer@13
|
256 case '*': sendAmplitude(v->id, v->louder()); break;
|
samer@13
|
257 case '/': sendAmplitude(v->id, v->quieter()); break;
|
samer@13
|
258 default: printf("unrecognised key: %d.\n",key);
|
samer@12
|
259 }
|
hekeus@6
|
260 }
|
hekeus@6
|
261 }
|
hekeus@6
|
262 }
|
hekeus@6
|
263 }
|
hekeus@6
|
264 }
|
hekeus@6
|
265 }
|
hekeus@6
|
266
|
samer@22
|
267 //- Mouse ------------------------------------------------------
|
hekeus@6
|
268
|
samer@22
|
269 void melodyTriangle::mouseDragged(int x, int y, int button){}
|
hekeus@6
|
270 void melodyTriangle::mouseMoved(int x, int y ){
|
hekeus@6
|
271 for (int i=0; i<numVoices;i++){
|
samer@16
|
272 voices[i]->highlight = voices[i]->isInVoice(x,y);
|
hekeus@6
|
273 }
|
hekeus@6
|
274 }
|
hekeus@6
|
275
|
hekeus@6
|
276 void melodyTriangle::mousePressed(int x, int y, int button){
|
hekeus@6
|
277
|
hekeus@6
|
278 for (int i=0; i<numVoices;i++){
|
samer@16
|
279 if (voices[i]->isInVoice(x,y)){
|
hekeus@6
|
280 voiceGrabbed=i;
|
hekeus@6
|
281 //printf("grabbed %i",voiceGrabbed);
|
hekeus@6
|
282 }else{
|
hekeus@6
|
283 //printf("didnt grab %i",i);
|
hekeus@6
|
284 }
|
hekeus@6
|
285 }
|
hekeus@6
|
286 }
|
hekeus@6
|
287
|
samer@22
|
288 void melodyTriangle::mouseReleased(int x, int y, int button){
|
samer@22
|
289 voiceGrabbed=-1;
|
hekeus@6
|
290 }
|
hekeus@6
|
291
|
hekeus@6
|
292 //--------------------------------------------------------------
|
samer@22
|
293
|
hekeus@6
|
294 void melodyTriangle::windowResized(int w, int h){
|
samer@22
|
295 fitTriangleIn(w,h);
|
samer@22
|
296 sendCalibrate();
|
samer@22
|
297 }
|
hekeus@6
|
298
|
samer@22
|
299
|
samer@22
|
300 // OSC Message handling -----------------------------------------
|
samer@22
|
301
|
samer@22
|
302 void melodyTriangle::handleMessage(ofxOscMessage &m) {
|
samer@22
|
303 string msg_path=m.getAddress();
|
samer@22
|
304
|
samer@22
|
305 if (msg_path=="/notify") {
|
samer@22
|
306 int id=m.getArgAsInt32(0)-1;
|
samer@22
|
307 string st=m.getArgAsString(1);
|
samer@22
|
308
|
samer@22
|
309 if (id>=0 && id<numVoices) {
|
samer@22
|
310 Voice *v=voices[id];
|
samer@22
|
311
|
samer@22
|
312 if (st=="received") v->status=Voice::clear;
|
samer@22
|
313 else if (st=="pending") v->status=Voice::pending;
|
samer@22
|
314 else if (st=="requested") v->status=Voice::waiting;
|
samer@22
|
315 else cout << "** unrecognised voice status: " << st << ".\n";
|
samer@22
|
316 } else {
|
samer@22
|
317 cout << "** voice id "<<id<<" out of range.\n";
|
samer@22
|
318 }
|
samer@22
|
319 } else if (msg_path=="/display") {
|
samer@22
|
320 display_msg=m.getArgAsString(0);
|
samer@22
|
321 display_frames=m.getArgAsInt32(1);
|
samer@22
|
322 if (display_frames<0) display_frames=0;
|
samer@22
|
323 } else {
|
samer@22
|
324 string msg_string;
|
samer@22
|
325 msg_string = m.getAddress();
|
samer@22
|
326 msg_string += ": ";
|
samer@22
|
327 for ( int i=0; i<m.getNumArgs(); i++ )
|
samer@22
|
328 {
|
samer@22
|
329 // get the argument type
|
samer@22
|
330 msg_string += m.getArgTypeName( i );
|
samer@22
|
331 msg_string += ":";
|
samer@22
|
332 // display the argument - make sure we get the right type
|
samer@22
|
333 if( m.getArgType( i ) == OFXOSC_TYPE_INT32 )
|
samer@22
|
334 msg_string += ofToString( m.getArgAsInt32( i ) );
|
samer@22
|
335 else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
|
samer@22
|
336 msg_string += ofToString( m.getArgAsFloat( i ) );
|
samer@22
|
337 else if( m.getArgType( i ) == OFXOSC_TYPE_STRING )
|
samer@22
|
338 msg_string += m.getArgAsString( i );
|
samer@22
|
339 else
|
samer@22
|
340 msg_string += "unknown";
|
samer@22
|
341 }
|
samer@22
|
342 cout<< msg_string << "\n";
|
samer@22
|
343 }
|
hekeus@6
|
344 }
|
samer@22
|
345
|
samer@22
|
346 // OSC Message sending -----------------------------------------
|
samer@22
|
347
|
samer@22
|
348 void melodyTriangle::sendPosition(Voice v){
|
samer@22
|
349
|
samer@22
|
350 ofxOscMessage m;
|
samer@22
|
351 ///track id x y left right top bottom area
|
samer@22
|
352 m.setAddress( "/track2d" );
|
samer@22
|
353 m.addIntArg( v.id );
|
samer@22
|
354 m.addIntArg( v.posx );
|
samer@22
|
355 m.addIntArg( v.posy );
|
samer@22
|
356 sender.sendMessage( m );
|
samer@22
|
357 printf("sent - /track2d %i %i %i\n",v.id,v.posx,v.posy);
|
samer@22
|
358
|
samer@22
|
359 }
|
samer@22
|
360 void melodyTriangle::sendCalibrate(){
|
samer@22
|
361 ofxOscMessage m;
|
samer@22
|
362 m.setAddress( "/calibrate" );
|
samer@22
|
363 m.addIntArg( x1 );
|
samer@22
|
364 m.addIntArg( y1 );
|
samer@22
|
365 m.addIntArg( x2 );
|
samer@22
|
366 m.addIntArg( y2 );
|
samer@22
|
367 m.addIntArg( x3 );
|
samer@22
|
368 m.addIntArg( y3 );
|
samer@22
|
369 sender.sendMessage( m );
|
samer@22
|
370 printf("sent /calibrate %i %i %i %i %i %i\n",x1,y1,x2,y2,x3,y3);
|
samer@22
|
371 }
|
samer@22
|
372
|
samer@22
|
373 void melodyTriangle::sendReplyTo(){
|
samer@22
|
374 ofxOscMessage m;
|
samer@22
|
375 m.setAddress( "/reply_to" );
|
samer@22
|
376 m.addIntArg( receivePort );
|
samer@22
|
377 sender.sendMessage( m );
|
samer@22
|
378 printf("sent /reply_to %i\n",receivePort);
|
samer@22
|
379 }
|
samer@22
|
380
|
samer@22
|
381 void melodyTriangle::sendPeriod(int id, int num, int den){
|
samer@22
|
382 ofxOscMessage m;
|
samer@22
|
383 m.setAddress("/period");
|
samer@22
|
384 m.addIntArg(id);
|
samer@22
|
385 m.addIntArg(num);
|
samer@22
|
386 m.addIntArg(den);
|
samer@22
|
387 sender.sendMessage(m);
|
samer@22
|
388 printf("sent /period %i %i %i\n",id,num,den);
|
samer@22
|
389 }
|
samer@22
|
390
|
samer@22
|
391 void melodyTriangle::sendShift(int id, int num, int den){
|
samer@22
|
392 ofxOscMessage m;
|
samer@22
|
393 m.setAddress("/shift");
|
samer@22
|
394 m.addIntArg(id);
|
samer@22
|
395 m.addIntArg(num);
|
samer@22
|
396 m.addIntArg(den);
|
samer@22
|
397 sender.sendMessage(m);
|
samer@22
|
398 printf("sent /shift %i %i %i\n",id,num,den);
|
samer@22
|
399 }
|
samer@22
|
400
|
samer@22
|
401 void melodyTriangle::sendOctave(int id, int oct){
|
samer@22
|
402 ofxOscMessage m;
|
samer@22
|
403 m.setAddress("/octave");
|
samer@22
|
404 m.addIntArg(id);
|
samer@22
|
405 m.addIntArg(oct);
|
samer@22
|
406 sender.sendMessage(m);
|
samer@22
|
407 printf("sent /octave %i %i\n",id,oct);
|
samer@22
|
408 }
|
samer@22
|
409
|
samer@22
|
410 void melodyTriangle::sendAmplitude(int id, float amp){
|
samer@22
|
411 ofxOscMessage m;
|
samer@22
|
412 m.setAddress("/amplitude");
|
samer@22
|
413 m.addIntArg(id);
|
samer@22
|
414 m.addFloatArg(amp);
|
samer@22
|
415 sender.sendMessage(m);
|
samer@22
|
416 printf("sent /amplitude %i %1.3f\n",id,amp);
|
samer@22
|
417 }
|
samer@22
|
418
|
samer@22
|
419
|
samer@22
|
420 void melodyTriangle::fitTriangleIn(int width, int height) {
|
samer@22
|
421 int triHeight = height*0.75;
|
samer@22
|
422 int triHalfWidth = triHeight/sqrt(3);
|
samer@22
|
423
|
samer@22
|
424 x1=width/2;
|
samer@22
|
425 x2=x1-triHalfWidth;
|
samer@22
|
426 x3=x1+triHalfWidth;
|
samer@22
|
427 y1=(height-triHeight)/2;
|
samer@22
|
428 y2=y3=height - (height-triHeight)/2;
|
samer@22
|
429
|
samer@22
|
430 // used for clipping
|
samer@22
|
431 DX13=x3-x1; DY13=y3-y1;
|
samer@22
|
432 SQLEN13=DX13*DX13+DY13*DY13;
|
samer@22
|
433 }
|