view hackday/testApp.cpp @ 25:2a025ea7c793

hackday work to get live midi input, follow the notes, output measure, read measure in with midi file
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Sat, 03 Dec 2011 21:09:13 +0000
parents 5a11b19906c7
children 179365726f07
line wrap: on
line source
#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
	midiFileName = "../../../data/frerejacques.mid";
	int retVal = cannamMainFunction();
	
	cout << "MIDI INPUT EXAMPLE" << endl;
	printf("midi in in in");
	ofSetVerticalSync(true);
	//ofBackground(255,255,255);

	midiIn.listPorts();
	midiIn.openPort(2);
	
	transpose = 24;
	noteInStream.transposeVal = &transpose;
	
	noteInStream.startTime = &midiEvents.startTime;//point start time of note in stream to the same time in MIDI events

	noteInStream.factor = &midiEvents.ticksFactor;
	printf("TICKS FACTOR %f \n", midiEvents.ticksFactor);//noteInStream->factor)
	
//	portName = "hello";//midiIn.portNames[2];
//	printf("MIDI PORT %c\n", portName);
	cout << "MIDI PORT " << endl;//portName << endl;
	
	
	//midiIn.addListener(this);	
	ofAddListener(midiIn.newMessageEvent, this, &testApp::newMessage);


	
	playing = false;

	
	receiver.setup( PORT );

	sender.setup( HOST, SEND_PORT );
	
	screenWidth = ofGetWidth();
	screenHeight = ofGetHeight();
	midiEvents.screenWidth = &screenWidth;
	midiEvents.screenHeight = &screenHeight;
	midiEvents.drawTempoMode = false;
	ofSetFrameRate(30);
	
	midiEvents.ticksPerScreen += 4000;
	lastScoreIndexSent = 0;
	
}

//--------------------------------------------------------------
void testApp::update(){
	if (playing){
		midiEvents.updatePlayPosition();//this fn calls	midiEvents.bayesStruct.updateBestEstimate();
	}
//	drawer.tickLocation+=20;
	
	// check for waiting messages
	while( receiver.hasWaitingMessages() )
	{
		ofxOscMessage m;
		receiver.getNextMessage( &m );
		
		if ( m.getAddress() == "/midinoteon" )
		{
			int newMidiOnPitch = m.getArgAsInt32(0);
			int velocity = m.getArgAsInt32(1);
			double time = m.getArgAsFloat(2);
			
			if (velocity != 0)
			midiEvents.newNoteOnEvent(newMidiOnPitch, velocity, time);

		}
		
		if ( m.getAddress() == "/setSpeedPrior" )
		{
			float speedPrior = m.getArgAsFloat(0);
			printf("speed prior set to %f\n", speedPrior);
			midiEvents.speedPriorValue = speedPrior;
			midiEvents.bayesStruct.speedPriorValue = speedPrior;
		}
		
		if ( m.getAddress() == "/startplaying" )
		{
			startPlaying();
		}
		
		if ( m.getAddress() == "/stopplaying" )
		{
			stopPlaying();
		}
		
		
		if ( m.getAddress() == "/integratedEstimate" )
		{
			midiEvents.bayesStruct.usingIntegratedTempoEstimate = true;
		}
	
		if ( m.getAddress() == "/MAPestimate" )
		{
			midiEvents.bayesStruct.usingIntegratedTempoEstimate = false;
		}
		
		
		if ( m.getAddress() == "/realtime" )
		{
			midiEvents.runningInRealTime = true;
		}
		
		
		if ( m.getAddress() == "/offline" )
		{
			midiEvents.runningInRealTime = false;
		}
		
		if ( m.getAddress() == "/minimumSpeedRatio" )
		{
				
			float minSpeed = m.getArgAsFloat(0);
			//printf("minimum speed received is  %f and max is %f\n", minSpeed, midiEvents.bayesStruct.relativeSpeedLikelihood.getIndexInRealTerms(midiEvents.bayesStruct.relativeSpeedLikelihood.length-1));
			if (minSpeed > 0 && minSpeed < midiEvents.bayesStruct.relativeSpeedLikelihood.getIndexInRealTerms(midiEvents.bayesStruct.relativeSpeedLikelihood.length-1)){
			printf("minimum speed accepted is %f\n", minSpeed);
				midiEvents.minimumMatchSpeed = minSpeed;
			}
		}
		
	}//end while osc

	checkNewScoreNote();
	
}


