#include "testApp.h"
#include "ofAppiOSWindow.h"
#include "ExplorePresetManager.h"
extern EventLogger eventLogger;
extern ExplorePresetManager expPresetManager;
// static members inited here. not my choice.
int SynthParam::mappingUID = 88000;
//--------------------------------------------------------------
void testApp::setup(){
    
    
    
    ofxiPhoneSetOrientation( OF_ORIENTATION_90_LEFT );
    //ofxiPhoneExternalDisplay::mirrorOn();
    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];

    initialiseVariables();

    
    
    
    //presetManager.startLoadAll();
    
    targetSynth.init(&core,"targetSynth");
    candidateSynth.init(&core,"candidateSynth");
    
    searchMessageOrganiser.init(targetSynth, candidateSynth);
    trainingMessageOrganiser.init(targetSynth,candidateSynth);
    expMessageOrganiser.init(targetSynth,candidateSynth);
    
    timeController.init();
    initialiseGUIs();
    initialiseMIDI();
    setupUIElements();

    ofEnableDepthTest();
    ofEnableAlphaBlending();


    //--------------------------------------
    
	int ticksPerBuffer = 8;	// 8 * 64 = buffer len of 512
	core.setup(2, 2, 44100, ticksPerBuffer);

    // setup OF sound stream
    bufSize = ofxPd::blockSize()*ticksPerBuffer;
    wavetableNew = (float *) malloc(bufSize * sizeof(float));
    
	ofSoundStreamSetup(2, 2, this, 44100, ofxPd::blockSize()*ticksPerBuffer, 3);
    
    if(false){ // force start
        startTheExpTests();
    }else{
        
        if(eventLogger.questionnaireCompleted){ // then show play again dialog, and log the test number
            
        }else{
            // then we're in timed session mode
            [usernameAlertViewController showUserNamePrompt];
        }
    }
    paused = false;
    
    
    eventLogger.logEvent(APP_LOADED);

    ofSoundStreamStart();

}

//-----------------------------------------------------------------------------

//DeviceID3523537000
void testApp::initialiseVariables(){
    paused = true;
    ofBackground( 0, 0, 0 );
    //ofEnableAlphaBlending();
    //ofEnableSmoothing();
    
	// open an outgoing connection to HOST:PORT for OSC
	// sender.setup( OSC_HOST, OSC_PORT );
    ofSetFrameRate(60);
    props = new UIProps();

}
//---------------------------------------------------------
void testApp::initialiseGUIs(){

    // set up iOS gui stuff
    
    helpViewController	= [[HelpViewController alloc] initWithNibName:@"HelpViewController" bundle:nil];
    [ofxiPhoneGetGLParentView() addSubview:helpViewController.view];
    [helpViewController setAppRef:(__bridge id)this];
    helpViewController.view.hidden = YES;
    

    usernameAlertViewController = [[UsernameAlertViewController alloc] init];
    [usernameAlertViewController setAppRef:(__bridge id)this];
 
}

