changeset 9:d5e928887f51

More refactoring. Mode for Synth value changes only sent to PD on triggering sound.
author Robert Tubb <rt300@eecs.qmul.ac.uk>
date Fri, 17 Oct 2014 17:50:41 +0100
parents d59de9fd3496
children e25d2b1b185e
files MessageOrganiser.h PDSynthWrapper.h SearchMessageOrganiser.h SynthParam.h TrainingMessageOrganiser.h UsernameAlertViewController.mm eventLogger.h globalVariables.h testApp.h testApp.mm trainingController.h trainingController.mm trainingTestController.h trainingTestController.mm
diffstat 14 files changed, 541 insertions(+), 414 deletions(-) [+]
line wrap: on
line diff
--- a/MessageOrganiser.h	Fri Oct 17 16:35:22 2014 +0100
+++ b/MessageOrganiser.h	Fri Oct 17 17:50:41 2014 +0100
@@ -63,6 +63,8 @@
     void init(PDSynthWrapper& cs, PDSynthWrapper& ts){
         candidateSynth = cs;
         targetSynth = ts;
+        
+        onlyChangeCandidateOnTrigger = true;
     }
     // could template for ui element type??
     void mapButtonToAction(UIElement* control, int mappingID){
@@ -71,16 +73,45 @@
         control->addHandler(callbackF, mappingID);
         currentMapping.insert(std::pair<int,UIElement*>(mappingID,control));
     }
+    void setControlPanel(SliderPanel* p){ // a bit specific?? 
+        panel = p;
+        
+    };
+    void setBottomPanel(ButtonPanel * ntb){
+        bottomPanel = ntb;
+    };
 protected:
 
     PDSynthWrapper candidateSynth;
     PDSynthWrapper targetSynth;
+    ButtonPanel* bottomPanel; // shows during test : play buttons and submit
+    SliderPanel* panel;
+    map<int,UIElement*> currentMapping; // could get more sophisticated if not 1-1 ?
     
 
-    map<int,UIElement*> currentMapping; // could get more sophisticated if not 1-1 ?
+    void triggerCandidateSound(){
+        // log event
+        sendSynthValuesAgain();
+        candidateSynth.trigger();
+        eventLogger.logEvent(CANDIDATE_PLAYED);
+        // flash panel?
+        panel->flash();
+    }
     
-    SliderPanel* panel;
+    void paramChangeCallback(int mappingID, int value){
+        
+        if(onlyChangeCandidateOnTrigger){
+            candidateSynth.paramChangeCallback(mappingID, value, false);
+        }else{
+            candidateSynth.paramChangeCallback(mappingID, value, true);
+        }
     
+        vector<int> evtData;
+        evtData.push_back(mappingID); // or just index?
+        evtData.push_back(value);
+        
+        eventLogger.logEvent(CANDIDATE_PARAM_ADJUSTED, evtData);
+    };
 
     void sendSynthValuesAgain(){
         candidateSynth.sendAllParams();
@@ -150,9 +181,6 @@
         control->setLabel(paramName);
     };
 
-    virtual void paramChangeCallback(int mappingID, int value){
-        // virtual?
-    };
     virtual void buttonPressCallback(int mappingID, int value){
         
     };
@@ -167,6 +195,8 @@
             setUIToParam(i, vals[i]);
         }
     }
+    
+    bool onlyChangeCandidateOnTrigger;
 
 };
 
--- a/PDSynthWrapper.h	Fri Oct 17 16:35:22 2014 +0100
+++ b/PDSynthWrapper.h	Fri Oct 17 17:50:41 2014 +0100
@@ -126,13 +126,17 @@
         return result;
     }
     
