Mercurial > hg > tweakathon2ios
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SearchMessageOrganiser.h Fri Oct 17 16:35:22 2014 +0100 @@ -0,0 +1,612 @@ +// +// SearchMessageOrganiser.h +// riftathon +// +// Created by Robert Tubb on 17/10/2014. +// +// + +#ifndef __riftathon__SearchMessageOrganiser__ +#define __riftathon__SearchMessageOrganiser__ + +#include <iostream> +#include "MessageOrganiser.h" + +class SearchMessageOrganiser : public MessageOrganiser { + +public: + + + +protected: + + + TimeController altPlaybackController; + + TestController* testController; + Buttron* newTestButton; + //Buttron* submitButton; + + ButtonPanel* bottomPanel; // shows during test : play buttons and submit + Buttron* targetPlayButton; // so we can hide target in memory test. this pointer stuff is getting out of hand + CountdownText* countdownPanel; + TargetSymbol* targetSymbol; + Leap3DBoxGL* box3D; + TextPanel* scorePanel; + TextPanel* finishPanel; + AppModeChangeFunction testAppModeChange; + + //int scoreRunningTotal; + TimerID currentSoundPlayTimer; + + int alternationSpeed; // ms between cand and target + bool playingAlternating; + + bool okToGetLeapMidi; + + +// protected methods + void testsFinished(){ + panel->hide(); + bottomPanel->hide(); + newTestButton->hide(); + + vector<int> eData; + eData.push_back(testController->getScoreRunningTotal()); + eventLogger.logEvent(ALL_TESTS_COMPLETED, eData); + + // TODO set final score screen txt to testController->getScoreRunningTotal() + finishPanel->show(); + + string user = eventLogger.getUsername(); + stringstream s; + s << "Experiment completed" + << endl << endl + << "You scored: " << testController->getScoreRunningTotal() << " well done " << user << endl << endl + << "to retake test please close " << endl << endl + << "the app and restart with username<num test>"; + finishPanel->setText(s.str()); + // get test app to do something... + + eventLogger.saveSessionToFile(); + }; + + void setupNewTest(){ + // get mapping for new test and make sure we have right controls and stuff + + + Test newTest = testController->goToNextTest(); + + // V0.2 put details about what kind of test it is + vector<int> eData; + eData.push_back(newTest.isPractice()); + eData.push_back(newTest.isWithHint()); + eData.push_back(newTest.isMemoryTest()); + eventLogger.logEvent(NEW_TEST, eData); + + + vector<int> mappingIDsForChangeableParams = setSynthsUpForNewTest(newTest); + + vector<UIElement*> UIElemHandles = panel->generateControls(testController->getCurrentListOfControls(), testController->getCurrentPanelType()); + + mapUIToNewTestParams(UIElemHandles, mappingIDsForChangeableParams); + + countdownPanel->setTestTypeString(newTest.getTestTypeAdvanceWarning()); + + + }; + void startNewTest(){ + Test t = testController->getCurrentTest(); + + countdownPanel->hide(); + panel->show(); + panel->setActive(true); + if(t.isWithHint()){ + panel->showHint(true); + } + + bottomPanel->show(); + targetPlayButton->show(); // incase it was memory test + if (t.isMemoryTest()){ + targetPlayButton->setLabel("Memorise!"); + }else{ + targetPlayButton->setLabel("Target"); + } + //startAlternatingPlayback(); + //timeController.scheduleEvent(boost::bind(&MessageOrganiser::sendSynthValuesAgain, this), 200); + timeController.startStopwatch(); + eventLogger.logEvent(TEST_TIMER_STARTED); + + + if(t.getListOfControlTypes()[0] == LEAP3D){ + okToGetLeapMidi = true; + } + }; + + vector<int> setSynthsUpForNewTest(Test newTest){ + targetSynth.setAllParams(newTest.getTargetValues()); + eventLogger.logEvent(TARGET_PARAM_SET, newTest.getTargetValues()); // unless something goes wrong in setAllParams + + candidateSynth.setAllParams(newTest.getStartingCandidateValues()); + eventLogger.logEvent(CANDIDATE_PARAM_SET,newTest.getStartingCandidateValues()); + + // eventLogger.logEvent(NEW_TARGET_PARAMS, vector<int> ); + // eventLogger.logEvent(NEW_CANDIDATE_PARAMS, vector<int> ); + + vector<int> mids = candidateSynth.getMappingIDForIndices(newTest.getChangeableIndices()); + + eventLogger.logEvent(CANDIDATE_CHANGEABLE_IDX, newTest.getChangeableIndices()); + eventLogger.logEvent(CANDIDATE_MAPPING_IDS, mids); + + return mids; + }; + + + // could have been cleverer. takes forever due to searching ??? + void mapUIToNewTestParams(vector<UIElement*> elems, vector<int> mids){ + + vector<UIElement*>::iterator elit; + vector<int> typeListLog; + int i = 0; + for(elit=elems.begin(); elit<elems.end();elit++){ + if ( (*elit)->getType() == XYPAD){ + if(i+1 >= mids.size()){ + cout << "ERROR ERROR: too many controls for mapping IDs" << endl; + } + + ButtronXY* theXY = (ButtronXY*)(*elit); + mapXYToParams(theXY, mids[i], mids[i+1]); + theXY->setValueAndScale(candidateSynth.getParamValueForID(mids[i]), candidateSynth.getParamValueForID(mids[i+1])); + theXY->setHintValue(targetSynth.getParamValueFromName(candidateSynth.getNameForMappingID(mids[i])) + ,targetSynth.getParamValueFromName(candidateSynth.getNameForMappingID(mids[i+1]))); + i+=2; + typeListLog.push_back(int(XYPAD)); + }else if ( (*elit)->getType() == SLIDER){ + if(i >= mids.size()){ + + cout << "ERROR ERROR: too many controls for mapping IDs: " << mids.size() << endl; + } + + ButtronSlider* theSlider = (ButtronSlider*)(*elit); + mapControlToParam((*elit), mids[i]); + theSlider->setValueAndScale(candidateSynth.getParamValueForID(mids[i])); + cout << "Hint Value " << targetSynth.getParamValueFromName(candidateSynth.getNameForMappingID(mids[i])) << endl; + theSlider->setHintValue(targetSynth.getParamValueFromName(candidateSynth.getNameForMappingID(mids[i]))); + i++; + typeListLog.push_back(int(SLIDER)); + }else if ( (*elit)->getType() == LEAP3D ){ + set3Dbox((Leap3DBoxGL*)(*elit)); + // UH + string nameX = candidateSynth.getNameForMappingID(mids[i]); + box3D->setHintValue(0,targetSynth.getParamValueFromName(nameX)); + box3D->setValueAndScale(0, candidateSynth.getParamValueForID(mids[i])); + i++; + + string nameY = candidateSynth.getNameForMappingID(mids[i]); + box3D->setHintValue(1,targetSynth.getParamValueFromName(nameY)); + box3D->setValueAndScale(1, candidateSynth.getParamValueForID(mids[i])); + i++; + + string nameZ = candidateSynth.getNameForMappingID(mids[i]); + box3D->setHintValue(2,targetSynth.getParamValueFromName(nameZ)); + box3D->setValueAndScale(2, candidateSynth.getParamValueForID(mids[i])); + i++; + + + box3D->setLabels(nameX,nameY,nameZ); + typeListLog.push_back(int(LEAP3D)); + + }else{ + cout << "ERROR ERROR: ui type not handled my mapping function !" << endl; + } + } + + eventLogger.logEvent(CONTROL_LIST,typeListLog); + }; + + // TODO - no, triggering playback needs to be logged + void startAlternatingPlayback(){ + + cout << "start alt playback" << endl; + // use our special timer to fire off play to pd + // sets off timed alternating playback + + playAlternating(); + playingAlternating = true; + + }; + void stopAlternatingPlayback(){ + cout << "stop alt playback" << endl; + // kill the alternation + timeController.cancelEvent(currentSoundPlayTimer); + playingAlternating = false; + }; + + void playAlternating(){ + + static bool alt; + int nextTime; + if (alt){ + targetSynth.trigger(); + // flash the target thingy + targetSymbol->flash(); + nextTime = alternationSpeed*1.503; // gap after target + }else{ + sendSynthValuesAgain(); // and again and again + candidateSynth.trigger(); + panel->flash(); + nextTime = alternationSpeed; + } + alt = !alt; + candidateSynth.setNoteLength(alternationSpeed); + targetSynth.setNoteLength(alternationSpeed); // could be user alterable + currentSoundPlayTimer = timeController.scheduleEvent(boost::bind(&SearchMessageOrganiser::playAlternating,this), nextTime); + + + + }; + + void delayedShowNewTest(){ + newTestButton->show(); + // JUST IN CASE IT CRASHES near end... + //eventLogger.saveSessionToFile(); + }; + void submitSingleControl(){ + // if last one + // submitPressed() + + // else + + // grey out that slider, + // activate next slider and show it's button (same button but moved!???) + + }; + + + void submitPressed(){ + + // depending on mode go to next control + // if(testController->getCurrentPanelType() == SEQUENTIAL){ + // submitSingleControl(); + // return; + // } + // otherwise do this other - or call + + okToGetLeapMidi = false; + + TimerMillisec timeTaken = timeController.stopStopwatch(); + vector<int> answer = candidateSynth.getAllParamValues(); + + eventLogger.logEvent(SUBMIT_PRESSED, answer); //, answer, scoreRunningTotal, time taken (why not?)); + + TestResult result = testController->submitAnswer(answer, timeTaken); // TODO returns all the results + + vector<int> logResult; + logResult.push_back(result.realDistanceToTarget*1000); // measured in milliCC !??! + logResult.push_back(result.timeTaken); // milliseconds + logResult.push_back(result.score); + logResult.push_back(result.targetBandHit); + logResult.push_back(result.timeWindowHit); + + eventLogger.logEvent(DISTANCE_TIME_SCORE, logResult); + + + // gui stuff - different controller? + panel->setActive(false); + panel->showHint(true); // add some encouraging feedback to hint + bottomPanel->hide(); + + showScoreForTest(result); + + stopAlternatingPlayback(); + + // was it the final sumbit? + if(testController->isLastTest()){ + // thats it - show a final score screen etc + timeController.scheduleEvent(boost::bind(&SearchMessageOrganiser::testsFinished, this), 500); + return; + }else{ + timeController.scheduleEvent(boost::bind(&SearchMessageOrganiser::delayedShowNewTest, this), 300); + } + + + }; + + void showScoreForTest(TestResult result){ + scorePanel->setText(result.displayText); + scorePanel->show(); + + ofColor c; + if(result.targetBandHit == 1){ + // yellow red blue + c = ofColor(255,255,0,255); + }else if(result.targetBandHit == 2){ + c = ofColor(255,0,0,255); + }else if(result.targetBandHit == 3){ + c = ofColor(45,45,255,255); + }else if(result.targetBandHit == 4){ + c = ofColor(0,255,0,255); + }else{ + c = ofColor(150,235,200,255); + } + scorePanel->setColor(c); + panel->setHintColor(c); + }; + +public: + void init( PDSynthWrapper& cs, PDSynthWrapper& ts){ + + testController = new TestController; + + MessageOrganiser::init(cs,ts); + + currentSoundPlayTimer = -1; + okToGetLeapMidi = false; + + alternationSpeed = 200; + + candidateSynth.setNoteLength(alternationSpeed); + targetSynth.setNoteLength(alternationSpeed); + + playingAlternating = false; + + + verdBig.loadFont("verdana.ttf", 18, true, true); + verdBig.setLineHeight(18.0f); + verdBig.setLetterSpacing(1.037); + }; + + //------------------------------------------------------------------------ + void drawScore(){ + ofColor txtCol = ofColor(150,235,200,255); + + int score = getScore(); + stringstream msg; + + msg << "Test: " << testController->getCurrentTestLetter(); + ofSetColor(txtCol); + verdBig.drawString(msg.str(), 40, 140); + msg.str(std::string()); + + msg << "Score: " << score; + verdBig.drawString(msg.str(), 240, 140); + msg.str(std::string()); + + pair<int,int> time; + time = getTime(); + msg << "Time taken: " << time.first << ":" << time.second << endl; + verdBig.drawString(msg.str(), 600, 140); + + } + + void setNewTestButton(Buttron * ntb){ + newTestButton = ntb; + }; + void set3Dbox(Leap3DBoxGL* box){ + box3D = box; + }; + void setBottomPanel(ButtonPanel * ntb){ + bottomPanel = ntb; + }; + void setControlPanel(SliderPanel* p){ + panel = p; + + }; + void setCountdownPanel(CountdownText* cd){ + countdownPanel = cd; + }; + void setTargetSymbol(TargetSymbol* ts){ + targetSymbol = ts; + }; + void setScorePanel(TextPanel* tp){ + scorePanel = tp; + }; + void setFinishPanel(TextPanel* fp){ + finishPanel = fp; + } + void setTargetButton(Buttron* tb){ + targetPlayButton = tb; + } + int getScore(){ + return testController->getScoreRunningTotal(); + }; + + pair<int,int> getTime(){ + TimerMillisec tms = timeController.getStopwatchElapsedTime(); + int s = int(tms/1000); + int hs = int((tms%1000)/10); + pair<int,int> p(s,hs); + return p; + }; + void countdownToNewTest(){ + + panel->hide(); + panel->setActive(false); + scorePanel->hide(); + bottomPanel->hide(); + newTestButton->hide(); + + // set up stuff + setupNewTest(); + eventLogger.logEvent(COUNTDOWN_INITIATED); + + countdownPanel->showAndStart(3); + + timeController.scheduleEvent(boost::bind(&SearchMessageOrganiser::startNewTest, this), 3000); + + }; + + void playTargetButtonPressed(){ + + static int numPlays = 3; + + Test* t = testController->getCurrentTestPtr(); + if (!t->checkTargetPlaysRemaining()){ + cout << t->getTargetPlaysLeft() << endl; + + sendSynthValuesAgain(); + targetSynth.trigger(); + eventLogger.logEvent(TARGET_PLAYED); + targetPlayButton->hide(); + return; + + } + cout << t->getTargetPlaysLeft() << endl; + + sendSynthValuesAgain(); + targetSynth.trigger(); + eventLogger.logEvent(TARGET_PLAYED); + + return; + } + void playCandidateButtonPressed(){ + // + } + void buttonPressCallback(int mappingID, int value){ + if(mappingID == VOLUME_CHANGE_ID){ + targetSynth.sendVolume(value); + candidateSynth.sendVolume(value); + + } + if(mappingID == SPEED_CHANGE_ID){ + alternationSpeed = 2*(140 - value); + vector<int> eData; + eData.push_back(alternationSpeed); + eventLogger.logEvent(SPEED_CHANGED, eData); + } + if(mappingID == NEW_TEST_ID){ + countdownToNewTest(); + return; + } + if (mappingID == START_ALTERNATE_ID){ + if(!playingAlternating){ + startAlternatingPlayback(); + + }else{ + stopAlternatingPlayback(); + } + return; + } + + if (mappingID == RANDOMISE_TARGET_ID){ // bleyeueurrrr + targetSynth.randomiseParams(); + return; + } + if (mappingID == TRIGGER_TARGET_ID){ + playTargetButtonPressed(); + + } + if (mappingID == TRIGGER_CANDIDATE_ID){ + // log event + sendSynthValuesAgain(); + candidateSynth.trigger(); + eventLogger.logEvent(CANDIDATE_PLAYED); + // flash panel? + panel->flash(); + return; + } + if (mappingID == SUBMIT_CANDIDATE){ + // log event + submitPressed(); + + return; + } + if (mappingID == CRAP_TEST_ID){ + // this is rubbish! send a log of target values, and mapping ids + vector<int> data; + vector<int> tvals = targetSynth.getAllParamValues(); + vector<int> pidx = testController->getCurrentChangeableParams(); + data.insert(data.end(), tvals.begin(), tvals.end()); + data.insert(data.end(), pidx.begin(), pidx.end()); + + eventLogger.logEvent(CRAP_TEST, data); + } + if(mappingID == SHOW_HIDE_PANEL){ + static bool showing; + + if(showing){ + cout << " showing"<<endl; + + panel->show(); + + }else{ + cout << " hiding"<<endl; + panel->hide(); + } + showing = !showing; + } + if(mappingID == SHOW_HIDE_HINT){ + static bool showingHint; + if(showingHint){ + panel->showHint(false); + showingHint = false; + }else{ + panel->showHint(true); + showingHint = true; + } + } + if(mappingID == SAVE_PRESET_HIT){ + expPresetManager.savePreset("blah", candidateSynth.getAllParamValues()); + + } + if(mappingID == RECALL_PRESET_HIT){ + + loadPreset("blah"); + //candidateSynth.startMetronome(); + + } + } + //TODO move this + void loadPreset(string pname){ + + vector<int> values = expPresetManager.recallPreset(pname); + if (values.size()){ + candidateSynth.setAllParams(values); + setAllSlidersToValues(values); + }else{ + cout << "ERROR, no preset found" << endl; + } + } + // called from UI + // OVERLOADED + void paramChangeCallback(int mappingID, int value){ + candidateSynth.paramChangeCallback(mappingID, value); + vector<int> evtData; + evtData.push_back(mappingID); // or just index? + evtData.push_back(value); + + eventLogger.logEvent(CANDIDATE_PARAM_ADJUSTED, evtData); + }; + + + void midiFromLeap(int ctl_num, int ctl_val){ + + if (!okToGetLeapMidi){ + return; + } + + Test *theTest = testController->getCurrentTestPtr(); + if (theTest == NULL) return; + + vector<int> ci = theTest->getChangeableIndices(); + vector<int> mids = candidateSynth.getMappingIDForIndices(ci); + if (ctl_num >= mids.size() || ctl_num < 0) return; + + candidateSynth.paramChangeCallback(mids[ctl_num], ctl_val); + + setUIToParam(ctl_num, ctl_val); + + vector<int> evtData; + evtData.push_back(mids[ctl_num]); // or just index? + evtData.push_back(ctl_val); + + eventLogger.logEvent(CANDIDATE_PARAM_ADJUSTED, evtData); + + } + + + ofTrueTypeFont verdBig; + +}; + +#endif /* defined(__riftathon__SearchMessageOrganiser__) */