//--------------------------------------------------------------
// GUI for finding and saving presets to express concepts
void testApp::setupExpressViewPanels(){
    ButtonPanel* bottomButtonPanel = new ButtonPanel(1,160+props->sliderPanelHeight,ofGetWidth(),250,*props);
    
    Buttron * playCandidateButton = new Buttron(props->buttonWidth*1.4,680, *props);
    playCandidateButton->setLabel("PLAY");
    expMessageOrganiser.mapButtonToAction(playCandidateButton, TRIGGER_CANDIDATE_ID);
    bottomButtonPanel->addButton(playCandidateButton);

    Buttron * savePresetButton = new Buttron(props->buttonWidth*1.4,680, *props);
    savePresetButton->setLabel("SAVE/NEXT");
    expMessageOrganiser.mapButtonToAction(savePresetButton, SAVE_PRESET_HIT);
    bottomButtonPanel->addButton(savePresetButton);
    
    expMessageOrganiser.setBottomPanel(bottomButtonPanel);
    UIElements.push_back(bottomButtonPanel);
    bottomButtonPanel->hide();
    
    IconPanel* iconPanel = new IconPanel(420, 10, 150, 150, *props);
    expMessageOrganiser.setIconPanel(iconPanel);
    UIElements.push_back(iconPanel);
    iconPanel->show();
    
    TextPanel * instructionPanel = new TextPanel("Instrcution panel", 10, 50, 300,150,(*props));
    instructionPanel->setFontSize(LARGEFONT);
    UIElements.push_back(instructionPanel);
    expMessageOrganiser.setInstructionPanel(instructionPanel);
    instructionPanel->show();
}
//--------------------------------------------------------------
// gui for the main training stage
void testApp::setupTrainingViewPanels(){

    ButtonPanel* bottomButtonPanel = new ButtonPanel(1,160+props->sliderPanelHeight,ofGetWidth(),250,*props);
    
    // play and submit are now same thing
    Buttron * playCandidateButton = new Buttron(props->buttonWidth*1.4,680, *props);
    playCandidateButton->setLabel("START");
    trainingMessageOrganiser.mapButtonToAction(playCandidateButton, TRIGGER_CANDIDATE_ID);
    bottomButtonPanel->addButton(playCandidateButton);

    trainingMessageOrganiser.setBottomPanel(bottomButtonPanel);
    UIElements.push_back(bottomButtonPanel);
    bottomButtonPanel->hide();
    
    IconPanel* iconPanel = new IconPanel(420, 10, 150, 150, *props);
    trainingMessageOrganiser.setIconPanel(iconPanel);
    UIElements.push_back(iconPanel);
    iconPanel->show();
    
    TextPanel * instructionPanel = new TextPanel("Instrcution panel", 10, 50, 300,150,(*props));
    instructionPanel->setFontSize(LARGEFONT);
    UIElements.push_back(instructionPanel);
    trainingMessageOrganiser.setInstructionPanel(instructionPanel);
    instructionPanel->show();
    
}
//--------------------------------------------------------------
// gui for the old style tweakathlon  stage
void testApp::setupSearchViewPanels(){

    ButtonPanel* bottomButtonPanel = new ButtonPanel(1,160+(*props).sliderPanelHeight,ofGetWidth(),250,(*props));
    
    Buttron* playTargetButton = new Buttron((*props).buttonWidth*0.2,680, (*props));
    playTargetButton->setLabel("Target");
    searchMessageOrganiser.mapButtonToAction(playTargetButton, TRIGGER_TARGET_ID);
    bottomButtonPanel->addButton(playTargetButton);
    searchMessageOrganiser.setTargetButton(playTargetButton);
    
    Buttron * playCandidateButton = new Buttron((*props).buttonWidth*1.4,680, (*props));
    playCandidateButton->setLabel("Current");
    searchMessageOrganiser.mapButtonToAction(playCandidateButton, TRIGGER_CANDIDATE_ID);
    bottomButtonPanel->addButton(playCandidateButton);
    
    
    // submit button - only one for now
    Buttron * submitButton = new Buttron(ofGetWidth()*0.5 - (*props).buttonWidth*0.5,680, (*props));
    submitButton->setLabel("Submit");
    searchMessageOrganiser.mapButtonToAction(submitButton, SUBMIT_CANDIDATE);
    bottomButtonPanel->addButton(submitButton);
    
    //  button - just for spacing pruposes
    Buttron * saveButton = new Buttron(ofGetWidth()*0.5 - (*props).buttonWidth*0.5,680, (*props));
    saveButton->setLabel("SAVE");
    searchMessageOrganiser.mapButtonToAction(saveButton, SAVE_PRESET_HIT);
    bottomButtonPanel->addButton(saveButton);
    saveButton->hide();
    
    Buttron * recallButton = new Buttron(ofGetWidth()*0.5 - (*props).buttonWidth*0.5,680, (*props));
    recallButton->setLabel("RECALL");
    searchMessageOrganiser.mapButtonToAction(recallButton, RECALL_PRESET_HIT);
    bottomButtonPanel->addButton(recallButton);
    recallButton->hide();
    
    searchMessageOrganiser.setBottomPanel(bottomButtonPanel);
    UIElements.push_back(bottomButtonPanel);
    bottomButtonPanel->showBorder(false);
    bottomButtonPanel->hide();
// -   -   - - - -- -  - OTHER BITS
    
    CountdownText * countDownBox = new CountdownText("5" , 500, 380, 455, 455, (*props));
    UIElements.push_back(countDownBox);
    searchMessageOrganiser.setCountdownPanel(countDownBox);
    countDownBox->hide();
    
    TextPanel * scoreFeedback = new TextPanel("Feedback panel", ofGetWidth()*0.5 - (*props).buttonWidth*0.5, 666, 400,100,(*props));
    scoreFeedback->setFontSize(SMALLFONT);
    UIElements.push_back(scoreFeedback);
    searchMessageOrganiser.setScorePanel(scoreFeedback);
    scoreFeedback->hide();
    
    TextPanel * finishPanel = new TextPanel("Finish txt panel", 250, 250, 1000,400,(*props));
    finishPanel->setFontSize(LARGEFONT);
    finishPanel->setText("Experiment completed");
    searchMessageOrganiser.setFinishPanel(finishPanel);
    
    UIElements.push_back(finishPanel);
    finishPanel->hide();
    
    
    Buttron * newTestButton = new Buttron(ofGetWidth()-300,690, (*props));
    newTestButton->setLabel("Next Test");
    UIElements.push_back(newTestButton);
    searchMessageOrganiser.mapButtonToAction(newTestButton, NEW_TEST_ID);
    newTestButton->hide();
    searchMessageOrganiser.setNewTestButton(newTestButton);
    
    TargetSymbol* targetSymbol = new TargetSymbol(ofGetWidth()*0.5,160,30,(*props));
    searchMessageOrganiser.setTargetSymbol(targetSymbol);
    UIElements.push_back(targetSymbol);
    
}
//--------------------------------------------------------------
void testApp::setupSliderPanel(){
    UIProps p;
    vector<controllerType> sl2;
    sl2.push_back(SLIDER);
    
    SliderPanel * controlPanel = new SliderPanel(1,
                                                 160,
                                                 ofGetWidth(),
                                                 (*props).sliderPanelHeight,
                                                 (*props),
                                                 sl2);
    
    UIElements.push_back(controlPanel);
    searchMessageOrganiser.setControlPanel(controlPanel);
    trainingMessageOrganiser.setControlPanel(controlPanel);
    expMessageOrganiser.setControlPanel(controlPanel);
    controlPanel->showBorder(true);
}
//--------------------------------------------------------------
void testApp::setupUIElements(){

    ofBackground((*props).generalBackground);

    setupSearchViewPanels();

    setupSliderPanel();

    setupTrainingViewPanels();
    
    setupExpressViewPanels();
}
//--------------------------------------------------------------------------
void testApp::initialiseMIDI(){
    
    /////////////////////////
    // MIDI
    
    midiChannel = 8;
    midiOffset = 0;
	
	// enables the network midi session between iOS and Mac OSX on a
	// local wifi network
	//
	// in ofxMidi: open the input/outport network ports named "Session 1"
	//
	// on OSX: use the Audio MIDI Setup Utility to connect to the iOS device
	//
	ofxMidi::enableNetworking();
	
	// list the number of available input & output ports
	ofxMidiIn::listPorts();
	ofxMidiOut::listPorts();
	
	// create and open input ports
	for(int i = 0; i < ofxMidiIn::getNumPorts(); ++i) {
		
		// new object
		inputs.push_back(new ofxMidiIn);
		
		// set this class to receive incoming midi events
		inputs[i]->addListener(this);
		
		// open input port via port number
		inputs[i]->openPort(i);
	}
	
	// create and open output ports
	for(int i = 0; i < ofxMidiOut::getNumPorts(); ++i) {
		
		// new object
		outputs.push_back(new ofxMidiOut);
		
		// open input port via port number
		outputs[i]->openPort(i);
	}
	
	// set this class to receieve midi device (dis)connection events
	ofxMidi::setConnectionListener(this);
    
    // END MIDI

}
//--------------------------------------------------------------

