annotate hackday/CannamMidiFileLoader.cpp @ 52:13194a9dca77 tip

Added exporting of image and text data
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Tue, 17 Jul 2012 22:13:10 +0100
parents 9a70d9abdc8b
children
rev   line source
andrew@24 1 /*
andrew@24 2 * CannamMidiFileLoader.cpp
andrew@24 3 * midi-score-follower
andrew@24 4 *
andrew@24 5 * Created by Andrew on 19/08/2011.
andrew@24 6 * Copyright 2011 QMUL. All rights reserved.
andrew@24 7 *
andrew@24 8 */
andrew@24 9
andrew@24 10 #include "MIDIFileReader.h"
andrew@24 11 #include "CannamMidiFileLoader.h"
andrew@24 12
andrew@24 13 CannamMidiFileLoader::CannamMidiFileLoader(){
andrew@24 14 chopBeginning = true;
andrew@31 15 firstTickTime = 0;
andrew@24 16 }
andrew@24 17
andrew@24 18 int CannamMidiFileLoader::loadFile(std::string& filename, midiEventHolder& myMidiEvents){
andrew@24 19
andrew@24 20 noteOnIndex = 0;
andrew@31 21 firstTickTime = 0;
andrew@24 22 myMidiEvents.clearAllEvents();
andrew@24 23
andrew@31 24
andrew@24 25 setTempoFromMidiValue(500000, myMidiEvents);//default is 120bpm
andrew@24 26 myMidiEvents.pulsesPerQuarternote = 240;//default
andrew@31 27 //myMidiEvents.measureVector.push_back(0);
andrew@24 28 //int main(int argc, char **argv)
andrew@24 29 //{
andrew@24 30 // if (argc != 2) {
andrew@24 31 // cerr << "Usage: midifile <file.mid>" << endl;
andrew@24 32 // return 1;
andrew@24 33 // }
andrew@24 34
andrew@24 35 // std::string filename = midiFileName;//argv[1];
andrew@24 36
andrew@24 37 MIDIFileReader fr(filename);
andrew@24 38
andrew@24 39 if (!fr.isOK()) {
andrew@24 40 std::cerr << "Error: " << fr.getError().c_str() << std::endl;
andrew@24 41 return 1;
andrew@24 42 }
andrew@24 43
andrew@24 44 MIDIComposition c = fr.load();
andrew@24 45
andrew@24 46 switch (fr.getFormat()) {
andrew@24 47 case MIDI_SINGLE_TRACK_FILE: cout << "Format: MIDI Single Track File" << endl; break;
andrew@24 48 case MIDI_SIMULTANEOUS_TRACK_FILE: cout << "Format: MIDI Simultaneous Track File" << endl; break;
andrew@24 49 case MIDI_SEQUENTIAL_TRACK_FILE: cout << "Format: MIDI Sequential Track File" << endl; break;
andrew@24 50 default: cout << "Format: Unknown MIDI file format?" << endl; break;
andrew@24 51 }
andrew@24 52
andrew@24 53 cout << "Tracks: " << c.size() << endl;
andrew@24 54
andrew@24 55 int td = fr.getTimingDivision();
andrew@24 56 if (td < 32768) {
andrew@24 57 if (printMidiInfo)
andrew@24 58 cout << "Timing division: " << fr.getTimingDivision() << " ppq" << endl;
andrew@24 59 myMidiEvents.pulsesPerQuarternote = fr.getTimingDivision();
andrew@25 60 ticksPerMeasure = myMidiEvents.pulsesPerQuarternote * 4;//default setting
andrew@25 61
andrew@24 62 } else {
andrew@24 63 int frames = 256 - (td >> 8);
andrew@24 64 int subframes = td & 0xff;
andrew@24 65 if (printMidiInfo)
andrew@24 66 cout << "SMPTE timing: " << frames << " fps, " << subframes << " subframes" << endl;
andrew@24 67 }
andrew@24 68
andrew@24 69 for (MIDIComposition::const_iterator i = c.begin(); i != c.end(); ++i) {
andrew@24 70 if (printMidiInfo)
andrew@24 71 cout << "Start of track: " << i->first+1 << endl;
andrew@24 72
andrew@24 73 for (MIDITrack::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
andrew@24 74
andrew@24 75 unsigned int t = j->getTime();
andrew@24 76 int ch = j->getChannelNumber();
andrew@24 77
andrew@24 78 if (j->isMeta()) {
andrew@24 79 int code = j->getMetaEventCode();
andrew@24 80 string name;
andrew@24 81 bool printable = true;
andrew@24 82 switch (code) {
andrew@24 83
andrew@24 84 case MIDI_END_OF_TRACK:
andrew@24 85 cout << t << ": End of track" << endl;
andrew@24 86 break;
andrew@24 87
andrew@24 88 case MIDI_TEXT_EVENT: name = "Text"; break;
andrew@24 89 case MIDI_COPYRIGHT_NOTICE: name = "Copyright"; break;
andrew@24 90 case MIDI_TRACK_NAME: name = "Track name"; break;
andrew@24 91 case MIDI_INSTRUMENT_NAME: name = "Instrument name"; break;
andrew@24 92 case MIDI_LYRIC: name = "Lyric"; break;
andrew@24 93 case MIDI_TEXT_MARKER: name = "Text marker"; break;
andrew@24 94 case MIDI_SEQUENCE_NUMBER: name = "Sequence number"; printable = false; break;
andrew@24 95 case MIDI_CHANNEL_PREFIX_OR_PORT: name = "Channel prefix or port"; printable = false; break;
andrew@24 96 case MIDI_CUE_POINT: name = "Cue point"; break;
andrew@24 97 case MIDI_CHANNEL_PREFIX: name = "Channel prefix"; printable = false; break;
andrew@24 98 case MIDI_SEQUENCER_SPECIFIC: name = "Sequencer specific"; printable = false; break;
andrew@24 99 case MIDI_SMPTE_OFFSET: name = "SMPTE offset"; printable = false; break;
andrew@24 100
andrew@24 101 case MIDI_SET_TEMPO:
andrew@24 102 {
andrew@24 103 int m0 = j->getMetaMessage()[0];
andrew@24 104 int m1 = j->getMetaMessage()[1];
andrew@24 105 int m2 = j->getMetaMessage()[2];
andrew@24 106 long tempo = (((m0 << 8) + m1) << 8) + m2;
andrew@31 107 //if (printMidiInfo)
andrew@24 108 cout << t << ": Tempo: " << 60000000.0 / double(tempo) << endl;
andrew@24 109 setTempoFromMidiValue(tempo, myMidiEvents);
andrew@31 110 DoubleVector tmp;
andrew@31 111
andrew@31 112
andrew@31 113 double lastTickInMillis = 0;
andrew@31 114 double millisTimeNow = lastTickInMillis;
andrew@31 115 int tickInterval = 0;
andrew@31 116 if (myMidiEvents.periodValues.size() > 0){
andrew@31 117 lastTickInMillis = myMidiEvents.periodValues[myMidiEvents.periodValues.size()-1][2];
andrew@31 118 tickInterval = (t - firstTickTime) - myMidiEvents.periodValues[myMidiEvents.periodValues.size()-1][0];
andrew@31 119 millisTimeNow = lastTickInMillis + (myMidiEvents.periodValues[myMidiEvents.periodValues.size()-1][1]*tickInterval);
andrew@31 120
andrew@31 121 }
andrew@31 122
andrew@31 123 if (!chopBeginning)
andrew@31 124 tmp.push_back(t);
andrew@31 125 else
andrew@31 126 tmp.push_back(t - firstTickTime);
andrew@31 127
andrew@31 128 tmp.push_back(60000000.0 / double(tempo));
andrew@31 129 tmp.push_back(millisTimeNow);
andrew@31 130 myMidiEvents.periodValues.push_back(tmp);
andrew@31 131
andrew@31 132 printf("tick[%i]: TEMPO period %f : time now %f\n", t, 60000000.0 / double(tempo), millisTimeNow);
andrew@31 133 //printf("period double is %f\n", myMidiEvents.period);
andrew@25 134
andrew@24 135 }
andrew@24 136 break;
andrew@24 137
andrew@24 138 case MIDI_TIME_SIGNATURE:
andrew@24 139 {
andrew@24 140 int numerator = j->getMetaMessage()[0];
andrew@24 141 int denominator = 1 << (int)j->getMetaMessage()[1];
andrew@25 142
andrew@25 143 newTimeSignature(t, numerator, denominator, myMidiEvents);
andrew@25 144
andrew@25 145 //if (printMidiInfo)
andrew@24 146 cout << t << ": Time signature: " << numerator << "/" << denominator << endl;
andrew@31 147 printf(" ticks %i Time signature: %i by %i \n", t, numerator , denominator );
andrew@24 148 }
andrew@24 149
andrew@24 150 case MIDI_KEY_SIGNATURE:
andrew@24 151 {
andrew@24 152 int accidentals = j->getMetaMessage()[0];
andrew@24 153 int isMinor = j->getMetaMessage()[1];
andrew@24 154 bool isSharp = accidentals < 0 ? false : true;
andrew@24 155 accidentals = accidentals < 0 ? -accidentals : accidentals;
andrew@24 156 if (printMidiInfo)
andrew@24 157 cout << t << ": Key signature: " << accidentals << " "
andrew@24 158 << (isSharp ?
andrew@24 159 (accidentals > 1 ? "sharps" : "sharp") :
andrew@24 160 (accidentals > 1 ? "flats" : "flat"))
andrew@24 161 << (isMinor ? ", minor" : ", major") << endl;
andrew@24 162 }
andrew@24 163
andrew@24 164 }
andrew@24 165
andrew@24 166
andrew@24 167 if (name != "") {
andrew@24 168 if (printable) {
andrew@24 169 cout << t << ": File meta event: code " << code
andrew@24 170 << ": " << name << ": \"" << j->getMetaMessage()
andrew@24 171 << "\"" << endl;
andrew@24 172 } else {
andrew@24 173 cout << t << ": File meta event: code " << code
andrew@24 174 << ": " << name << ": ";
andrew@24 175 for (int k = 0; k < j->getMetaMessage().length(); ++k) {
andrew@24 176 cout << (int)j->getMetaMessage()[k] << " ";
andrew@24 177 }
andrew@24 178 }
andrew@24 179 }
andrew@24 180 continue;
andrew@24 181 }
andrew@24 182
andrew@24 183 switch (j->getMessageType()) {
andrew@24 184
andrew@24 185 case MIDI_NOTE_ON:
andrew@24 186 if (printMidiInfo)
andrew@24 187 cout << t << ": Note: channel " << ch
andrew@24 188 << " duration " << j->getDuration()
andrew@24 189 << " pitch " << j->getPitch()
andrew@24 190 << " velocity " << j->getVelocity()
andrew@24 191 << "event time " << myMidiEvents.getEventTimeMillis(t) << endl;
andrew@24 192
andrew@24 193
andrew@31 194 if (noteOnIndex == 0 || t < firstTickTime){
andrew@31 195 //easier just to pick the minimum
andrew@24 196 firstTickTime = t;
andrew@31 197 printf("FIRST TICK TIME %i\n", firstTickTime);
andrew@24 198 }
andrew@24 199
andrew@24 200 noteOnIndex++;
andrew@24 201
andrew@24 202 v.clear();
andrew@24 203
andrew@31 204 // printf("note on at %i\n", t);
andrew@31 205
andrew@24 206 if (!chopBeginning)
andrew@24 207 v.push_back(t);
andrew@24 208 else
andrew@24 209 v.push_back(t - firstTickTime);
andrew@24 210
andrew@24 211 v.push_back(j->getPitch());
andrew@24 212 v.push_back(j->getVelocity());
andrew@24 213 v.push_back(j->getDuration());
andrew@24 214 myMidiEvents.recordedNoteOnMatrix.push_back(v);
andrew@24 215
andrew@24 216 myMidiEvents.noteOnMatches.push_back(false);
andrew@24 217
andrew@24 218 break;
andrew@24 219
andrew@24 220 case MIDI_POLY_AFTERTOUCH:
andrew@24 221 if (printMidiInfo)
andrew@24 222 cout << t << ": Polyphonic aftertouch: channel " << ch
andrew@24 223 << " pitch " << j->getPitch()
andrew@24 224 << " pressure " << j->getData2() << endl;
andrew@24 225 break;
andrew@24 226
andrew@24 227 case MIDI_CTRL_CHANGE:
andrew@24 228 {
andrew@24 229 int controller = j->getData1();
andrew@24 230 string name;
andrew@24 231 switch (controller) {
andrew@24 232 case MIDI_CONTROLLER_BANK_MSB: name = "Bank select MSB"; break;
andrew@24 233 case MIDI_CONTROLLER_VOLUME: name = "Volume"; break;
andrew@24 234 case MIDI_CONTROLLER_BANK_LSB: name = "Bank select LSB"; break;
andrew@24 235 case MIDI_CONTROLLER_MODULATION: name = "Modulation wheel"; break;
andrew@24 236 case MIDI_CONTROLLER_PAN: name = "Pan"; break;
andrew@24 237 case MIDI_CONTROLLER_SUSTAIN: name = "Sustain"; break;
andrew@24 238 case MIDI_CONTROLLER_RESONANCE: name = "Resonance"; break;
andrew@24 239 case MIDI_CONTROLLER_RELEASE: name = "Release"; break;
andrew@24 240 case MIDI_CONTROLLER_ATTACK: name = "Attack"; break;
andrew@24 241 case MIDI_CONTROLLER_FILTER: name = "Filter"; break;
andrew@24 242 case MIDI_CONTROLLER_REVERB: name = "Reverb"; break;
andrew@24 243 case MIDI_CONTROLLER_CHORUS: name = "Chorus"; break;
andrew@24 244 case MIDI_CONTROLLER_NRPN_1: name = "NRPN 1"; break;
andrew@24 245 case MIDI_CONTROLLER_NRPN_2: name = "NRPN 2"; break;
andrew@24 246 case MIDI_CONTROLLER_RPN_1: name = "RPN 1"; break;
andrew@24 247 case MIDI_CONTROLLER_RPN_2: name = "RPN 2"; break;
andrew@24 248 case MIDI_CONTROLLER_SOUNDS_OFF: name = "All sounds off"; break;
andrew@24 249 case MIDI_CONTROLLER_RESET: name = "Reset"; break;
andrew@24 250 case MIDI_CONTROLLER_LOCAL: name = "Local"; break;
andrew@24 251 case MIDI_CONTROLLER_ALL_NOTES_OFF: name = "All notes off"; break;
andrew@24 252 }
andrew@24 253 if (printMidiInfo)
andrew@24 254 cout << t << ": Controller change: channel " << ch
andrew@24 255 << " controller " << j->getData1();
andrew@24 256 if (name != "") cout << " (" << name << ")";
andrew@24 257 cout << " value " << j->getData2() << endl;
andrew@24 258 }
andrew@24 259 break;
andrew@24 260
andrew@24 261 case MIDI_PROG_CHANGE:
andrew@24 262 if (printMidiInfo)
andrew@24 263 cout << t << ": Program change: channel " << ch
andrew@24 264 << " program " << j->getData1() << endl;
andrew@24 265 break;
andrew@24 266
andrew@24 267 case MIDI_CHNL_AFTERTOUCH:
andrew@24 268 if (printMidiInfo)
andrew@24 269 cout << t << ": Channel aftertouch: channel " << ch
andrew@24 270 << " pressure " << j->getData1() << endl;
andrew@24 271 break;
andrew@24 272
andrew@24 273 case MIDI_PITCH_BEND:
andrew@24 274 if (printMidiInfo)
andrew@24 275 cout << t << ": Pitch bend: channel " << ch
andrew@24 276 << " value " << (int)j->getData2() * 128 + (int)j->getData1() << endl;
andrew@24 277 break;
andrew@24 278
andrew@24 279 case MIDI_SYSTEM_EXCLUSIVE:
andrew@24 280 if (printMidiInfo)
andrew@24 281 cout << t << ": System exclusive: code "
andrew@24 282 << (int)j->getMessageType() << " message length " <<
andrew@24 283 j->getMetaMessage().length() << endl;
andrew@24 284 break;
andrew@24 285
andrew@24 286
andrew@24 287 }
andrew@24 288
andrew@24 289
andrew@24 290 }
andrew@24 291
andrew@24 292
andrew@24 293 }
andrew@24 294 if (printMidiInfo)
andrew@24 295 myMidiEvents.printRecordedEvents();
andrew@24 296
andrew@25 297 //printMeasuresSoFar(myMidiEvents);
andrew@25 298
andrew@25 299 if (myMidiEvents.recordedNoteOnMatrix.size() > 0){
andrew@25 300
andrew@25 301 printf("END FILE MEASURE UPDATE\n");
andrew@25 302 updateMeasureToTickPosition(myMidiEvents.recordedNoteOnMatrix[myMidiEvents.recordedNoteOnMatrix.size()-1][0], myMidiEvents);
andrew@25 303 // printMeasuresSoFar(myMidiEvents);
andrew@25 304 }
andrew@25 305
andrew@24 306 // printf("|||||||||||||||||||||| \n\n\n\n\n\n\n");
andrew@24 307 myMidiEvents.reorderMatrixFromNoteTimes(myMidiEvents.recordedNoteOnMatrix);
andrew@24 308 myMidiEvents.correctTiming(myMidiEvents.recordedNoteOnMatrix);
andrew@24 309 myMidiEvents.doublecheckOrder(myMidiEvents.recordedNoteOnMatrix);
andrew@24 310
andrew@24 311 createEventTiming(myMidiEvents);
andrew@25 312
andrew@31 313 printf("BEFORE DOING MEASURE UPDATE\n first tick pos is %i\n", firstTickTime);
andrew@31 314 // printMeasuresSoFar(myMidiEvents);
andrew@31 315 if (chopBeginning)
andrew@31 316 correctMeasuresTiming(myMidiEvents);
andrew@31 317
andrew@24 318 if (printMidiInfo)
andrew@25 319 myMidiEvents.printRecordedEvents();
andrew@25 320
andrew@30 321 printf("Duration of MIDI file is %f \n", myMidiEvents.recordedEventTimes[myMidiEvents.recordedEventTimes.size()-1]);
andrew@30 322
andrew@25 323 //printMeasuresSoFar(myMidiEvents);
andrew@24 324
andrew@24 325 }//end cannam midi main
andrew@24 326
andrew@24 327
andrew@24 328 void CannamMidiFileLoader::createEventTiming( midiEventHolder& myMidiEvents){
andrew@24 329
andrew@24 330 long t;
andrew@24 331 t = myMidiEvents.recordedNoteOnMatrix[0][0];
andrew@24 332 firstNoteTime = myMidiEvents.getEventTimeMillis(t);
andrew@24 333
andrew@24 334 for (int i = 0; i < myMidiEvents.recordedNoteOnMatrix.size();i++){
andrew@24 335 t = myMidiEvents.recordedNoteOnMatrix[i][0];
andrew@24 336
andrew@24 337 if (!chopBeginning)
andrew@24 338 myMidiEvents.recordedEventTimes.push_back(myMidiEvents.getEventTimeMillis(t));
andrew@24 339 else {
andrew@24 340 myMidiEvents.recordedEventTimes.push_back(myMidiEvents.getEventTimeMillis(t) - firstNoteTime);
andrew@24 341
andrew@24 342 }
andrew@24 343 }
andrew@24 344
andrew@24 345 }
andrew@24 346
andrew@24 347 void CannamMidiFileLoader::setTempoFromMidiValue(long tempo, midiEventHolder& myMidiEvents){
andrew@24 348 myMidiEvents.tempo = 60000000.0 / double(tempo);
andrew@24 349 myMidiEvents.period = double(tempo)/1000.0;
andrew@24 350 myMidiEvents.ticksFactor = myMidiEvents.pulsesPerQuarternote / myMidiEvents.period;
andrew@24 351 }
andrew@24 352
andrew@24 353
andrew@25 354 void CannamMidiFileLoader::newTimeSignature(int ticks, int numerator, int denominator, midiEventHolder& myMidiEvents){
andrew@24 355
andrew@25 356 updateMeasureToTickPosition(ticks, myMidiEvents);
andrew@25 357
andrew@25 358 ticksPerMeasure = myMidiEvents.pulsesPerQuarternote * 4 * numerator / denominator;
andrew@25 359
andrew@25 360 }
andrew@25 361
andrew@25 362 void CannamMidiFileLoader::updateMeasureToTickPosition(int ticks, midiEventHolder& myMidiEvents){
andrew@25 363 printf("update measure at tick pos %i at tpm %i\n", ticks, ticksPerMeasure);
andrew@25 364
andrew@25 365 int measureVectorSize = myMidiEvents.measureVector.size();
andrew@25 366 int lastMeasurePosition = 0;
andrew@31 367 // if (chopBeginning)
andrew@31 368 // lastMeasurePosition = -1*firstTickTime;
andrew@25 369
andrew@25 370 if (measureVectorSize > 0)
andrew@25 371 lastMeasurePosition = myMidiEvents.measureVector[measureVectorSize-1];
andrew@31 372
andrew@25 373
andrew@25 374 while (lastMeasurePosition < ticks){
andrew@25 375 //update
andrew@25 376 lastMeasurePosition += ticksPerMeasure;
andrew@25 377 myMidiEvents.measureVector.push_back(lastMeasurePosition);
andrew@27 378 // cout << "MEASURE " << myMidiEvents.measureVector.size()-1 << " is " << lastMeasurePosition << endl;
andrew@27 379 // printf("MEASURE %i is %i \n", (int)myMidiEvents.measureVector.size()-1 , lastMeasurePosition);
andrew@25 380 }
andrew@25 381 }
andrew@25 382
andrew@25 383
andrew@31 384 void CannamMidiFileLoader::correctMeasuresTiming(midiEventHolder& myMidiEvents){
andrew@31 385 //correct measures
andrew@31 386 for (int i = 0;i <myMidiEvents.measureVector.size();i++){
andrew@31 387 myMidiEvents.measureVector[i] -= firstTickTime;
andrew@31 388 }
andrew@31 389 printf("AFTER DOING MEASURE UPDATE\n");
andrew@31 390 printMeasuresSoFar(myMidiEvents);
andrew@31 391 }
andrew@31 392
andrew@25 393 void CannamMidiFileLoader::printMeasuresSoFar(midiEventHolder& myMidiEvents){
andrew@25 394 for (int i = 0;i < myMidiEvents.measureVector.size();i++){
andrew@25 395 printf("measure [%i] at %i\n", i, myMidiEvents.measureVector[i]);
andrew@25 396 }
andrew@25 397 }