comparison SearchMessageOrganiser.h @ 8:d59de9fd3496

Refactored messageController, ready for instroduction of other stages and tests.
author Robert Tubb <rt300@eecs.qmul.ac.uk>
date Fri, 17 Oct 2014 16:35:22 +0100
parents
children d5e928887f51
comparison
equal deleted inserted replaced
7:4e00f92567d9 8:d59de9fd3496
1 //
2 // SearchMessageOrganiser.h
3 // riftathon
4 //
5 // Created by Robert Tubb on 17/10/2014.
6 //
7 //
8
9 #ifndef __riftathon__SearchMessageOrganiser__
10 #define __riftathon__SearchMessageOrganiser__
11
12 #include <iostream>
13 #include "MessageOrganiser.h"
14
15 class SearchMessageOrganiser : public MessageOrganiser {
16
17 public:
18
19
20
21 protected:
22
23
24 TimeController altPlaybackController;
25
26 TestController* testController;
27 Buttron* newTestButton;
28 //Buttron* submitButton;
29
30 ButtonPanel* bottomPanel; // shows during test : play buttons and submit
31 Buttron* targetPlayButton; // so we can hide target in memory test. this pointer stuff is getting out of hand
32 CountdownText* countdownPanel;
33 TargetSymbol* targetSymbol;
34 Leap3DBoxGL* box3D;
35 TextPanel* scorePanel;
36 TextPanel* finishPanel;
37 AppModeChangeFunction testAppModeChange;
38
39 //int scoreRunningTotal;
40 TimerID currentSoundPlayTimer;
41
42 int alternationSpeed; // ms between cand and target
43 bool playingAlternating;
44
45 bool okToGetLeapMidi;
46
47
48 // protected methods
49 void testsFinished(){
50 panel->hide();
51 bottomPanel->hide();
52 newTestButton->hide();
53
54 vector<int> eData;
55 eData.push_back(testController->getScoreRunningTotal());
56 eventLogger.logEvent(ALL_TESTS_COMPLETED, eData);
57
58 // TODO set final score screen txt to testController->getScoreRunningTotal()
59 finishPanel->show();
60
61 string user = eventLogger.getUsername();
62 stringstream s;
63 s << "Experiment completed"
64 << endl << endl
65 << "You scored: " << testController->getScoreRunningTotal() << " well done " << user << endl << endl
66 << "to retake test please close " << endl << endl
67 << "the app and restart with username<num test>";
68 finishPanel->setText(s.str());
69 // get test app to do something...
70
71 eventLogger.saveSessionToFile();
72 };
73
74 void setupNewTest(){
75 // get mapping for new test and make sure we have right controls and stuff
76
77
78 Test newTest = testController->goToNextTest();
79
80 // V0.2 put details about what kind of test it is
81 vector<int> eData;
82 eData.push_back(newTest.isPractice());
83 eData.push_back(newTest.isWithHint());
84 eData.push_back(newTest.isMemoryTest());
85 eventLogger.logEvent(NEW_TEST, eData);
86
87
88 vector<int> mappingIDsForChangeableParams = setSynthsUpForNewTest(newTest);
89
90 vector<UIElement*> UIElemHandles = panel->generateControls(testController->getCurrentListOfControls(), testController->getCurrentPanelType());
91
92 mapUIToNewTestParams(UIElemHandles, mappingIDsForChangeableParams);
93
94 countdownPanel->setTestTypeString(newTest.getTestTypeAdvanceWarning());
95
96
97 };
98 void startNewTest(){
99 Test t = testController->getCurrentTest();
100
101 countdownPanel->hide();
102 panel->show();
103 panel->setActive(true);
104 if(t.isWithHint()){
105 panel->showHint(true);
106 }
107
108 bottomPanel->show();
109 targetPlayButton->show(); // incase it was memory test
110 if (t.isMemoryTest()){
111 targetPlayButton->setLabel("Memorise!");
112 }else{
113 targetPlayButton->setLabel("Target");
114 }
115 //startAlternatingPlayback();
116 //timeController.scheduleEvent(boost::bind(&MessageOrganiser::sendSynthValuesAgain, this), 200);
117 timeController.startStopwatch();
118 eventLogger.logEvent(TEST_TIMER_STARTED);
119
120
121 if(t.getListOfControlTypes()[0] == LEAP3D){
122 okToGetLeapMidi = true;
123 }
124 };
125
126 vector<int> setSynthsUpForNewTest(Test newTest){
127 targetSynth.setAllParams(newTest.getTargetValues());
128 eventLogger.logEvent(TARGET_PARAM_SET, newTest.getTargetValues()); // unless something goes wrong in setAllParams
129
130 candidateSynth.setAllParams(newTest.getStartingCandidateValues());
131 eventLogger.logEvent(CANDIDATE_PARAM_SET,newTest.getStartingCandidateValues());
132
133 // eventLogger.logEvent(NEW_TARGET_PARAMS, vector<int> );
134 // eventLogger.logEvent(NEW_CANDIDATE_PARAMS, vector<int> );
135
136 vector<int> mids = candidateSynth.getMappingIDForIndices(newTest.getChangeableIndices());
137
138 eventLogger.logEvent(CANDIDATE_CHANGEABLE_IDX, newTest.getChangeableIndices());
139 eventLogger.logEvent(CANDIDATE_MAPPING_IDS, mids);
140
141 return mids;
142 };
143
144
145 // could have been cleverer. takes forever due to searching ???
146 void mapUIToNewTestParams(vector<UIElement*> elems, vector<int> mids){
147
148 vector<UIElement*>::iterator elit;
149 vector<int> typeListLog;
150 int i = 0;
151 for(elit=elems.begin(); elit<elems.end();elit++){
152 if ( (*elit)->getType() == XYPAD){
153 if(i+1 >= mids.size()){
154 cout << "ERROR ERROR: too many controls for mapping IDs" << endl;
155 }
156
157 ButtronXY* theXY = (ButtronXY*)(*elit);
158 mapXYToParams(theXY, mids[i], mids[i+1]);
159 theXY->setValueAndScale(candidateSynth.getParamValueForID(mids[i]), candidateSynth.getParamValueForID(mids[i+1]));
160 theXY->setHintValue(targetSynth.getParamValueFromName(candidateSynth.getNameForMappingID(mids[i]))
161 ,targetSynth.getParamValueFromName(candidateSynth.getNameForMappingID(mids[i+1])));
162 i+=2;
163 typeListLog.push_back(int(XYPAD));
164 }else if ( (*elit)->getType() == SLIDER){
165 if(i >= mids.size()){
166
167 cout << "ERROR ERROR: too many controls for mapping IDs: " << mids.size() << endl;
168 }
169
170 ButtronSlider* theSlider = (ButtronSlider*)(*elit);
171 mapControlToParam((*elit), mids[i]);
172 theSlider->setValueAndScale(candidateSynth.getParamValueForID(mids[i]));
173 cout << "Hint Value " << targetSynth.getParamValueFromName(candidateSynth.getNameForMappingID(mids[i])) << endl;
174 theSlider->setHintValue(targetSynth.getParamValueFromName(candidateSynth.getNameForMappingID(mids[i])));
175 i++;
176 typeListLog.push_back(int(SLIDER));
177 }else if ( (*elit)->getType() == LEAP3D ){
178 set3Dbox((Leap3DBoxGL*)(*elit));
179 // UH
180 string nameX = candidateSynth.getNameForMappingID(mids[i]);
181 box3D->setHintValue(0,targetSynth.getParamValueFromName(nameX));
182 box3D->setValueAndScale(0, candidateSynth.getParamValueForID(mids[i]));
183 i++;
184
185 string nameY = candidateSynth.getNameForMappingID(mids[i]);
186 box3D->setHintValue(1,targetSynth.getParamValueFromName(nameY));
187 box3D->setValueAndScale(1, candidateSynth.getParamValueForID(mids[i]));
188 i++;
189
190 string nameZ = candidateSynth.getNameForMappingID(mids[i]);
191 box3D->setHintValue(2,targetSynth.getParamValueFromName(nameZ));
192 box3D->setValueAndScale(2, candidateSynth.getParamValueForID(mids[i]));
193 i++;
194
195
196 box3D->setLabels(nameX,nameY,nameZ);
197 typeListLog.push_back(int(LEAP3D));
198
199 }else{
200 cout << "ERROR ERROR: ui type not handled my mapping function !" << endl;
201 }
202 }
203
204 eventLogger.logEvent(CONTROL_LIST,typeListLog);
205 };
206
207 // TODO - no, triggering playback needs to be logged
208 void startAlternatingPlayback(){
209
210 cout << "start alt playback" << endl;
211 // use our special timer to fire off play to pd
212 // sets off timed alternating playback
213
214 playAlternating();
215 playingAlternating = true;
216
217 };
218 void stopAlternatingPlayback(){
219 cout << "stop alt playback" << endl;
220 // kill the alternation
221 timeController.cancelEvent(currentSoundPlayTimer);
222 playingAlternating = false;
223 };
224
225 void playAlternating(){
226
227 static bool alt;
228 int nextTime;
229 if (alt){
230 targetSynth.trigger();
231 // flash the target thingy
232 targetSymbol->flash();
233 nextTime = alternationSpeed*1.503; // gap after target
234 }else{
235 sendSynthValuesAgain(); // and again and again
236 candidateSynth.trigger();
237 panel->flash();
238 nextTime = alternationSpeed;
239 }
240 alt = !alt;
241 candidateSynth.setNoteLength(alternationSpeed);
242 targetSynth.setNoteLength(alternationSpeed); // could be user alterable
243 currentSoundPlayTimer = timeController.scheduleEvent(boost::bind(&SearchMessageOrganiser::playAlternating,this), nextTime);
244
245
246
247 };
248
249 void delayedShowNewTest(){
250 newTestButton->show();
251 // JUST IN CASE IT CRASHES near end...
252 //eventLogger.saveSessionToFile();
253 };
254 void submitSingleControl(){
255 // if last one
256 // submitPressed()
257
258 // else
259
260 // grey out that slider,
261 // activate next slider and show it's button (same button but moved!???)
262
263 };
264
265
266 void submitPressed(){
267
268 // depending on mode go to next control
269 // if(testController->getCurrentPanelType() == SEQUENTIAL){
270 // submitSingleControl();
271 // return;
272 // }
273 // otherwise do this other - or call
274
275 okToGetLeapMidi = false;
276
277 TimerMillisec timeTaken = timeController.stopStopwatch();
278 vector<int> answer = candidateSynth.getAllParamValues();
279
280 eventLogger.logEvent(SUBMIT_PRESSED, answer); //, answer, scoreRunningTotal, time taken (why not?));
281
282 TestResult result = testController->submitAnswer(answer, timeTaken); // TODO returns all the results
283
284 vector<int> logResult;
285 logResult.push_back(result.realDistanceToTarget*1000); // measured in milliCC !??!
286 logResult.push_back(result.timeTaken); // milliseconds
287 logResult.push_back(result.score);
288 logResult.push_back(result.targetBandHit);
289 logResult.push_back(result.timeWindowHit);
290
291 eventLogger.logEvent(DISTANCE_TIME_SCORE, logResult);
292
293
294 // gui stuff - different controller?
295 panel->setActive(false);
296 panel->showHint(true); // add some encouraging feedback to hint
297 bottomPanel->hide();
298
299 showScoreForTest(result);
300
301 stopAlternatingPlayback();
302
303 // was it the final sumbit?
304 if(testController->isLastTest()){
305 // thats it - show a final score screen etc
306 timeController.scheduleEvent(boost::bind(&SearchMessageOrganiser::testsFinished, this), 500);
307 return;
308 }else{
309 timeController.scheduleEvent(boost::bind(&SearchMessageOrganiser::delayedShowNewTest, this), 300);
310 }
311
312
313 };
314
315 void showScoreForTest(TestResult result){
316 scorePanel->setText(result.displayText);
317 scorePanel->show();
318
319 ofColor c;
320 if(result.targetBandHit == 1){
321 // yellow red blue
322 c = ofColor(255,255,0,255);
323 }else if(result.targetBandHit == 2){
324 c = ofColor(255,0,0,255);
325 }else if(result.targetBandHit == 3){
326 c = ofColor(45,45,255,255);
327 }else if(result.targetBandHit == 4){
328 c = ofColor(0,255,0,255);
329 }else{
330 c = ofColor(150,235,200,255);
331 }
332 scorePanel->setColor(c);
333 panel->setHintColor(c);
334 };
335
336 public:
337 void init( PDSynthWrapper& cs, PDSynthWrapper& ts){
338
339 testController = new TestController;
340
341 MessageOrganiser::init(cs,ts);
342
343 currentSoundPlayTimer = -1;
344 okToGetLeapMidi = false;
345
346 alternationSpeed = 200;
347
348 candidateSynth.setNoteLength(alternationSpeed);
349 targetSynth.setNoteLength(alternationSpeed);
350
351 playingAlternating = false;
352
353
354 verdBig.loadFont("verdana.ttf", 18, true, true);
355 verdBig.setLineHeight(18.0f);
356 verdBig.setLetterSpacing(1.037);
357 };
358
359 //------------------------------------------------------------------------
360 void drawScore(){
361 ofColor txtCol = ofColor(150,235,200,255);
362
363 int score = getScore();
364 stringstream msg;
365
366 msg << "Test: " << testController->getCurrentTestLetter();
367 ofSetColor(txtCol);
368 verdBig.drawString(msg.str(), 40, 140);
369 msg.str(std::string());
370
371 msg << "Score: " << score;
372 verdBig.drawString(msg.str(), 240, 140);
373 msg.str(std::string());
374
375 pair<int,int> time;
376 time = getTime();
377 msg << "Time taken: " << time.first << ":" << time.second << endl;
378 verdBig.drawString(msg.str(), 600, 140);
379
380 }
381
382 void setNewTestButton(Buttron * ntb){
383 newTestButton = ntb;
384 };
385 void set3Dbox(Leap3DBoxGL* box){
386 box3D = box;
387 };
388 void setBottomPanel(ButtonPanel * ntb){
389 bottomPanel = ntb;
390 };
391 void setControlPanel(SliderPanel* p){
392 panel = p;
393
394 };
395 void setCountdownPanel(CountdownText* cd){
396 countdownPanel = cd;
397 };
398 void setTargetSymbol(TargetSymbol* ts){
399 targetSymbol = ts;
400 };
401 void setScorePanel(TextPanel* tp){
402 scorePanel = tp;
403 };
404 void setFinishPanel(TextPanel* fp){
405 finishPanel = fp;
406 }
407 void setTargetButton(Buttron* tb){
408 targetPlayButton = tb;
409 }
410 int getScore(){
411 return testController->getScoreRunningTotal();
412 };
413
414 pair<int,int> getTime(){
415 TimerMillisec tms = timeController.getStopwatchElapsedTime();
416 int s = int(tms/1000);
417 int hs = int((tms%1000)/10);
418 pair<int,int> p(s,hs);
419 return p;
420 };
421 void countdownToNewTest(){
422
423 panel->hide();
424 panel->setActive(false);
425 scorePanel->hide();
426 bottomPanel->hide();
427 newTestButton->hide();
428
429 // set up stuff
430 setupNewTest();
431 eventLogger.logEvent(COUNTDOWN_INITIATED);
432
433 countdownPanel->showAndStart(3);
434
435 timeController.scheduleEvent(boost::bind(&SearchMessageOrganiser::startNewTest, this), 3000);
436
437 };
438
439 void playTargetButtonPressed(){
440
441 static int numPlays = 3;
442
443 Test* t = testController->getCurrentTestPtr();
444 if (!t->checkTargetPlaysRemaining()){
445 cout << t->getTargetPlaysLeft() << endl;
446
447 sendSynthValuesAgain();
448 targetSynth.trigger();
449 eventLogger.logEvent(TARGET_PLAYED);
450 targetPlayButton->hide();
451 return;
452
453 }
454 cout << t->getTargetPlaysLeft() << endl;
455
456 sendSynthValuesAgain();
457 targetSynth.trigger();
458 eventLogger.logEvent(TARGET_PLAYED);
459
460 return;
461 }
462 void playCandidateButtonPressed(){
463 //
464 }
465 void buttonPressCallback(int mappingID, int value){
466 if(mappingID == VOLUME_CHANGE_ID){
467 targetSynth.sendVolume(value);
468 candidateSynth.sendVolume(value);
469
470 }
471 if(mappingID == SPEED_CHANGE_ID){
472 alternationSpeed = 2*(140 - value);
473 vector<int> eData;
474 eData.push_back(alternationSpeed);
475 eventLogger.logEvent(SPEED_CHANGED, eData);
476 }
477 if(mappingID == NEW_TEST_ID){
478 countdownToNewTest();
479 return;
480 }
481 if (mappingID == START_ALTERNATE_ID){
482 if(!playingAlternating){
483 startAlternatingPlayback();
484
485 }else{
486 stopAlternatingPlayback();
487 }
488 return;
489 }
490
491 if (mappingID == RANDOMISE_TARGET_ID){ // bleyeueurrrr
492 targetSynth.randomiseParams();
493 return;
494 }
495 if (mappingID == TRIGGER_TARGET_ID){
496 playTargetButtonPressed();
497
498 }
499 if (mappingID == TRIGGER_CANDIDATE_ID){
500 // log event
501 sendSynthValuesAgain();
502 candidateSynth.trigger();
503 eventLogger.logEvent(CANDIDATE_PLAYED);
504 // flash panel?
505 panel->flash();
506 return;
507 }
508 if (mappingID == SUBMIT_CANDIDATE){
509 // log event
510 submitPressed();
511
512 return;
513 }
514 if (mappingID == CRAP_TEST_ID){
515 // this is rubbish! send a log of target values, and mapping ids
516 vector<int> data;
517 vector<int> tvals = targetSynth.getAllParamValues();
518 vector<int> pidx = testController->getCurrentChangeableParams();
519 data.insert(data.end(), tvals.begin(), tvals.end());
520 data.insert(data.end(), pidx.begin(), pidx.end());
521
522 eventLogger.logEvent(CRAP_TEST, data);
523 }
524 if(mappingID == SHOW_HIDE_PANEL){
525 static bool showing;
526
527 if(showing){
528 cout << " showing"<<endl;
529
530 panel->show();
531
532 }else{
533 cout << " hiding"<<endl;
534 panel->hide();
535 }
536 showing = !showing;
537 }
538 if(mappingID == SHOW_HIDE_HINT){
539 static bool showingHint;
540 if(showingHint){
541 panel->showHint(false);
542 showingHint = false;
543 }else{
544 panel->showHint(true);
545 showingHint = true;
546 }
547 }
548 if(mappingID == SAVE_PRESET_HIT){
549 expPresetManager.savePreset("blah", candidateSynth.getAllParamValues());
550
551 }
552 if(mappingID == RECALL_PRESET_HIT){
553
554 loadPreset("blah");
555 //candidateSynth.startMetronome();
556
557 }
558 }
559 //TODO move this
560 void loadPreset(string pname){
561
562 vector<int> values = expPresetManager.recallPreset(pname);
563 if (values.size()){
564 candidateSynth.setAllParams(values);
565 setAllSlidersToValues(values);
566 }else{
567 cout << "ERROR, no preset found" << endl;
568 }
569 }
570 // called from UI
571 // OVERLOADED
572 void paramChangeCallback(int mappingID, int value){
573 candidateSynth.paramChangeCallback(mappingID, value);
574 vector<int> evtData;
575 evtData.push_back(mappingID); // or just index?
576 evtData.push_back(value);
577
578 eventLogger.logEvent(CANDIDATE_PARAM_ADJUSTED, evtData);
579 };
580
581
582 void midiFromLeap(int ctl_num, int ctl_val){
583
584 if (!okToGetLeapMidi){
585 return;
586 }
587
588 Test *theTest = testController->getCurrentTestPtr();
589 if (theTest == NULL) return;
590
591 vector<int> ci = theTest->getChangeableIndices();
592 vector<int> mids = candidateSynth.getMappingIDForIndices(ci);
593 if (ctl_num >= mids.size() || ctl_num < 0) return;
594
595 candidateSynth.paramChangeCallback(mids[ctl_num], ctl_val);
596
597 setUIToParam(ctl_num, ctl_val);
598
599 vector<int> evtData;
600 evtData.push_back(mids[ctl_num]); // or just index?
601 evtData.push_back(ctl_val);
602
603 eventLogger.logEvent(CANDIDATE_PARAM_ADJUSTED, evtData);
604
605 }
606
607
608 ofTrueTypeFont verdBig;
609
610 };
611
612 #endif /* defined(__riftathon__SearchMessageOrganiser__) */