template <class T>
void deleteVectorOfPointers( T * inVectorOfPointers )
{
    typename T::iterator i;
    for ( i = inVectorOfPointers->begin() ; i < inVectorOfPointers->end(); i++ )
    {
        delete * i;
    }
    //delete inVectorOfPointers;
}


//--------------------------------------------------------------
void testApp::exit(){
    eventLogger.logEvent(APP_EXITED);
    eventLogger.exitAndSave();
    
    core.exit();
    
    // are these handled automatically?
    //[introViewController release];
    //[topButtonViewController release];
    //[bottomTabViewController release];
    
    // clean up MIDI
	for(int i = 0; i < inputs.size(); ++i) {
		inputs[i]->closePort();
		inputs[i]->removeListener(this);
		delete inputs[i];
	}
    
	for(int i = 0; i < outputs.size(); ++i) {
		outputs[i]->closePort();
		delete outputs[i];
	}
    deleteVectorOfPointers(&UIElements); // TODO this crashes??

    
    cout << "exit done \n";
}

#pragma mark GUI
////////////////////////////
// These functions called from iOS toolbars
//--------------------------------------------------------------

//--------------------------------------------------------------
void testApp::showQuestionnaire(){

    
    questionnaireViewController	= [[QuestionnaireViewController alloc] initWithNibName:@"QuestionnaireViewController" bundle:nil];
    [ofxiPhoneGetGLParentView() addSubview:questionnaireViewController.view];
	
    [questionnaireViewController setAppRef:(__bridge id)this];
    [questionnaireViewController show:(__bridge id)this];
    
    whichInterfaceShowing = QUESTIONNAIRE;
    
    
}
//--------------------------------------------------------------
void testApp::questionnaireHidden(vector<int> answers, const char* userComments){
    // send answers to server as json
    eventLogger.questionnaireAnswersObtained(answers, userComments);

}