-    void paramChangeCallback(int mappingID, int value){
+    void paramChangeCallback(int mappingID, int value, bool send = true){
 
         // look for id in params
         std::vector<SynthParam>::iterator psp;
         for(psp = timbreParams.begin(); psp < timbreParams.end(); psp++){
             if ( psp->getID() == mappingID){
-                psp->setValue(value);
+                if (send){
+                    psp->setValue(value);
+                }else{
+                    psp->setValueWithoutSend(value);
+                }
                 return;
             }
         }
--- a/SearchMessageOrganiser.h	Fri Oct 17 16:35:22 2014 +0100
+++ b/SearchMessageOrganiser.h	Fri Oct 17 17:50:41 2014 +0100
@@ -15,331 +15,12 @@
 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;
         
@@ -385,13 +66,8 @@
     void set3Dbox(Leap3DBoxGL* box){
         box3D = box;
     };
-    void setBottomPanel(ButtonPanel * ntb){
-        bottomPanel = ntb;
-    };
-    void setControlPanel(SliderPanel* p){
-        panel = p;
-        
-    };
+
+    
     void setCountdownPanel(CountdownText* cd){
         countdownPanel = cd;
     };
@@ -435,7 +111,7 @@
         timeController.scheduleEvent(boost::bind(&SearchMessageOrganiser::startNewTest, this), 3000);
         
     };
-
+    
     void playTargetButtonPressed(){
         
         static int numPlays = 3;
@@ -459,9 +135,7 @@
         
         return;
     }
-    void playCandidateButtonPressed(){
-        //
-    }
+
     void buttonPressCallback(int mappingID, int value){
         if(mappingID == VOLUME_CHANGE_ID){
             targetSynth.sendVolume(value);
@@ -487,7 +161,7 @@
             }
             return;
         }
-
+        
         if (mappingID == RANDOMISE_TARGET_ID){ // bleyeueurrrr
             targetSynth.randomiseParams();
             return;
@@ -568,15 +242,6 @@
         }
     }
     // 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){
@@ -601,12 +266,325 @@
         evtData.push_back(ctl_val);
         
         eventLogger.logEvent(CANDIDATE_PARAM_ADJUSTED, evtData);
-
+        
     }
-
+    
     
     ofTrueTypeFont verdBig;
 
+
+protected:
+    
+    
+    TimeController altPlaybackController;
+    
+    TestController* testController;
+    Buttron* newTestButton;
+    //Buttron* submitButton;
+    
+
+    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");
+        }
+
+        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());
+
+        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);
+    };
+ 
+
 };
 
 #endif /* defined(__riftathon__SearchMessageOrganiser__) */
--- a/SynthParam.h	Fri Oct 17 16:35:22 2014 +0100
+++ b/SynthParam.h	Fri Oct 17 17:50:41 2014 +0100
@@ -26,9 +26,11 @@
     };
     void setValue(int i){
         value = i;
-        // TODO should this send an eventlog message? what if called from randomise ? what if different synth?
         sendToPD();
     };
+    void setValueWithoutSend(int i){
+        value = i;
+    }
     int getValue() const{
         return value;
     }
--- a/TrainingMessageOrganiser.h	Fri Oct 17 16:35:22 2014 +0100
+++ b/TrainingMessageOrganiser.h	Fri Oct 17 17:50:41 2014 +0100
@@ -10,8 +10,91 @@
 #define __riftathon__TrainingMessageOrganiser__
 
 #include <iostream>