void testApp::checkNewScoreNote(){
	if (lastScoreIndexSent != midiEvents.bestMatchIndex){
	//then we send out new note
		sendNoteToMuseScore();
		lastScoreIndexSent = midiEvents.bestMatchIndex;
		findMeasure();
	}
}

void testApp::findMeasure(){
	int ticks = midiEvents.recordedNoteOnMatrix[midiEvents.bestMatchIndex][0];
	int tmpMeasure = lastMeasureSent;
	
	while (lastMeasureSent > 0 && midiEvents.measureVector[lastMeasureSent] > ticks) {
		lastMeasureSent--;
	}
	
	while (lastMeasureSent < midiEvents.measureVector.size() && midiEvents.measureVector[lastMeasureSent] < ticks) {
		lastMeasureSent++;
	}
	if (lastMeasureSent != tmpMeasure){
	sendMeasureToMuseScore();
	}
	

}

void testApp::sendNoteToMuseScore(){
	int ticks = midiEvents.recordedNoteOnMatrix[midiEvents.bestMatchIndex][0];
	int pitch = midiEvents.recordedNoteOnMatrix[midiEvents.bestMatchIndex][1];
	printf("sending to muse score %i, %i \n", ticks, pitch);
		
		   ofxOscMessage m;
		   m.setAddress( "/plugin" );
		   m.addStringArg( "coloronenote.js" );
		   m.addStringArg("mytick");
		   m.addIntArg( ticks );
		   m.addStringArg("mypitch");
		   m.addIntArg( pitch);
		   sender.sendMessage( m );  
		   
	//	   /plugin coloronenote.js mytick 100 mypitch 56;
}



void testApp::sendMeasureToMuseScore(){

	printf("sending measure to muse score %i \n", lastMeasureSent);
	
	ofxOscMessage m;
	m.setAddress( "/select-measure" );
	m.addIntArg(lastMeasureSent);
	sender.sendMessage( m );  
	
	//		/select-measure 6
	//	   /plugin coloronenote.js mytick 100 mypitch 56;
}


void testApp::newMessage(ofxMidiEventArgs &args){

	int pitch;
	if (noteInStream.noteInReceived(args)){
	double timeNow = ofGetElapsedTimeMillis();
		
		if (!liveInputPlaying){
			firstNoteTime = timeNow;
			liveInputPlaying = true;
			startPlaying();
			printf("FIRST LIVE NOTE IS NOW AT TIME %f\n", timeNow);
		}
		
		pitch = args.byteOne + transpose;
		
		midiEvents.newNoteOnEvent(pitch, args.byteTwo, timeNow - firstNoteTime);
		
		
		int tickTime = midiEvents.getEventTimeTicks(timeNow-firstNoteTime);
		IntVector v;
		v.push_back(tickTime);
		v.push_back(pitch);
		v.push_back(args.byteTwo);
		v.push_back(200);//tmp time til note off happens
		noteInStream.midiInputEvents.push_back(v);
		noteInStream.midiInputTimes.push_back(timeNow - firstNoteTime);
		//printf("NOTE %i at time %f at tick time %i\n", pitch, (timeNow - firstNoteTime), tickTime);
	}
	
//	cout << "MIDI message [port: " << args.port << ", channel: " << args.channel << ", status: " << args.status << ", byteOne: " << pitch << ", byteTwo: " << args.byteTwo << ", timestamp: " << args.timestamp << "]" << endl;
}

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

	midiEvents.drawFile();
	
	string info = "Measure ";
	info += ofToString(lastMeasureSent);
	info += "  Last note ";
	info += ofToString(lastScoreIndexSent);
	
	ofSetHexColor(0x000000);
	ofDrawBitmapString(info, 20, 20);
	midiEvents.drawMidiFile(noteInStream.midiInputEvents);

}