//--------------------------------------------------------------
void testApp::showIntro(){
    // display a thing that gives us an option as to which stage to start
    // EXPLORE, PERFORMANCE TRAINING, SEARCH
    cout << "SHOW INTRO\n";

    introViewController	= [[IntroViewController alloc] initWithNibName:@"IntroViewController" bundle:nil];
    [ofxiPhoneGetGLParentView() addSubview:introViewController.view];
	
    [introViewController setAppRef:(__bridge id)this];
    [introViewController show:(__bridge id)this];
    
    whichInterfaceShowing = INTRO;
    
}
//--------------------------------------------------------------
//void testApp::introHidden(){
//    eventLogger.consentGiven = true;
//    eventLogger.logEvent(INTRO_CONSENTED);
//    [usernameAlertViewController showUserNamePrompt];
//    // after prompt goes it calls usernameEntered()
//}
//--------------------------------------------------------------
void testApp::introHidden(int mode){
    eventLogger.consentGiven = true;
    eventLogger.logEvent(INTRO_CONSENTED);
    
    if(mode == 1){
        startTheExpTests();
    }else if(mode == 2){
        startTheTrainingTests();
    }else if(mode == 3){
        startTheSearchTests();
    }
    
}
//--------------------------------------------------------------
void testApp::usernameEntered(){

    eventLogger.onUsernameEntered();
    expPresetManager.onAppLoad();
    
    showIntro();
    
}
//--------------------------------------------------------------
void testApp::startTheExpTests(){
    eventLogger.logEvent(START_THE_EXP_TESTS);
    whichInterfaceShowing = COUNT_DOWN;
    // do countdown etc
    
    trainingMessageOrganiser.hideMyPanels();
    
    expMessageOrganiser.setup();
    expMessageOrganiser.showMyPanels();
    // TODO how is testApp going to kknow whichInterfaceShowing ???
    
}
//--------------------------------------------------------------
void testApp::startTheSearchTests(){
    eventLogger.logEvent(START_THE_SEARCH_TESTS);
    whichInterfaceShowing = COUNT_DOWN;
    // do countdown etc
    
    trainingMessageOrganiser.hideMyPanels();
    searchMessageOrganiser.countdownToNewTest();
    // TODO how is testApp going to kknow whichInterfaceShowing ???
    
}
void testApp::startTheTrainingTests(){
    eventLogger.logEvent(START_THE_TRAINING_TESTS);
    
    trainingMessageOrganiser.setupDefaultMapping();
    trainingMessageOrganiser.showMyPanels();
    
}
//--------------------------------------------------------------
//--------------------------------------------------------------
void testApp::showHelp(){
    // stop clock etc
    previousInterface = whichInterfaceShowing;
    whichInterfaceShowing = HELP;
    helpViewController.view.hidden = NO;
    eventLogger.logEvent(HELP_PRESSED);
    
}
void testApp::helpHidden(){
    whichInterfaceShowing = previousInterface;
    
}
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
#pragma mark sending to pd and midi
void testApp::sendParametersToPD(){

    
}
//--------------------------------------------------------------
void testApp::sendMidiParam(int which){
    int midiChannel = 8;
    int offset = 0;
    
    for(int i = 0; i < outputs.size(); ++i) {
        outputs[i]->sendControlChange(midiChannel, offset+which, 66);
    }
    
    
}