+#include "trainingTestController.h"
+#include "MessageOrganiser.h"
+class TrainingMessageOrganiser : public MessageOrganiser {
+public:
+    void init( PDSynthWrapper& cs, PDSynthWrapper& ts){
+        
+        trainingTestController = new TrainingTestController;
+        
+        MessageOrganiser::init(cs,ts);
 
-class TrainingMessageOrganiser{
+    }
     
+    void setupDefaultMapping(){
+        vector<int> mappingIDsForChangeableParams = getMappingIDsFromSynths();
+        
+        controlPanelType cpt = REVISITABLE;
+        vector<controllerType> elemList;
+        for(int i = 0; i < 8; i++){
+            elemList.push_back(SLIDER);
+        }
+        
+        vector<UIElement*> UIElemHandles = panel->generateControls(elemList, cpt);
+        
+        mapSlidersToParams(UIElemHandles, mappingIDsForChangeableParams);
+        
+        bottomPanel->show();
+    }
+    
+    vector<int> getMappingIDsFromSynths(){
+        vector<int> index;
+        for(int i = 0; i < 8; i++){
+            index.push_back(i);
+        }
+        vector<int> mids = candidateSynth.getMappingIDForIndices(index);
+        
+        return mids;
+    }
+    
+    void setupNewTest(){
+
+        
+    };
+   
+protected:
+    void mapSlidersToParams(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() == 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{
+                cout << "ERROR ERROR: ui type not handled my mapping function !" << endl;
+            }
+        }
+        
+        eventLogger.logEvent(CONTROL_LIST,typeListLog);
+    };
+    
+    void buttonPressCallback(int mappingID, int value){
+        if(mappingID == VOLUME_CHANGE_ID){
+            targetSynth.sendVolume(value);
+            candidateSynth.sendVolume(value);
+            
+        }
+
+        if (mappingID == TRIGGER_CANDIDATE_ID){
+            triggerCandidateSound();
+            return;
+        }
+    }
+    
+    TrainingTestController* trainingTestController;
 };
 #endif /* defined(__riftathon__TrainingMessageOrganiser__) */
--- a/UsernameAlertViewController.mm	Fri Oct 17 16:35:22 2014 +0100
+++ b/UsernameAlertViewController.mm	Fri Oct 17 17:50:41 2014 +0100
@@ -62,7 +62,7 @@
         
     }else{
         eventLogger.setUsername([userName cStringUsingEncoding:NSASCIIStringEncoding]);
-        ((testApp *)self.theOFAppRef)->startTheTests();
+        ((testApp *)self.theOFAppRef)->usernameEntered();
         
     }
     [self.alert dismissWithClickedButtonIndex:self.alert.firstOtherButtonIndex animated:YES];
--- a/eventLogger.h	Fri Oct 17 16:35:22 2014 +0100
+++ b/eventLogger.h	Fri Oct 17 17:50:41 2014 +0100
@@ -62,11 +62,12 @@
                 ALL_TESTS_COMPLETED,               // 16 ?
                 TARGET_PLAYED,              // 17 however this happens it might be good to know when this happens
                 CANDIDATE_PLAYED,           // 18
-                START_THE_TESTS,            // 19 probably superfluous
+                START_THE_SEARCH_TESTS,            // 19 probably superfluous
                 CRAP_TEST,                  // eliminate these coords somehow ???
                 EMPTY_EVENT,
                 SPEED_CHANGED,              // 22 ms between sounds
-                SAVE_PRESET                 // 23 save a preset
+                SAVE_PRESET,                 // 23 save a preset
+                START_THE_TRAINING_TESTS,
     
 };
 
--- a/globalVariables.h	Fri Oct 17 16:35:22 2014 +0100
+++ b/globalVariables.h	Fri Oct 17 17:50:41 2014 +0100
@@ -33,6 +33,8 @@
 typedef enum {TOUCH_DOWN, TOUCH_MOVED, TOUCH_UP} touchType;
 typedef enum {INTRO,QUESTIONNAIRE, HELP, TEST_IN_PROGRESS, SCORE_AND_HINT, COUNT_DOWN, READY_FOR_NEXT} interfaceType;
 
+typedef enum {FAMILIARISATION, EXPRESS, TRAINING, SEARCH} testStages;
+
 typedef enum {SLIDER, XYPAD, BUTTON, LEAP3D} controllerType;
 typedef enum {SEQUENTIAL, REVISITABLE, SIMULTANEOUS, MULTI_SIMPLE, MULTI_COMPLEX} controlPanelType;
 
--- a/testApp.h	Fri Oct 17 16:35:22 2014 +0100
+++ b/testApp.h	Fri Oct 17 17:50:41 2014 +0100
@@ -30,7 +30,6 @@
 #include "MessageOrganiser.h"
 #include "SearchMessageOrganiser.h"
 #include "TrainingMessageOrganiser.h"
