00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "MIDIFileWriter.h"
00024
00025 #include "MIDIEvent.h"
00026
00027 #include "model/NoteModel.h"
00028
00029 #include "base/Pitch.h"
00030
00031 #include <algorithm>
00032 #include <fstream>
00033
00034 using std::ofstream;
00035 using std::string;
00036 using std::ios;
00037
00038 using namespace MIDIConstants;
00039
00040 MIDIFileWriter::MIDIFileWriter(QString path, NoteModel *model, float tempo) :
00041 m_path(path),
00042 m_model(model),
00043 m_modelUsesHz(false),
00044 m_tempo(tempo)
00045 {
00046 if (model->getScaleUnits().toLower() == "hz") m_modelUsesHz = true;
00047
00048 if (!convert()) {
00049 m_error = "Conversion from model to internal MIDI format failed";
00050 }
00051 }
00052
00053 MIDIFileWriter::~MIDIFileWriter()
00054 {
00055 for (MIDIComposition::iterator i = m_midiComposition.begin();
00056 i != m_midiComposition.end(); ++i) {
00057
00058 for (MIDITrack::iterator j = i->second.begin();
00059 j != i->second.end(); ++j) {
00060 delete *j;
00061 }
00062
00063 i->second.clear();
00064 }
00065
00066 m_midiComposition.clear();
00067 }
00068
00069 bool
00070 MIDIFileWriter::isOK() const
00071 {
00072 return m_error == "";
00073 }
00074
00075 QString
00076 MIDIFileWriter::getError() const
00077 {
00078 return m_error;
00079 }
00080
00081 void
00082 MIDIFileWriter::write()
00083 {
00084 writeComposition();
00085 }
00086
00087 string
00088 MIDIFileWriter::intToMIDIBytes(int number) const
00089 {
00090 MIDIByte upper;
00091 MIDIByte lower;
00092
00093 upper = (number & 0xFF00) >> 8;
00094 lower = (number & 0x00FF);
00095
00096 string rv;
00097 rv += upper;
00098 rv += lower;
00099 return rv;
00100 }
00101
00102 string
00103 MIDIFileWriter::longToMIDIBytes(unsigned long number) const
00104 {
00105 MIDIByte upper1;
00106 MIDIByte lower1;
00107 MIDIByte upper2;
00108 MIDIByte lower2;
00109
00110 upper1 = (number & 0xff000000) >> 24;
00111 lower1 = (number & 0x00ff0000) >> 16;
00112 upper2 = (number & 0x0000ff00) >> 8;
00113 lower2 = (number & 0x000000ff);
00114
00115 string rv;
00116 rv += upper1;
00117 rv += lower1;
00118 rv += upper2;
00119 rv += lower2;
00120 return rv;
00121 }
00122
00123
00124
00125
00126
00127 string
00128 MIDIFileWriter::longToVarBuffer(unsigned long number) const
00129 {
00130 string rv;
00131
00132 long inNumber = number;
00133 long outNumber;
00134
00135
00136 outNumber = number & 0x7f;
00137
00138
00139
00140
00141
00142 while ((inNumber >>= 7 ) > 0) {
00143 outNumber <<= 8;
00144 outNumber |= 0x80;
00145 outNumber += (inNumber & 0x7f);
00146 }
00147
00148
00149
00150 while (true) {
00151 rv += (MIDIByte)(outNumber & 0xff);
00152 if (outNumber & 0x80)
00153 outNumber >>= 8;
00154 else
00155 break;
00156 }
00157
00158 return rv;
00159 }
00160
00161 bool
00162 MIDIFileWriter::writeHeader()
00163 {
00164 *m_midiFile << MIDI_FILE_HEADER;
00165
00166
00167 *m_midiFile << (MIDIByte) 0x00;
00168 *m_midiFile << (MIDIByte) 0x00;
00169 *m_midiFile << (MIDIByte) 0x00;
00170 *m_midiFile << (MIDIByte) 0x06;
00171
00172
00173 *m_midiFile << (MIDIByte) 0x00;
00174 *m_midiFile << (MIDIByte) m_format;
00175
00176 *m_midiFile << intToMIDIBytes(m_numberOfTracks);
00177
00178 *m_midiFile << intToMIDIBytes(m_timingDivision);
00179
00180 return true;
00181 }
00182
00183 bool
00184 MIDIFileWriter::writeTrack(int trackNumber)
00185 {
00186 bool retOK = true;
00187 MIDIByte eventCode = 0;
00188 MIDITrack::iterator midiEvent;
00189
00190
00191
00192
00193 string trackBuffer;
00194
00195 for (midiEvent = m_midiComposition[trackNumber].begin();
00196 midiEvent != m_midiComposition[trackNumber].end();
00197 midiEvent++) {
00198
00199
00200 trackBuffer += longToVarBuffer((*midiEvent)->getTime());
00201
00202 if ((*midiEvent)->isMeta()) {
00203 trackBuffer += MIDI_FILE_META_EVENT;
00204 trackBuffer += (*midiEvent)->getMetaEventCode();
00205
00206
00207 trackBuffer += longToVarBuffer((*midiEvent)->
00208 getMetaMessage().length());
00209
00210 trackBuffer += (*midiEvent)->getMetaMessage();
00211 } else {
00212
00213 if (((*midiEvent)->getEventCode() != eventCode) ||
00214 ((*midiEvent)->getEventCode() == MIDI_SYSTEM_EXCLUSIVE)) {
00215 trackBuffer += (*midiEvent)->getEventCode();
00216 eventCode = (*midiEvent)->getEventCode();
00217 }
00218
00219
00220
00221 switch ((*midiEvent)->getMessageType()) {
00222 case MIDI_NOTE_ON:
00223 case MIDI_NOTE_OFF:
00224 case MIDI_POLY_AFTERTOUCH:
00225 trackBuffer += (*midiEvent)->getData1();
00226 trackBuffer += (*midiEvent)->getData2();
00227 break;
00228
00229 case MIDI_CTRL_CHANGE:
00230 trackBuffer += (*midiEvent)->getData1();
00231 trackBuffer += (*midiEvent)->getData2();
00232 break;
00233
00234 case MIDI_PROG_CHANGE:
00235 trackBuffer += (*midiEvent)->getData1();
00236 break;
00237
00238 case MIDI_CHNL_AFTERTOUCH:
00239 trackBuffer += (*midiEvent)->getData1();
00240 break;
00241
00242 case MIDI_PITCH_BEND:
00243 trackBuffer += (*midiEvent)->getData1();
00244 trackBuffer += (*midiEvent)->getData2();
00245 break;
00246
00247 case MIDI_SYSTEM_EXCLUSIVE:
00248
00249 trackBuffer +=
00250 longToVarBuffer((*midiEvent)->getMetaMessage().length());
00251
00252
00253 trackBuffer += (*midiEvent)->getMetaMessage();
00254 break;
00255
00256 default:
00257 break;
00258 }
00259 }
00260 }
00261
00262
00263
00264 *m_midiFile << MIDI_TRACK_HEADER;
00265
00266
00267
00268 *m_midiFile << longToMIDIBytes((long)trackBuffer.length());
00269
00270
00271
00272 *m_midiFile << trackBuffer;
00273
00274 return retOK;
00275 }
00276
00277 bool
00278 MIDIFileWriter::writeComposition()
00279 {
00280 bool retOK = true;
00281
00282 m_midiFile =
00283 new ofstream(m_path.toLocal8Bit().data(), ios::out | ios::binary);
00284
00285 if (!(*m_midiFile)) {
00286 m_error = "Can't open file for writing.";
00287 delete m_midiFile;
00288 m_midiFile = 0;
00289 return false;
00290 }
00291
00292 if (!writeHeader()) {
00293 retOK = false;
00294 }
00295
00296 for (unsigned int i = 0; i < m_numberOfTracks; i++) {
00297 if (!writeTrack(i)) {
00298 retOK = false;
00299 }
00300 }
00301
00302 m_midiFile->close();
00303 delete m_midiFile;
00304 m_midiFile = 0;
00305
00306 if (!retOK) {
00307 m_error = "MIDI file write failed";
00308 }
00309
00310 return retOK;
00311 }
00312
00313 bool
00314 MIDIFileWriter::convert()
00315 {
00316 m_timingDivision = 480;
00317 m_format = MIDI_SINGLE_TRACK_FILE;
00318 m_numberOfTracks = 1;
00319
00320 int track = 0;
00321 int midiChannel = 0;
00322
00323 MIDIEvent *event;
00324
00325 event = new MIDIEvent(0, MIDI_FILE_META_EVENT, MIDI_CUE_POINT,
00326 "Exported from Sonic Visualiser");
00327 m_midiComposition[track].push_back(event);
00328
00329 event = new MIDIEvent(0, MIDI_FILE_META_EVENT, MIDI_CUE_POINT,
00330 "http://www.sonicvisualiser.org/");
00331 m_midiComposition[track].push_back(event);
00332
00333 long tempoValue = long(60000000.0 / m_tempo + 0.01);
00334 string tempoString;
00335 tempoString += (MIDIByte)(tempoValue >> 16 & 0xFF);
00336 tempoString += (MIDIByte)(tempoValue >> 8 & 0xFF);
00337 tempoString += (MIDIByte)(tempoValue & 0xFF);
00338
00339 event = new MIDIEvent(0, MIDI_FILE_META_EVENT, MIDI_SET_TEMPO,
00340 tempoString);
00341 m_midiComposition[track].push_back(event);
00342
00343
00344
00345 const NoteModel::PointList ¬es =
00346 static_cast<SparseModel<Note> *>(m_model)->getPoints();
00347
00348 for (NoteModel::PointList::const_iterator i = notes.begin();
00349 i != notes.end(); ++i) {
00350
00351 long frame = i->frame;
00352 float value = i->value;
00353 size_t duration = i->duration;
00354
00355 int pitch;
00356
00357 if (m_modelUsesHz) {
00358 pitch = Pitch::getPitchForFrequency(value);
00359 } else {
00360 pitch = lrintf(value);
00361 }
00362
00363 if (pitch < 0) pitch = 0;
00364 if (pitch > 127) pitch = 127;
00365
00366
00367
00368 double seconds = double(frame) / double(m_model->getSampleRate());
00369 double quarters = (seconds * m_tempo) / 60.0;
00370 unsigned long midiTime = lrint(quarters * m_timingDivision);
00371
00372 int velocity = 100;
00373 if (i->level > 0.f && i->level <= 1.f) {
00374 velocity = lrintf(i->level * 127.f);
00375 }
00376
00377
00378 seconds = double(frame + duration) / double(m_model->getSampleRate());
00379 quarters = (seconds * m_tempo) / 60.0;
00380 unsigned long endTime = lrint(quarters * m_timingDivision);
00381
00382
00383
00384
00385
00386
00387 event = new MIDIEvent(midiTime,
00388 MIDI_NOTE_ON | midiChannel,
00389 pitch,
00390 velocity);
00391 m_midiComposition[track].push_back(event);
00392
00393 event = new MIDIEvent(endTime,
00394 MIDI_NOTE_OFF | midiChannel,
00395 pitch,
00396 127);
00397
00398 m_midiComposition[track].push_back(event);
00399 }
00400
00401
00402
00403
00404 for (unsigned int i = 0; i < m_numberOfTracks; i++) {
00405
00406 unsigned long lastMidiTime = 0;
00407
00408
00409
00410
00411
00412 std::stable_sort(m_midiComposition[i].begin(),
00413 m_midiComposition[i].end(),
00414 MIDIEventCmp());
00415
00416 for (MIDITrack::iterator it = m_midiComposition[i].begin();
00417 it != m_midiComposition[i].end(); it++) {
00418 unsigned long deltaTime = (*it)->getTime() - lastMidiTime;
00419 lastMidiTime = (*it)->getTime();
00420 (*it)->setTime(deltaTime);
00421 }
00422
00423
00424
00425 event = new MIDIEvent(0, MIDI_FILE_META_EVENT,
00426 MIDI_END_OF_TRACK, "");
00427
00428 m_midiComposition[i].push_back(event);
00429 }
00430
00431 return true;
00432 }
00433