view SearchMessageOrganiser.h @ 44:d810aa9ca03a

times. cosmetic stuff
author Robert Tubb <rt300@eecs.qmul.ac.uk>
date Mon, 15 Dec 2014 17:33:41 +0000
parents 953db6518738
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__) */