void setup() {
		ofSetFrameRate(60);
		ofSetVerticalSync(false);
		ledRing.setup();
		fbo.allocate(512, 512);
		fbo.begin();
		ofClear(0);
		fbo.end();
		ofSetCircleResolution(64);
		ofSetLineWidth(2);
		ofLog() << "MIDI Ports: " << ofToString(midi.getPortList());
		midi.openPort();
		Pulse::midiWrapper = &midiWrapper;
		pulses.push_back(Pulse(0, 0));
	}
	void update() {
		for(int i = 0; i < pulses.size(); i++) {
			pulses[i].update(people);
		}
		fbo.begin();
		ofPushStyle();
		ofSetLineWidth(30);
		ofSetColor(0, 10);
		ofFill();
		ofRect(0, 0, fbo.getWidth(), fbo.getHeight());
		ofSetColor(255);
		ofTranslate(ofGetWidth() / 2, ofGetHeight() / 2);
		float saturation = ofMap(pulses.size(), 1, 10, 0, 255, true);
		for(int i = 0; i < pulses.size(); i++) {
			ofPushMatrix();
			ofRotate(pulses[i].getAngle());
			ofSetColor(ofColor::fromHsb(pulses[i].getHue(), saturation, 255));
			ofLine(0, 0, ofGetWidth() / 2, 0);
			ofPopMatrix();
		}
		ofPopStyle();
		fbo.end();
		fbo.readToPixels(fboPixels);
		ledRing.update(fboPixels);
		float presence = 0;
		for(int i = 0; i < people.size(); i++) {
			presence += people[i].getPresence();
		}
		presence /= people.size();
		midi.sendControlChange(2, 1, presence);
	}
	void sendNote(int channel, int pitch, int velocity, int noteLength) {
		if(isThreadRunning()) {
			midi.sendNoteOn(channel, pitch, velocity);
			MidiNote note(channel, pitch, noteLength);
			lock();
			frontNotes.push_back(note);
			unlock();
		}
	}
	void threadedFunction() {
		while(isThreadRunning()) {
			for(int i = 0; i < backNotes.size(); i++) {
				MidiNote& cur = backNotes[i];
				if(cur.finished()) {
					midi.sendNoteOff(cur.channel, cur.pitch, 0);
				}
			}
			ofRemove(backNotes, isFinished);
			lock();
			swap();
			unlock();
			ofSleepMillis(1);
		}
		// silence remaining notes
		swap();
		for(int i = 0; i < backNotes.size(); i++) {
			MidiNote& cur = backNotes[i];
			midi.sendNoteOff(cur.channel, cur.pitch, 0);
		}
	}
	void update(vector<Person>& people) {
		float angle = getAngle();
		ofVec2f direction = makeRotatedVector(angle);
		float bestDiff;
		for(int i = 0; i < people.size(); i++) {
			float diff = direction.angle(people[i].position);
			if(i == 0 || abs(diff) < abs(bestDiff)) {
				bestDiff = diff;
			}
		}
		float bounceEpsilon = 15;
		if(abs(bestDiff) < bounceEpsilon &&
			 (bestDiff>0) != (lastDiff>0) &&
			 ((bestDiff < 0 && clockwise) || (bestDiff > 0 && !clockwise))) {
			bounce();
		} else {
			lastDiff = bestDiff;
			lastAngle = angle;
		}
		midi.sendControlChange(1, 1 + 2 * index + 0, ofMap(direction.x, 0, ofGetWidth(), 0, 127, true));
		midi.sendControlChange(1, 1 + 2 * index + 1, ofMap(direction.y, 0, ofGetHeight(), 0, 127, true));
	}
void ofxMidiOutProcessor::linkToMidiOut(ofxMidiOut &midiOut){

    if( midiOut.isOpen() ){
        if(connected){
            closePort();
        }

        midiOut_p = &midiOut;
        if(verbose) cout<<"[ofxPDSP]: linked to midi Out \n";

        startMidiDaemon();
        
        connected = true;
    }else{
        if(verbose) cout<<"[ofxPDSP]: midi out not linked, open midi out before linking! \n";
    }
}
 void listPorts()
 {
     mMidiOut.listPorts();
 }
 void ctlOut(int cc, int value, int channel = 0)
 {
     if (channel != 0) mChannel = channel;
     mMidiOut.sendControlChange(mChannel, cc, value);
 }
 void openPort(const string& deviceName)
 {
     mMidiOut.openPort(deviceName);
 }
 void willDelete()
 {
     mMidi->sendNoteOff(mCh, mPich);
 }
 Note(ofxMidiOut * midi, int pich, int velo, int channel):
 mMidi(midi), mCh(channel), mPich(pich)
 {
     mMidi->sendNoteOn(channel, pich, velo);
 }
	void setPosition(float x, float y) {
		position.set(x, y);
		midi.sendControlChange(1, 64 + index, getPresence());
	}