//--------------------------------------------------------------
void testApp::keyPressed(int key){

	
//	if (key == ' '){
//		startPlaying();
//	}
	if (key == '-')
		transpose -= 12;
	
	if (key == '=')
		transpose += 12;
	
	if (key == 'c'){
		double timenow = ofGetElapsedTimeMillis();
		midiEvents.exampleCrossUpdate();
		timenow *= -1;
		timenow += ofGetElapsedTimeMillis();
		printf("CROSS UPDATE TOOK %f", timenow);
	}
	
	if (key == OF_KEY_RETURN)
		stopPlaying();
	
	if (key == OF_KEY_UP){
		if (midiEvents.ticksPerScreen >= 4000)
		midiEvents.ticksPerScreen += 2000;
		else 
			midiEvents.ticksPerScreen += 500;
	}
	
	if (key == 'm'){
//		midiEvents.findMatch(84, 0, 10000);
	}
	
	if (key == OF_KEY_DOWN){
		if (midiEvents.ticksPerScreen >= 4000)
		midiEvents.ticksPerScreen -= 2000;
	else if (midiEvents.ticksPerScreen > 500)
		midiEvents.ticksPerScreen -= 500;
	}
	
	if (key == 'w')
		midiEvents.printMatchMatrix();
	
	if (key == 'k'){
		noteInStream.printNotes();
	}
	
	if (key == 'p'){
		midiEvents.printNotes();
	}

	if (key == 'l')
		midiEvents.bayesStruct.decaySpeedDistribution(100);
	
	if (key == 't')
		midiEvents.drawTempoMode = !midiEvents.drawTempoMode;
	
	if (key == 'y')
		midiEvents.drawPhaseMode = !midiEvents.drawPhaseMode;
	
	if (key == 'r'){
		noteInStream.reset();
		liveInputPlaying = false;
		stopPlaying();
	}
	
	if (key == 'o'){
		//open audio file
		string *filePtr;
		filePtr = &midiFileName;	
		
		if (getFilenameFromDialogBox(filePtr)){
			printf("Midifile: Loaded name okay :\n'%s' \n", midiFileName.c_str());	
			cannamMainFunction();
		}
	}

	
	
}

//--------------------------------------------------------------
void testApp::keyReleased(int key){

}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){
	midiEvents.mouseX = midiEvents.getEventTimeMillis((x * midiEvents.ticksPerScreen)/ screenWidth);
}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::windowResized(int w, int h){
	screenWidth = w;
	screenHeight = h;
	midiEvents.noteHeight = screenHeight / (float)(midiEvents.noteMaximum - midiEvents.noteMinimum);
	
}



void testApp::startPlaying(){
	playing = !playing;
	midiEvents.reset();
	midiEvents.setStartPlayingTimes();
	
	//this is where we stop and start playing
}

void testApp::stopPlaying(){
	playing = false;
	liveInputPlaying = false;
	lastScoreIndexSent = 0;
	midiEvents.bestMatchIndex = 0;
	sendNoteToMuseScore();
}

bool testApp::getFilenameFromDialogBox(string* fileNameToSave){
	//this uses a pointer structure within the loader and returns true if the dialogue box was used successfully
	// first, create a string that will hold the URL
	string URL;
	
	// openFile(string& URL) returns 1 if a file was picked
	// returns 0 when something went wrong or the user pressed 'cancel'
	int response = ofxFileDialogOSX::openFile(URL);
	if(response){
		// now you can use the URL 
		*fileNameToSave = URL;
		//printf("\n filename is %s \n", soundFileName.c_str());
		return true;
	}
	else {
		//	soundFileName = "OPEN canceled. ";
		printf("\n open file cancelled \n");
		return false;
	}
	
	
	
}




