svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 00008 This program is free software; you can redistribute it and/or 00009 modify it under the terms of the GNU General Public License as 00010 published by the Free Software Foundation; either version 2 of the 00011 License, or (at your option) any later version. See the file 00012 COPYING included with this distribution for more information. 00013 */ 00014 00015 00016 /* 00017 This is a modified version of a source file from the 00018 Rosegarden MIDI and audio sequencer and notation editor. 00019 This file copyright 2000-2007 Richard Bown and Chris Cannam 00020 and copyright 2007 QMUL. 00021 */ 00022 00023 #include "MIDIFileWriter.h" 00024 00025 #include "data/midi/MIDIEvent.h" 00026 #include "model/NoteData.h" 00027 00028 #include "base/Pitch.h" 00029 00030 #include <algorithm> 00031 #include <fstream> 00032 00033 using std::ofstream; 00034 using std::string; 00035 using std::ios; 00036 00037 using namespace MIDIConstants; 00038 00039 MIDIFileWriter::MIDIFileWriter(QString path, const NoteExportable *exportable, 00040 int sampleRate, float tempo) : 00041 m_path(path), 00042 m_exportable(exportable), 00043 m_sampleRate(sampleRate), 00044 m_tempo(tempo), 00045 m_midiFile(0) 00046 { 00047 if (!convert()) { 00048 m_error = "Conversion from model to internal MIDI format failed"; 00049 } 00050 } 00051 00052 MIDIFileWriter::~MIDIFileWriter() 00053 { 00054 for (MIDIComposition::iterator i = m_midiComposition.begin(); 00055 i != m_midiComposition.end(); ++i) { 00056 00057 for (MIDITrack::iterator j = i->second.begin(); 00058 j != i->second.end(); ++j) { 00059 delete *j; 00060 } 00061 00062 i->second.clear(); 00063 } 00064 00065 m_midiComposition.clear(); 00066 } 00067 00068 bool 00069 MIDIFileWriter::isOK() const 00070 { 00071 return m_error == ""; 00072 } 00073 00074 QString 00075 MIDIFileWriter::getError() const 00076 { 00077 return m_error; 00078 } 00079 00080 void 00081 MIDIFileWriter::write() 00082 { 00083 writeComposition(); 00084 } 00085 00086 string 00087 MIDIFileWriter::intToMIDIBytes(int number) const 00088 { 00089 MIDIByte upper; 00090 MIDIByte lower; 00091 00092 upper = (number & 0xFF00) >> 8; 00093 lower = (number & 0x00FF); 00094 00095 string rv; 00096 rv += upper; 00097 rv += lower; 00098 return rv; 00099 } 00100 00101 string 00102 MIDIFileWriter::longToMIDIBytes(unsigned long number) const 00103 { 00104 MIDIByte upper1; 00105 MIDIByte lower1; 00106 MIDIByte upper2; 00107 MIDIByte lower2; 00108 00109 upper1 = (number & 0xff000000) >> 24; 00110 lower1 = (number & 0x00ff0000) >> 16; 00111 upper2 = (number & 0x0000ff00) >> 8; 00112 lower2 = (number & 0x000000ff); 00113 00114 string rv; 00115 rv += upper1; 00116 rv += lower1; 00117 rv += upper2; 00118 rv += lower2; 00119 return rv; 00120 } 00121 00122 // Turn a delta time into a MIDI time - overlapping into 00123 // a maximum of four bytes using the MSB as the carry on 00124 // flag. 00125 // 00126 string 00127 MIDIFileWriter::longToVarBuffer(unsigned long number) const 00128 { 00129 string rv; 00130 00131 long inNumber = number; 00132 long outNumber; 00133 00134 // get the lowest 7 bits of the number 00135 outNumber = number & 0x7f; 00136 00137 // Shift and test and move the numbers 00138 // on if we need them - setting the MSB 00139 // as we go. 00140 // 00141 while ((inNumber >>= 7 ) > 0) { 00142 outNumber <<= 8; 00143 outNumber |= 0x80; 00144 outNumber += (inNumber & 0x7f); 00145 } 00146 00147 // Now move the converted number out onto the buffer 00148 // 00149 while (true) { 00150 rv += (MIDIByte)(outNumber & 0xff); 00151 if (outNumber & 0x80) 00152 outNumber >>= 8; 00153 else 00154 break; 00155 } 00156 00157 return rv; 00158 } 00159 00160 bool 00161 MIDIFileWriter::writeHeader() 00162 { 00163 *m_midiFile << MIDI_FILE_HEADER; 00164 00165 // Number of bytes in header 00166 *m_midiFile << (MIDIByte) 0x00; 00167 *m_midiFile << (MIDIByte) 0x00; 00168 *m_midiFile << (MIDIByte) 0x00; 00169 *m_midiFile << (MIDIByte) 0x06; 00170 00171 // File format 00172 *m_midiFile << (MIDIByte) 0x00; 00173 *m_midiFile << (MIDIByte) m_format; 00174 00175 *m_midiFile << intToMIDIBytes(m_numberOfTracks); 00176 00177 *m_midiFile << intToMIDIBytes(m_timingDivision); 00178 00179 return true; 00180 } 00181 00182 bool 00183 MIDIFileWriter::writeTrack(int trackNumber) 00184 { 00185 bool retOK = true; 00186 MIDIByte eventCode = 0; 00187 MIDITrack::iterator midiEvent; 00188 00189 // First we write into the trackBuffer, then write it out to the 00190 // file with its accompanying length. 00191 // 00192 string trackBuffer; 00193 00194 for (midiEvent = m_midiComposition[trackNumber].begin(); 00195 midiEvent != m_midiComposition[trackNumber].end(); 00196 midiEvent++) { 00197 00198 // Write the time to the buffer in MIDI format 00199 trackBuffer += longToVarBuffer((*midiEvent)->getTime()); 00200 00201 if ((*midiEvent)->isMeta()) { 00202 trackBuffer += MIDI_FILE_META_EVENT; 00203 trackBuffer += (*midiEvent)->getMetaEventCode(); 00204 00205 // Variable length number field 00206 trackBuffer += longToVarBuffer((*midiEvent)-> 00207 getMetaMessage().length()); 00208 00209 trackBuffer += (*midiEvent)->getMetaMessage(); 00210 } else { 00211 // Send the normal event code (with encoded channel information) 00212 if (((*midiEvent)->getEventCode() != eventCode) || 00213 ((*midiEvent)->getEventCode() == MIDI_SYSTEM_EXCLUSIVE)) { 00214 trackBuffer += (*midiEvent)->getEventCode(); 00215 eventCode = (*midiEvent)->getEventCode(); 00216 } 00217 00218 // Send the relevant data 00219 // 00220 switch ((*midiEvent)->getMessageType()) { 00221 case MIDI_NOTE_ON: 00222 case MIDI_NOTE_OFF: 00223 case MIDI_POLY_AFTERTOUCH: 00224 trackBuffer += (*midiEvent)->getData1(); 00225 trackBuffer += (*midiEvent)->getData2(); 00226 break; 00227 00228 case MIDI_CTRL_CHANGE: 00229 trackBuffer += (*midiEvent)->getData1(); 00230 trackBuffer += (*midiEvent)->getData2(); 00231 break; 00232 00233 case MIDI_PROG_CHANGE: 00234 trackBuffer += (*midiEvent)->getData1(); 00235 break; 00236 00237 case MIDI_CHNL_AFTERTOUCH: 00238 trackBuffer += (*midiEvent)->getData1(); 00239 break; 00240 00241 case MIDI_PITCH_BEND: 00242 trackBuffer += (*midiEvent)->getData1(); 00243 trackBuffer += (*midiEvent)->getData2(); 00244 break; 00245 00246 case MIDI_SYSTEM_EXCLUSIVE: 00247 // write out message length 00248 trackBuffer += 00249 longToVarBuffer((*midiEvent)->getMetaMessage().length()); 00250 00251 // now the message 00252 trackBuffer += (*midiEvent)->getMetaMessage(); 00253 break; 00254 00255 default: 00256 break; 00257 } 00258 } 00259 } 00260 00261 // Now we write the track - First the standard header.. 00262 // 00263 *m_midiFile << MIDI_TRACK_HEADER; 00264 00265 // ..now the length of the buffer.. 00266 // 00267 *m_midiFile << longToMIDIBytes((long)trackBuffer.length()); 00268 00269 // ..then the buffer itself.. 00270 // 00271 *m_midiFile << trackBuffer; 00272 00273 return retOK; 00274 } 00275 00276 bool 00277 MIDIFileWriter::writeComposition() 00278 { 00279 bool retOK = true; 00280 00281 m_midiFile = 00282 new ofstream(m_path.toLocal8Bit().data(), ios::out | ios::binary); 00283 00284 if (!(*m_midiFile)) { 00285 m_error = "Can't open file for writing."; 00286 delete m_midiFile; 00287 m_midiFile = 0; 00288 return false; 00289 } 00290 00291 if (!writeHeader()) { 00292 retOK = false; 00293 } 00294 00295 for (unsigned int i = 0; i < m_numberOfTracks; i++) { 00296 if (!writeTrack(i)) { 00297 retOK = false; 00298 } 00299 } 00300 00301 m_midiFile->close(); 00302 delete m_midiFile; 00303 m_midiFile = 0; 00304 00305 if (!retOK) { 00306 m_error = "MIDI file write failed"; 00307 } 00308 00309 return retOK; 00310 } 00311 00312 bool 00313 MIDIFileWriter::convert() 00314 { 00315 m_timingDivision = 480; 00316 m_format = MIDI_SINGLE_TRACK_FILE; 00317 m_numberOfTracks = 1; 00318 00319 int track = 0; 00320 int midiChannel = 0; 00321 00322 MIDIEvent *event; 00323 00324 event = new MIDIEvent(0, MIDI_FILE_META_EVENT, MIDI_CUE_POINT, 00325 "Exported from Sonic Visualiser"); 00326 m_midiComposition[track].push_back(event); 00327 00328 event = new MIDIEvent(0, MIDI_FILE_META_EVENT, MIDI_CUE_POINT, 00329 "http://www.sonicvisualiser.org/"); 00330 m_midiComposition[track].push_back(event); 00331 00332 long tempoValue = long(60000000.0 / m_tempo + 0.01); 00333 string tempoString; 00334 tempoString += (MIDIByte)(tempoValue >> 16 & 0xFF); 00335 tempoString += (MIDIByte)(tempoValue >> 8 & 0xFF); 00336 tempoString += (MIDIByte)(tempoValue & 0xFF); 00337 00338 event = new MIDIEvent(0, MIDI_FILE_META_EVENT, MIDI_SET_TEMPO, 00339 tempoString); 00340 m_midiComposition[track].push_back(event); 00341 00342 // Omit time signature 00343 00344 NoteList notes = m_exportable->getNotes(); 00345 00346 for (NoteList::const_iterator i = notes.begin(); i != notes.end(); ++i) { 00347 00348 int frame = i->start; 00349 int duration = i->duration; 00350 int pitch = i->midiPitch; 00351 int velocity = i->velocity; 00352 00353 if (pitch < 0) pitch = 0; 00354 if (pitch > 127) pitch = 127; 00355 00356 // Convert frame to MIDI time 00357 00358 double seconds = double(frame) / double(m_sampleRate); 00359 double quarters = (seconds * m_tempo) / 60.0; 00360 unsigned long midiTime = int(quarters * m_timingDivision + 0.5); 00361 00362 // Get the sounding time for the matching NOTE_OFF 00363 seconds = double(frame + duration) / double(m_sampleRate); 00364 quarters = (seconds * m_tempo) / 60.0; 00365 unsigned long endTime = int(quarters * m_timingDivision + 0.5); 00366 00367 // At this point all the notes we insert have absolute times 00368 // in the delta time fields. We resolve these into delta 00369 // times further down (can't do it until all the note offs are 00370 // in place). 00371 00372 event = new MIDIEvent(midiTime, 00373 MIDI_NOTE_ON | midiChannel, 00374 pitch, 00375 velocity); 00376 m_midiComposition[track].push_back(event); 00377 00378 event = new MIDIEvent(endTime, 00379 MIDI_NOTE_OFF | midiChannel, 00380 pitch, 00381 127); // loudest silence you can muster 00382 00383 m_midiComposition[track].push_back(event); 00384 } 00385 00386 // Now gnash through the MIDI events and turn the absolute times 00387 // into delta times. 00388 // 00389 for (unsigned int i = 0; i < m_numberOfTracks; i++) { 00390 00391 unsigned long lastMidiTime = 0; 00392 00393 // First sort the track with the MIDIEvent comparator. Use 00394 // stable_sort so that events with equal times are maintained 00395 // in their current order. 00396 // 00397 std::stable_sort(m_midiComposition[i].begin(), 00398 m_midiComposition[i].end(), 00399 MIDIEventCmp()); 00400 00401 for (MIDITrack::iterator it = m_midiComposition[i].begin(); 00402 it != m_midiComposition[i].end(); it++) { 00403 unsigned long deltaTime = (*it)->getTime() - lastMidiTime; 00404 lastMidiTime = (*it)->getTime(); 00405 (*it)->setTime(deltaTime); 00406 } 00407 00408 // Insert end of track event (delta time = 0) 00409 // 00410 event = new MIDIEvent(0, MIDI_FILE_META_EVENT, 00411 MIDI_END_OF_TRACK, ""); 00412 00413 m_midiComposition[i].push_back(event); 00414 } 00415 00416 return true; 00417 } 00418