//--------------------------------------------------------------
//void testApp::sendOSCParams(){
//       
//    ofxOscMessage m;
//    m.setAddress( "Template" );
//
//    m.addFloatArg(9.9999);
//
//    sender.sendMessage( m );
//}
//--------------------------------------------------------------

void testApp::setupNewUser(){
    // this function is for supervised trials with my ipad
    eventLogger.newUser();
}
//--------------------------------------------------------------
#pragma mark STANDARD OF FUNCTIONS
//--------------------------------------------------------------
void testApp::update(){
    
    if(paused) return;
    
    // run timer check here
    // look at time, work out difference
    timeController.tick();
    
    // test mutex crash thing
    //eventLogger.logEvent(CANDIDATE_PLAYED);
}
//--------------------------------------------------------------

void testApp::appModeChange(interfaceType mode){
    whichInterfaceShowing = mode;
}


//------------------------------------------------------------------------

void testApp::draw(){
    
    switch (whichInterfaceShowing){
        case QUESTIONNAIRE:
            break;
        case INTRO:
            break;
        case TEST_IN_PROGRESS:
            break;
        case SCORE_AND_HINT:
            break;
        case COUNT_DOWN:
            break;
        case READY_FOR_NEXT:
            break;
        default:
            break;
    }
    
    
    
    drawUIElements();
    
    //ofSetColor(234, 234, 234);
    //ofLine(0,150,1024,150);
    
    //drawWaveform();
    if (currentStage == SEARCH)
        searchMessageOrganiser.drawScore();
    
    //image.draw(10,10,200,200);

}
//------------------------------------------------------------------------
void testApp::drawUIElements(){

    vector<UIElement *>::iterator UIitr;
    for(UIitr = UIElements.begin(); UIitr < UIElements.end(); UIitr++){
        (*UIitr)->draw();
    }
}

//------------------------------------------------------------------------

//--------------------------------------------------------------
// passes touch to UI elements
//--------------------------------------------------------------

void testApp::touchToUIElements(int x, int y, touchType ttype, int tid){
    vector<UIElement *>::iterator UIitr;
    for(UIitr = UIElements.begin(); UIitr < UIElements.end(); UIitr++){
        (*UIitr)->touch(x,y,ttype, tid);
    }
}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
    //touch = transformTouchCoords(touch);
    touchToUIElements(touch.x, touch.y, TOUCH_DOWN, touch.id);
    
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){

    touchToUIElements(touch.x, touch.y, TOUCH_MOVED, touch.id);
    
}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){

    touchToUIElements(touch.x, touch.y, TOUCH_UP, touch.id);
}


//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
    
}

//--------------------------------------------------------------
void testApp::lostFocus(){
    //exit();
}

//--------------------------------------------------------------
void testApp::gotFocus(){
    
}

//--------------------------------------------------------------
void testApp::gotMemoryWarning(){
    
}

//--------------------------------------------------------------

void testApp::deviceOrientationChanged(int newOrientation){
    
    cout << "orientation: " << newOrientation;
    
    // do something here?

}



//--------------------------------------------------------------
void testApp::touchCancelled(ofTouchEventArgs& args){
    
}
//---------------------------------------------------------------
// AUDIO STUFF
//---------------------------------------------------------------

#pragma mark AUDIO STREAMS
//--------------------------------------------------------------
void testApp::audioReceived(float * input, int bufferSize, int nChannels) {
	//core.audioReceived(input, bufferSize, nChannels);
}

void testApp::audioRequested(float * output, int bufferSize, int nChannels) {

	core.audioRequested(output, bufferSize, nChannels);
//    for(int i=0;i<bufferSize/2;i++){
//        output[i] = 0.2;
//    }
//    // pass buffer to drawable thing
//    for(int i=0;i<bufferSize;i++){
//        wavetableNew[i] = output[i];
//    }
    
}