int testApp::cannamMainFunction(){


	midiEvents.clearAllEvents();
	
	//int main(int argc, char **argv)
	//{
	//	if (argc != 2) {
	//		cerr << "Usage: midifile <file.mid>" << endl;
	//		return 1;
	//	}
		
	std::string filename = midiFileName;//argv[1];
	
//	fileLoader.chopBeginning = true;
	fileLoader.loadFile(filename, midiEvents);
	
}//new end of load function




	//trying to port to new class
	/*
			MIDIFileReader fr(filename);
	
		if (!fr.isOK()) {
			std::cerr << "Error: " << fr.getError().c_str() << std::endl;
			return 1;
		}
		
		MIDIComposition c = fr.load();
		
		switch (fr.getFormat()) {
			case MIDI_SINGLE_TRACK_FILE: cout << "Format: MIDI Single Track File" << endl; break;
			case MIDI_SIMULTANEOUS_TRACK_FILE: cout << "Format: MIDI Simultaneous Track File" << endl; break;
			case MIDI_SEQUENTIAL_TRACK_FILE: cout << "Format: MIDI Sequential Track File" << endl; break;
			default: cout << "Format: Unknown MIDI file format?" << endl; break;
		}
		
		cout << "Tracks: " << c.size() << endl;
		
		int td = fr.getTimingDivision();
		if (td < 32768) {
			cout << "Timing division: " << fr.getTimingDivision() << " ppq" << endl;
			
			midiEvents.pulsesPerQuarternote = fr.getTimingDivision();
		} else {
			int frames = 256 - (td >> 8);
			int subframes = td & 0xff;
			cout << "SMPTE timing: " << frames << " fps, " << subframes << " subframes" << endl;
		}
		
		for (MIDIComposition::const_iterator i = c.begin(); i != c.end(); ++i) {
			
			cout << "Start of track: " << i->first+1 << endl;
			
			for (MIDITrack::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
				
				unsigned int t = j->getTime();
				int ch = j->getChannelNumber();
				
				if (j->isMeta()) {
					int code = j->getMetaEventCode();
					string name;
					bool printable = true;
					switch (code) {
							
						case MIDI_END_OF_TRACK:
							cout << t << ": End of track" << endl;
							break;
							
						case MIDI_TEXT_EVENT: name = "Text"; break;
						case MIDI_COPYRIGHT_NOTICE: name = "Copyright"; break;
						case MIDI_TRACK_NAME: name = "Track name"; break;
						case MIDI_INSTRUMENT_NAME: name = "Instrument name"; break;
						case MIDI_LYRIC: name = "Lyric"; break;
						case MIDI_TEXT_MARKER: name = "Text marker"; break;
						case MIDI_SEQUENCE_NUMBER: name = "Sequence number"; printable = false; break;
						case MIDI_CHANNEL_PREFIX_OR_PORT: name = "Channel prefix or port"; printable = false; break;
						case MIDI_CUE_POINT: name = "Cue point"; break;
						case MIDI_CHANNEL_PREFIX: name = "Channel prefix"; printable = false; break;
						case MIDI_SEQUENCER_SPECIFIC: name = "Sequencer specific"; printable = false; break;
						case MIDI_SMPTE_OFFSET: name = "SMPTE offset"; printable = false; break;
							
						case MIDI_SET_TEMPO:
						{
							int m0 = j->getMetaMessage()[0];
							int m1 = j->getMetaMessage()[1];
							int m2 = j->getMetaMessage()[2];
							long tempo = (((m0 << 8) + m1) << 8) + m2;
							
							cout << t << ": Tempo: " << 60000000.0 / double(tempo) << endl;
							midiEvents.tempo = 60000000.0 / double(tempo);
							midiEvents.period = double(tempo)/1000.0;
							
							printf("period double is %f\n", midiEvents.period);
						}
							break;
							
						case MIDI_TIME_SIGNATURE:
						{
							int numerator = j->getMetaMessage()[0];
							int denominator = 1 << (int)j->getMetaMessage()[1];
							
							cout << t << ": Time signature: " << numerator << "/" << denominator << endl;
						}
							
						case MIDI_KEY_SIGNATURE:
						{
							int accidentals = j->getMetaMessage()[0];
							int isMinor = j->getMetaMessage()[1];
							bool isSharp = accidentals < 0 ? false : true;
							accidentals = accidentals < 0 ? -accidentals : accidentals;
							cout << t << ": Key signature: " << accidentals << " "
							<< (isSharp ?
								(accidentals > 1 ? "sharps" : "sharp") :
								(accidentals > 1 ? "flats" : "flat"))
							<< (isMinor ? ", minor" : ", major") << endl;
						}
							
					}
					
					
					if (name != "") {
						if (printable) {
							cout << t << ": File meta event: code " << code
							<< ": " << name << ": \"" << j->getMetaMessage()
							<< "\"" << endl;
						} else {
							cout << t << ": File meta event: code " << code
							<< ": " << name << ": ";
							for (int k = 0; k < j->getMetaMessage().length(); ++k) {
								cout << (int)j->getMetaMessage()[k] << " ";
							}
						}
					}
					continue;
				}
				
				switch (j->getMessageType()) {
						
					case MIDI_NOTE_ON:
						cout << t << ": Note: channel " << ch
						<< " duration " << j->getDuration()
						<< " pitch " << j->getPitch()
						<< " velocity " << j->getVelocity() 
						<< "event time " << midiEvents.getEventTimeMillis(t) << endl;
						v.clear();
						v.push_back(t);
						v.push_back(j->getPitch());
						v.push_back(j->getVelocity());
						v.push_back(j->getDuration());
						midiEvents.recordedNoteOnMatrix.push_back(v);
						midiEvents.recordedEventTimes.push_back(midiEvents.getEventTimeMillis(t));
						break;
						
					case MIDI_POLY_AFTERTOUCH:
						cout << t << ": Polyphonic aftertouch: channel " << ch
						<< " pitch " << j->getPitch()
						<< " pressure " << j->getData2() << endl;
						break;
						
					case MIDI_CTRL_CHANGE:
					{
						int controller = j->getData1();
						string name;
						switch (controller) {
							case MIDI_CONTROLLER_BANK_MSB: name = "Bank select MSB"; break;
							case MIDI_CONTROLLER_VOLUME: name = "Volume"; break;
							case MIDI_CONTROLLER_BANK_LSB: name = "Bank select LSB"; break;
							case MIDI_CONTROLLER_MODULATION: name = "Modulation wheel"; break;
							case MIDI_CONTROLLER_PAN: name = "Pan"; break;
							case MIDI_CONTROLLER_SUSTAIN: name = "Sustain"; break;
							case MIDI_CONTROLLER_RESONANCE: name = "Resonance"; break;
							case MIDI_CONTROLLER_RELEASE: name = "Release"; break;
							case MIDI_CONTROLLER_ATTACK: name = "Attack"; break;
							case MIDI_CONTROLLER_FILTER: name = "Filter"; break;
							case MIDI_CONTROLLER_REVERB: name = "Reverb"; break;
							case MIDI_CONTROLLER_CHORUS: name = "Chorus"; break;
							case MIDI_CONTROLLER_NRPN_1: name = "NRPN 1"; break;
							case MIDI_CONTROLLER_NRPN_2: name = "NRPN 2"; break;
							case MIDI_CONTROLLER_RPN_1: name = "RPN 1"; break;
							case MIDI_CONTROLLER_RPN_2: name = "RPN 2"; break;
							case MIDI_CONTROLLER_SOUNDS_OFF: name = "All sounds off"; break;
							case MIDI_CONTROLLER_RESET: name = "Reset"; break;
							case MIDI_CONTROLLER_LOCAL: name = "Local"; break;
							case MIDI_CONTROLLER_ALL_NOTES_OFF: name = "All notes off"; break;
						}
						cout << t << ": Controller change: channel " << ch
						<< " controller " << j->getData1();
						if (name != "") cout << " (" << name << ")";
						cout << " value " << j->getData2() << endl;
					}
						break;
						
					case MIDI_PROG_CHANGE:
						cout << t << ": Program change: channel " << ch
						<< " program " << j->getData1() << endl;
						break;
						
					case MIDI_CHNL_AFTERTOUCH:
						cout << t << ": Channel aftertouch: channel " << ch
						<< " pressure " << j->getData1() << endl;
						break;
						
					case MIDI_PITCH_BEND:
						cout << t << ": Pitch bend: channel " << ch
						<< " value " << (int)j->getData2() * 128 + (int)j->getData1() << endl;
						break;
						
					case MIDI_SYSTEM_EXCLUSIVE:
						cout << t << ": System exclusive: code "
						<< (int)j->getMessageType() << " message length " <<
						j->getMetaMessage().length() << endl;
						break;
						
						
				}
				
				
			}
			
			
		}
		
}//end cannam midi main	
	
*/