-#include "TestController.h"
 #include "timeController.h"
 #include <buttonPanel.h>
 #include "targetSymbol.h"
@@ -50,6 +49,8 @@
 	
 public:
 
+    testStages currentStage;
+    
     ofTrueTypeFont verdBig;
     bool sendMIDIAndOSC;
     bool paused;
@@ -84,8 +85,7 @@
     
     PDSynthWrapper targetSynth;
     PDSynthWrapper candidateSynth;
-    
-    TestController* testController;
+
     //TimeController timeController;
     interfaceType whichInterfaceShowing, previousInterface;
     
@@ -117,7 +117,8 @@
     void showIntro();
     void introHidden();
     void usernameEntered();
-    void startTheTests();
+    void startTheSearchTests();
+    void startTheTrainingTests();
     void showHelp();
     void helpHidden();
     void setupNewUser();
--- a/testApp.mm	Fri Oct 17 16:35:22 2014 +0100
+++ b/testApp.mm	Fri Oct 17 17:50:41 2014 +0100
@@ -27,6 +27,7 @@
     candidateSynth.init(&core,"candidateSynth");
     
     searchMessageOrganiser.init(targetSynth, candidateSynth);
+    trainingMessageOrganiser.init(targetSynth,candidateSynth);
     timeController.init();
     initialiseGUIs();
     initialiseMIDI();
