Mercurial > hg > tweakathon2ios
diff MessageOrganiser.h @ 0:a223551fdc1f
First commit - copy from tweakathlon.
author | Robert Tubb <rt300@eecs.qmul.ac.uk> |
---|---|
date | Fri, 10 Oct 2014 11:46:42 +0100 |
parents | |
children | 851833072cf1 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MessageOrganiser.h Fri Oct 10 11:46:42 2014 +0100 @@ -0,0 +1,699 @@ +// +// MessageOrganiser.h +// tweakathlon +// +// Created by Robert Tubb on 10/12/2013. +// +// This object handles the mapping from GUI to params +// +// and sends their messages to PD and eventLogger +#pragma once +#include "eventLogger.h" +#include <map.h> +#include <vector> +#include <string> +#include "boost/bind.hpp" +#include "boost/function.hpp" + +#include <UIElement.h> +#include <Buttron.h> +#include <ButtronSlider.h> +#include <ButtronXY.h> +#include "AppCore.h" +#include "ofxPd.h" +#include "TestController.h" +#include "timeController.h" +#include "PDSynthWrapper.h" +#include "ofxTimer.h" +#include "sliderPanel.h" +//#include "testApp.h" +#include "targetSymbol.h" +#include "3Dbox.h" +#include "TextPanel.h" +#include "CountdownText.h" +#include "buttonPanel.h" + +// event logger needs to know +// which controls were showing in what mode +// which controls were mapped to what param +// what was the target sound params +// all the updates of control movements, submit, quit etc + +// this is the bit that handles mapping from UI elements to synth i.e testApp DOESNT DO THAT + +// has links to panel sliders can show hide them + +// controls flow of stuff + +//--------------------------------------------------------------------- +//--------------------------------------------------------------------- +extern TimeController timeController; + +extern EventLogger eventLogger; + +typedef boost::function<void(void)> AppModeChangeFunction; + +class MessageOrganiser { +private: + AppCore* core; + //testApp* theOFApp; + PDSynthWrapper targetSynth; + PDSynthWrapper candidateSynth; + + map<int,UIElement*> currentMapping; // could get more sophisticated if not 1-1 ? + + SliderPanel* panel; + 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; + + 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; + }; + void sendSynthValuesAgain(){ + candidateSynth.sendAllParams(); + targetSynth.sendAllParams(); + }; + + // 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" << 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(&MessageOrganiser::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(&MessageOrganiser::testsFinished, this), 500); + return; + }else{ + timeController.scheduleEvent(boost::bind(&MessageOrganiser::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); + }; + + // we want to set UI object + void setUIToParam(int index, int value){ // e.g. from MIDI incoming, will handle both box and sliders... + // theXY->setValueAndScale(candidateSynth.getParamValueForID(mids[i]), candidateSynth.getParamValueForID(mids[i+1])); + UIElement* elem; + // get the element + if(panel->subElements.size() <= index){ + return; + } + elem = panel->subElements[index]; + if ( elem->getType() == SLIDER){ + ButtronSlider* theSlider = (ButtronSlider*)elem; + theSlider->setValueAndScale(value); + + }else{ + cout << "ERROR ERROR: ui type not handled by setUIToParam!" << endl; + } + + }; + + + void mapControlToParam(UIElement* control, int mappingID){ + + UICallbackFunction callbackF; + callbackF = boost::bind(&MessageOrganiser::paramChangeCallback, this, _1,_2); + control->addHandler(callbackF, mappingID); + // put in our map so we can send param values to gui + currentMapping.insert(std::pair<int,UIElement*>(mappingID,control)); + cout << " Mapped control to ID: " << mappingID << "Name: " << candidateSynth.getNameForMappingID(mappingID) << endl; + control->setLabel(candidateSynth.getNameForMappingID(mappingID)); + }; + + void mapXYToParams(ButtronXY* control, int mappingIDX, int mappingIDY){ + UICallbackFunction callback; + + callback = boost::bind(&MessageOrganiser::paramChangeCallback, this, _1,_2); + + control->addHandler(callback, mappingIDX, mappingIDY); + + // put in our map so we can send param values to gui + //currentMapping.insert(std::pair<int,UIElement*>(mappingID,control)); + + + cout << " Mapped control to XID: " << mappingIDX << "Name: " << candidateSynth.getNameForMappingID(mappingIDX) << endl; + cout << " Mapped control to YID: " << mappingIDY << "Name: " << candidateSynth.getNameForMappingID(mappingIDY) << endl; + control->setLabel(candidateSynth.getNameForMappingID(mappingIDX), candidateSynth.getNameForMappingID(mappingIDY)); + + }; + + void mapLeapToParams(ButtronXY* control, int mappingIDX, int mappingIDY, int mappingIDZ){ +// UICallbackFunction callbackX; +// UICallbackFunction callbackY; +// UICallbackFunction callbackZ; +// +// callbackX = boost::bind(&MessageOrganiser::paramChangeCallback, this, _1,_2); +// callbackY = boost::bind(&MessageOrganiser::paramChangeCallback, this, _1,_2); +// callbackZ = boost::bind(&MessageOrganiser::paramChangeCallback, this, _1,_2); +// +// control->addHandler(callbackX, mappingIDX); +// control->addHandler(callbackY, mappingIDY); +// +// // put in our map so we can send param values to gui +// //currentMapping.insert(std::pair<int,UIElement*>(mappingID,control)); +// +// +// cout << " Mapped control to XID: " << mappingIDX << "Name: " << candidateSynth.getNameForMappingID(mappingIDX) << endl; +// cout << " Mapped control to YID: " << mappingIDY << "Name: " << candidateSynth.getNameForMappingID(mappingIDY) << endl; +// control->setLabel(candidateSynth.getNameForMappingID(mappingIDX), candidateSynth.getNameForMappingID(mappingIDY)); + + }; + + void mapControlToParam(UIElement* control, string paramName){ + // get mapping ID from synth + int mappingID = candidateSynth.getMappingIDForName(paramName); + mapControlToParam(control, mappingID); + control->setLabel(paramName); + }; +public: + void init(AppCore* aCore, TestController* tc){ + // set PD core... + + core = aCore; + targetSynth.init(aCore,"targetSynth"); + candidateSynth.init(aCore,"candidateSynth"); + + testController = tc; + currentSoundPlayTimer = -1; + okToGetLeapMidi = false; + + alternationSpeed = 200; + + candidateSynth.setNoteLength(alternationSpeed); + targetSynth.setNoteLength(alternationSpeed); + + playingAlternating = false; + }; + 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(&MessageOrganiser::startNewTest, this), 3000); + + }; + void sendToGUI(vector<int> paramsToMap){ + // look up these ids in mapping table + }; + void saveGoodTest(Test t){ + + }; + 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 == GOOD_TEST_ID){ + Test t = testController->getCurrentTest(); + saveGoodTest(t); + } + 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; + } + } + } + // called from UI + 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); + }; + + // could template for ui element type?? + void mapButtonToAction(UIElement* control, int mappingID){ + UICallbackFunction callbackF; + callbackF = boost::bind(&MessageOrganiser::buttonPressCallback, this, _1,_2); + control->addHandler(callbackF, mappingID); + currentMapping.insert(std::pair<int,UIElement*>(mappingID,control)); + } + + + void midiFromLeap(int ctl_num, int ctl_val){ + + + if (!okToGetLeapMidi){ + return; + } + + // this fails - try pointer version? + + Test *theTest = testController->getCurrentTestPtr(); + if (theTest == NULL) return; + + box3D->setValueAndScale(ctl_num, ctl_val); + + + 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); + + vector<int> evtData; + evtData.push_back(mids[ctl_num]); // or just index? + evtData.push_back(ctl_val); + + eventLogger.logEvent(CANDIDATE_PARAM_ADJUSTED, evtData); + // also call UI object + // get mapping ID for + // setUIToParam(ctl_num, ctl_val); + } + +}; +