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