@@ -66,7 +67,7 @@
 	ofSoundStreamSetup(2, 2, this, 44100, ofxPd::blockSize()*ticksPerBuffer, 3);
     
     if(true){ // force start
-        startTheTests();
+        startTheTrainingTests();
     }else{
         
         if(eventLogger.questionnaireCompleted){ // then show play again dialog, and log the test number
@@ -124,7 +125,18 @@
 //--------------------------------------------------------------
 // gui for the main training stage
 void testApp::setupTrainingViewPanels(){
+    UIProps p;
+    ButtonPanel* bottomButtonPanel = new ButtonPanel(1,160+p.sliderPanelHeight,ofGetWidth(),250,p);
     
+    // play and submit are now same thing
+    Buttron * playCandidateButton = new Buttron(p.buttonWidth*1.4,680, p);
+    playCandidateButton->setLabel("PLAY");
+    trainingMessageOrganiser.mapButtonToAction(playCandidateButton, TRIGGER_CANDIDATE_ID);
+    bottomButtonPanel->addButton(playCandidateButton);
+
+    trainingMessageOrganiser.setBottomPanel(bottomButtonPanel);
+    UIElements.push_back(bottomButtonPanel);
+    bottomButtonPanel->hide();
 }
 //--------------------------------------------------------------
 // gui for the old style tweakathlon  stage
@@ -167,7 +179,7 @@
     searchMessageOrganiser.setBottomPanel(bottomButtonPanel);
     UIElements.push_back(bottomButtonPanel);
     bottomButtonPanel->showBorder(false);
-
+    bottomButtonPanel->hide();
 // -   -   - - - -- -  - OTHER BITS
     
     CountdownText * countDownBox = new CountdownText("5" , 500, 380, 455, 455, p);
@@ -217,6 +229,7 @@
     
     UIElements.push_back(controlPanel);
     searchMessageOrganiser.setControlPanel(controlPanel);
+    trainingMessageOrganiser.setControlPanel(controlPanel);
     controlPanel->showBorder(true);
 }
 //--------------------------------------------------------------
@@ -229,7 +242,7 @@
 
     setupSliderPanel();
 
-    
+    setupTrainingViewPanels();
 }
 //--------------------------------------------------------------------------
 void testApp::initialiseMIDI(){
@@ -376,16 +389,24 @@
 void testApp::usernameEntered(){
     // display a thing that gives us an option as to which stage to start
     // EXPLORE, PERFORMANCE TRAINING, SEARCH
+    
+    
 }
 //--------------------------------------------------------------
-void testApp::startTheTests(){
-    eventLogger.logEvent(START_THE_TESTS);
+void testApp::startTheSearchTests(){
+    eventLogger.logEvent(START_THE_SEARCH_TESTS);
     whichInterfaceShowing = COUNT_DOWN;
     // do countdown etc
     searchMessageOrganiser.countdownToNewTest();
     // TODO how is testApp going to kknow whichInterfaceShowing ???
     
 }
+void testApp::startTheTrainingTests(){
+    eventLogger.logEvent(START_THE_TRAINING_TESTS);
+    
+    trainingMessageOrganiser.setupDefaultMapping();
+    
+}
 //--------------------------------------------------------------
 //--------------------------------------------------------------
 void testApp::showHelp(){
@@ -486,7 +507,8 @@
     //ofLine(0,150,1024,150);
     
     //drawWaveform();
-    searchMessageOrganiser.drawScore();
+    if (currentStage == SEARCH)
+        searchMessageOrganiser.drawScore();
     
     
 
@@ -642,7 +664,10 @@
         int ctl_val = msg.value;
         // TODO route control change message here
         //cout << " ctrl : " << ctl_num << " : " << ctl_val << endl;
-        searchMessageOrganiser.midiFromLeap(ctl_num, ctl_val);
+        if (currentStage == SEARCH)
+            searchMessageOrganiser.midiFromLeap(ctl_num, ctl_val);
+        
+        
     }
  
 }
--- a/trainingController.h	Fri Oct 17 16:35:22 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-//
-//  trainingController.h
-//  riftathon
-//
-//  Created by Robert Tubb on 10/10/2014.
-//
-// this controls the sequence fo events, for both the slider UI and the VR one
-// it sends and recieves messages from a wrapper, it doesn't care which interface you're using
-// it only cares about the sequence of events
-
-
-#ifndef __riftathon__trainingController__
-#define __riftathon__trainingController__
-#include "presetManager.h"
-#include <iostream>
-
-// include message types ( or use defines? ) these message type should be sendable by osc / midi
-class TrainingController {
-    
-    vector<Preset> presetSequence;
-    
-    // inputs
-    void soundSubmitted();
-    void readyForNextSequence();
-    
-    // outputs
-    void presentTheSequence();
-    void showCountdown();
-    void startSearch();
-    
-    //
-    void makePresetSequence(int sequenceLength);
-    
-    Preset getRandomPreset();
-    
-    
-    
-};
-#endif /* defined(__riftathon__trainingController__) */
--- a/trainingController.mm	Fri Oct 17 16:35:22 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-//
-//  trainingController.mm
-//  riftathon
-//
-//  Created by Robert Tubb on 10/10/2014.
-//
-//
-
-#include "trainingController.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trainingTestController.h	Fri Oct 17 17:50:41 2014 +0100
@@ -0,0 +1,40 @@
+//
+//  trainingController.h
+//  riftathon
+//
+//  Created by Robert Tubb on 10/10/2014.
+//
+// this controls the sequence fo events, for both the slider UI and the VR one
+// it sends and recieves messages from a wrapper, it doesn't care which interface you're using
+// it only cares about the sequence of events
+
+
+#ifndef __riftathon__trainingController__
+#define __riftathon__trainingController__
+#include "presetManager.h"
+#include <iostream>
+
+// include message types ( or use defines? ) these message type should be sendable by osc / midi
+class TrainingTestController {
+public:
+    int numParams;
+    vector<Preset> presetSequence;
+    
+    // inputs
+    void soundSubmitted();
+    void readyForNextSequence();
+    
+    // outputs
+    void presentTheSequence();
+    void showCountdown();
+    void startSearch();
+    
+    //
+    void makePresetSequence(int sequenceLength);
+    
+    Preset getRandomPreset();
+    
+    
+    
+};
+#endif /* defined(__riftathon__trainingController__) */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trainingTestController.mm	Fri Oct 17 17:50:41 2014 +0100
@@ -0,0 +1,9 @@
+//
+//  trainingController.mm
+//  riftathon
+//
+//  Created by Robert Tubb on 10/10/2014.
+//
+//
+
+#include "trainingTestController.h"