void testApp::drawWaveform(){
    // draw the actual waveform in the corner
    
    int width = 768;
    int height = 128;
    double sampval = 0.0;
    int leftsampnum = 0;
    int rightsampnum = 0;
    float sampscale = 0.0, prevsampscale = 0.0, interp = 0.0;
    
    ofSetColor(256, 0, 0);
    double step = double(bufSize)/width; // how much we are stepping thru wave per pixel
    for(int i = 0; i < width; i++){
        
        leftsampnum = floor(i * step); // basic nearest neighbour interpolation
        rightsampnum = ceil(i*step);
        interp  = (i*step)-leftsampnum;
        if(rightsampnum < bufSize){
            sampval = (1 - interp)*wavetableNew[leftsampnum] + interp*wavetableNew[rightsampnum];
        }
        sampscale = (sampval * 700) + height/2.0; // centre and scale
        ofSetLineWidth(2);
        ofLine(sampscale, i, prevsampscale, i-1); // draw a line from pixel to pixel (?)
        prevsampscale = sampscale;
    }
    
}
//---------------------------------------------------------------
#pragma mark UTILITIES
//-------------------------------------------------------------------------
//--------------------------------------------------------------

#pragma mark MIDI
void testApp::addMessage(string msg) {
	cout << msg << endl;
	messages.push_back(msg);
	while(messages.size() > maxMessages)
		messages.pop_front();
}

//--------------------------------------------------------------
void testApp::newMidiMessage(ofxMidiMessage& msg) {
    
    // looks out for: 30 31 32... on channel 8
    if(msg.channel == midiChannel && msg.status == MIDI_CONTROL_CHANGE){
        int ctl_num = msg.control - 30;
        int ctl_val = msg.value;
        // TODO route control change message here
        //cout << " ctrl : " << ctl_num << " : " << ctl_val << endl;
        if (currentStage == SEARCH)
            searchMessageOrganiser.midiFromLeap(ctl_num, ctl_val);
        
        
    }
 
}

//--------------------------------------------------------------
void testApp::midiInputAdded(string name, bool isNetwork) {
	stringstream msg;
	msg << "ofxMidi: input added: " << name << " network: " << isNetwork;
    cout << msg.str();
	addMessage(msg.str());
	
	// create and open a new input port
	ofxMidiIn * newInput = new ofxMidiIn;
	newInput->openPort(name);
	newInput->addListener(this);
	inputs.push_back(newInput);
}

//--------------------------------------------------------------
void testApp::midiInputRemoved(string name, bool isNetwork) {
	stringstream msg;
	msg << "ofxMidi: input removed: " << name << " network: " << isNetwork << endl;
    cout << msg.str();
	addMessage(msg.str());
	
	// close and remove input port
	vector<ofxMidiIn*>::iterator iter;
	for(iter = inputs.begin(); iter != inputs.end(); ++iter) {
		ofxMidiIn * input = (*iter);
		if(input->getName() == name) {
			input->closePort();
			input->removeListener(this);
			delete input;
			inputs.erase(iter);
			break;
		}
	}
}

//--------------------------------------------------------------
void testApp::midiOutputAdded(string name, bool isNetwork) {
	stringstream msg;
	msg << "ofxMidi: output added: " << name << " network: " << isNetwork << endl;
    cout << msg.str();
	addMessage(msg.str());
	
	// create and open new output port
	ofxMidiOut * newOutput = new ofxMidiOut;
	newOutput->openPort(name);
	outputs.push_back(newOutput);
}

//--------------------------------------------------------------
void testApp::midiOutputRemoved(string name, bool isNetwork) {
	stringstream msg;
	msg << "ofxMidi: output removed: " << name << " network: " << isNetwork << endl;
    cout << msg.str();
	addMessage(msg.str());
	
	// close and remove output port
	vector<ofxMidiOut*>::iterator iter;
	for(iter = outputs.begin(); iter != outputs.end(); ++iter) {
		ofxMidiOut * output = (*iter);
		if(output->getName() == name) {
			output->closePort();
			delete output;
			outputs.erase(iter);
			break;
		}
	}
}
