Mercurial > hg > midi-score-follower
view hackday/CannamMidiFileLoader.cpp @ 36:5a1b0c6fa1fb
Added class to read in the csv Annotation file, then write out the respective difference between the performed piece as followed here, and the annotation of RWC by Ewert and Muller
author | Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk> |
---|---|
date | Thu, 15 Dec 2011 02:28:49 +0000 |
parents | 9a70d9abdc8b |
children |
line wrap: on
line source
/* * CannamMidiFileLoader.cpp * midi-score-follower * * Created by Andrew on 19/08/2011. * Copyright 2011 QMUL. All rights reserved. * */ #include "MIDIFileReader.h" #include "CannamMidiFileLoader.h" CannamMidiFileLoader::CannamMidiFileLoader(){ chopBeginning = true; firstTickTime = 0; } int CannamMidiFileLoader::loadFile(std::string& filename, midiEventHolder& myMidiEvents){ noteOnIndex = 0; firstTickTime = 0; myMidiEvents.clearAllEvents(); setTempoFromMidiValue(500000, myMidiEvents);//default is 120bpm myMidiEvents.pulsesPerQuarternote = 240;//default //myMidiEvents.measureVector.push_back(0); //int main(int argc, char **argv) //{ // if (argc != 2) { // cerr << "Usage: midifile <file.mid>" << endl; // return 1; // } // std::string filename = midiFileName;//argv[1]; 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) { if (printMidiInfo) cout << "Timing division: " << fr.getTimingDivision() << " ppq" << endl; myMidiEvents.pulsesPerQuarternote = fr.getTimingDivision(); ticksPerMeasure = myMidiEvents.pulsesPerQuarternote * 4;//default setting } else { int frames = 256 - (td >> 8); int subframes = td & 0xff; if (printMidiInfo) cout << "SMPTE timing: " << frames << " fps, " << subframes << " subframes" << endl; } for (MIDIComposition::const_iterator i = c.begin(); i != c.end(); ++i) { if (printMidiInfo) 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; //if (printMidiInfo) cout << t << ": Tempo: " << 60000000.0 / double(tempo) << endl; setTempoFromMidiValue(tempo, myMidiEvents); DoubleVector tmp; double lastTickInMillis = 0; double millisTimeNow = lastTickInMillis; int tickInterval = 0; if (myMidiEvents.periodValues.size() > 0){ lastTickInMillis = myMidiEvents.periodValues[myMidiEvents.periodValues.size()-1][2]; tickInterval = (t - firstTickTime) - myMidiEvents.periodValues[myMidiEvents.periodValues.size()-1][0]; millisTimeNow = lastTickInMillis + (myMidiEvents.periodValues[myMidiEvents.periodValues.size()-1][1]*tickInterval); } if (!chopBeginning) tmp.push_back(t); else tmp.push_back(t - firstTickTime); tmp.push_back(60000000.0 / double(tempo)); tmp.push_back(millisTimeNow); myMidiEvents.periodValues.push_back(tmp); printf("tick[%i]: TEMPO period %f : time now %f\n", t, 60000000.0 / double(tempo), millisTimeNow); //printf("period double is %f\n", myMidiEvents.period); } break; case MIDI_TIME_SIGNATURE: { int numerator = j->getMetaMessage()[0]; int denominator = 1 << (int)j->getMetaMessage()[1]; newTimeSignature(t, numerator, denominator, myMidiEvents); //if (printMidiInfo) cout << t << ": Time signature: " << numerator << "/" << denominator << endl; printf(" ticks %i Time signature: %i by %i \n", t, numerator , denominator ); } 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; if (printMidiInfo) 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: if (printMidiInfo) cout << t << ": Note: channel " << ch << " duration " << j->getDuration() << " pitch " << j->getPitch() << " velocity " << j->getVelocity() << "event time " << myMidiEvents.getEventTimeMillis(t) << endl; if (noteOnIndex == 0 || t < firstTickTime){ //easier just to pick the minimum firstTickTime = t; printf("FIRST TICK TIME %i\n", firstTickTime); } noteOnIndex++; v.clear(); // printf("note on at %i\n", t); if (!chopBeginning) v.push_back(t); else v.push_back(t - firstTickTime); v.push_back(j->getPitch()); v.push_back(j->getVelocity()); v.push_back(j->getDuration()); myMidiEvents.recordedNoteOnMatrix.push_back(v); myMidiEvents.noteOnMatches.push_back(false); break; case MIDI_POLY_AFTERTOUCH: if (printMidiInfo) 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; } if (printMidiInfo) cout << t << ": Controller change: channel " << ch << " controller " << j->getData1(); if (name != "") cout << " (" << name << ")"; cout << " value " << j->getData2() << endl; } break; case MIDI_PROG_CHANGE: if (printMidiInfo) cout << t << ": Program change: channel " << ch << " program " << j->getData1() << endl; break; case MIDI_CHNL_AFTERTOUCH: if (printMidiInfo) cout << t << ": Channel aftertouch: channel " << ch << " pressure " << j->getData1() << endl; break; case MIDI_PITCH_BEND: if (printMidiInfo) cout << t << ": Pitch bend: channel " << ch << " value " << (int)j->getData2() * 128 + (int)j->getData1() << endl; break; case MIDI_SYSTEM_EXCLUSIVE: if (printMidiInfo) cout << t << ": System exclusive: code " << (int)j->getMessageType() << " message length " << j->getMetaMessage().length() << endl; break; } } } if (printMidiInfo) myMidiEvents.printRecordedEvents(); //printMeasuresSoFar(myMidiEvents); if (myMidiEvents.recordedNoteOnMatrix.size() > 0){ printf("END FILE MEASURE UPDATE\n"); updateMeasureToTickPosition(myMidiEvents.recordedNoteOnMatrix[myMidiEvents.recordedNoteOnMatrix.size()-1][0], myMidiEvents); // printMeasuresSoFar(myMidiEvents); } // printf("|||||||||||||||||||||| \n\n\n\n\n\n\n"); myMidiEvents.reorderMatrixFromNoteTimes(myMidiEvents.recordedNoteOnMatrix); myMidiEvents.correctTiming(myMidiEvents.recordedNoteOnMatrix); myMidiEvents.doublecheckOrder(myMidiEvents.recordedNoteOnMatrix); createEventTiming(myMidiEvents); printf("BEFORE DOING MEASURE UPDATE\n first tick pos is %i\n", firstTickTime); // printMeasuresSoFar(myMidiEvents); if (chopBeginning) correctMeasuresTiming(myMidiEvents); if (printMidiInfo) myMidiEvents.printRecordedEvents(); printf("Duration of MIDI file is %f \n", myMidiEvents.recordedEventTimes[myMidiEvents.recordedEventTimes.size()-1]); //printMeasuresSoFar(myMidiEvents); }//end cannam midi main void CannamMidiFileLoader::createEventTiming( midiEventHolder& myMidiEvents){ long t; t = myMidiEvents.recordedNoteOnMatrix[0][0]; firstNoteTime = myMidiEvents.getEventTimeMillis(t); for (int i = 0; i < myMidiEvents.recordedNoteOnMatrix.size();i++){ t = myMidiEvents.recordedNoteOnMatrix[i][0]; if (!chopBeginning) myMidiEvents.recordedEventTimes.push_back(myMidiEvents.getEventTimeMillis(t)); else { myMidiEvents.recordedEventTimes.push_back(myMidiEvents.getEventTimeMillis(t) - firstNoteTime); } } } void CannamMidiFileLoader::setTempoFromMidiValue(long tempo, midiEventHolder& myMidiEvents){ myMidiEvents.tempo = 60000000.0 / double(tempo); myMidiEvents.period = double(tempo)/1000.0; myMidiEvents.ticksFactor = myMidiEvents.pulsesPerQuarternote / myMidiEvents.period; } void CannamMidiFileLoader::newTimeSignature(int ticks, int numerator, int denominator, midiEventHolder& myMidiEvents){ updateMeasureToTickPosition(ticks, myMidiEvents); ticksPerMeasure = myMidiEvents.pulsesPerQuarternote * 4 * numerator / denominator; } void CannamMidiFileLoader::updateMeasureToTickPosition(int ticks, midiEventHolder& myMidiEvents){ printf("update measure at tick pos %i at tpm %i\n", ticks, ticksPerMeasure); int measureVectorSize = myMidiEvents.measureVector.size(); int lastMeasurePosition = 0; // if (chopBeginning) // lastMeasurePosition = -1*firstTickTime; if (measureVectorSize > 0) lastMeasurePosition = myMidiEvents.measureVector[measureVectorSize-1]; while (lastMeasurePosition < ticks){ //update lastMeasurePosition += ticksPerMeasure; myMidiEvents.measureVector.push_back(lastMeasurePosition); // cout << "MEASURE " << myMidiEvents.measureVector.size()-1 << " is " << lastMeasurePosition << endl; // printf("MEASURE %i is %i \n", (int)myMidiEvents.measureVector.size()-1 , lastMeasurePosition); } } void CannamMidiFileLoader::correctMeasuresTiming(midiEventHolder& myMidiEvents){ //correct measures for (int i = 0;i <myMidiEvents.measureVector.size();i++){ myMidiEvents.measureVector[i] -= firstTickTime; } printf("AFTER DOING MEASURE UPDATE\n"); printMeasuresSoFar(myMidiEvents); } void CannamMidiFileLoader::printMeasuresSoFar(midiEventHolder& myMidiEvents){ for (int i = 0;i < myMidiEvents.measureVector.size();i++){ printf("measure [%i] at %i\n", i, myMidiEvents.measureVector[i]); } }