view SearchMessageOrganiser.h @ 28:953db6518738

leap version more or less there, needs btter results feedback but thats detail. "no movement" bit is stupid cos peopel can move their hand. light flash not work.
author Robert Tubb <rt300@eecs.qmul.ac.uk>
date Thu, 30 Oct 2014 18:35:00 +0000
parents 27cdf475aa4b
children
line wrap: on
line source
//
//  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:
    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 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(){
        
        controlPanel->hide();
        controlPanel->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 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?
            controlPanel->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;
                
                controlPanel->show();
                
            }else{
                cout << " hiding"<<endl;
                controlPanel->hide();
            }
            showing = !showing;
        }
        if(mappingID == SHOW_HIDE_HINT){
            static bool showingHint;
            if(showingHint){
                controlPanel->showHint(false);
                showingHint = false;
            }else{
                controlPanel->showHint(true);
                showingHint = true;
            }
        }

    }

    // called from UI
    
    
    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;


protected:

    
    
// protected methods
    void testsFinished(){
        controlPanel->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 = controlPanel->generateControls(testController->getCurrentListOfControls(), testController->getCurrentPanelType());
        
        mapUIToNewTestParams(UIElemHandles, mappingIDsForChangeableParams);
        
        countdownPanel->setTestTypeString(newTest.getTestTypeAdvanceWarning());
        
        
    };
    void startNewTest(){
        Test t = testController->getCurrentTest();
        
        countdownPanel->hide();
        controlPanel->show();
        controlPanel->setActive(true);
        if(t.isWithHint()){
            controlPanel->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 SEARCHMSGORG: 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();
            controlPanel->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?
        controlPanel->setActive(false);
        controlPanel->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);
        controlPanel->setHintColor(c);
    };
 
    
    
    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;
    
    //int scoreRunningTotal;
    TimerID currentSoundPlayTimer;
    
    int alternationSpeed; // ms between cand and target
    bool playingAlternating;
    
    bool okToGetLeapMidi;

};

#endif /* defined(__riftathon__SearchMessageOrganiser__) */