MIDIFileWriter.cpp

Go to the documentation of this file.
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 "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 // Turn a delta time into a MIDI time - overlapping into
00124 // a maximum of four bytes using the MSB as the carry on
00125 // flag.
00126 //
00127 string
00128 MIDIFileWriter::longToVarBuffer(unsigned long number) const
00129 {
00130     string rv;
00131 
00132     long inNumber = number;
00133     long outNumber;
00134 
00135     // get the lowest 7 bits of the number
00136     outNumber = number & 0x7f;
00137 
00138     // Shift and test and move the numbers
00139     // on if we need them - setting the MSB
00140     // as we go.
00141     //
00142     while ((inNumber >>= 7 ) > 0) {
00143         outNumber <<= 8;
00144         outNumber |= 0x80;
00145         outNumber += (inNumber & 0x7f);
00146     }
00147 
00148     // Now move the converted number out onto the buffer
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     // Number of bytes in header
00167     *m_midiFile << (MIDIByte) 0x00;
00168     *m_midiFile << (MIDIByte) 0x00;
00169     *m_midiFile << (MIDIByte) 0x00;
00170     *m_midiFile << (MIDIByte) 0x06;
00171 
00172     // File format
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     // First we write into the trackBuffer, then write it out to the
00191     // file with its accompanying length.
00192     //
00193     string trackBuffer;
00194 
00195     for (midiEvent = m_midiComposition[trackNumber].begin();
00196          midiEvent != m_midiComposition[trackNumber].end();
00197          midiEvent++) {
00198 
00199         // Write the time to the buffer in MIDI format
00200         trackBuffer += longToVarBuffer((*midiEvent)->getTime());
00201 
00202         if ((*midiEvent)->isMeta()) {
00203             trackBuffer += MIDI_FILE_META_EVENT;
00204             trackBuffer += (*midiEvent)->getMetaEventCode();
00205 
00206             // Variable length number field
00207             trackBuffer += longToVarBuffer((*midiEvent)->
00208                                            getMetaMessage().length());
00209 
00210             trackBuffer += (*midiEvent)->getMetaMessage();
00211         } else {
00212             // Send the normal event code (with encoded channel information)
00213             if (((*midiEvent)->getEventCode() != eventCode) ||
00214                 ((*midiEvent)->getEventCode() == MIDI_SYSTEM_EXCLUSIVE)) {
00215                 trackBuffer += (*midiEvent)->getEventCode();
00216                 eventCode = (*midiEvent)->getEventCode();
00217             }
00218 
00219             // Send the relevant data
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                 // write out message length
00249                 trackBuffer +=
00250                     longToVarBuffer((*midiEvent)->getMetaMessage().length());
00251 
00252                 // now the message
00253                 trackBuffer += (*midiEvent)->getMetaMessage();
00254                 break;
00255 
00256             default:
00257                 break;
00258             }
00259         }
00260     }
00261 
00262     // Now we write the track - First the standard header..
00263     //
00264     *m_midiFile << MIDI_TRACK_HEADER;
00265 
00266     // ..now the length of the buffer..
00267     //
00268     *m_midiFile << longToMIDIBytes((long)trackBuffer.length());
00269 
00270     // ..then the buffer itself..
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     // Omit time signature
00344 
00345     const NoteModel::PointList &notes =
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         // Convert frame to MIDI time
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         // Get the sounding time for the matching NOTE_OFF
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         // At this point all the notes we insert have absolute times
00383         // in the delta time fields.  We resolve these into delta
00384         // times further down (can't do it until all the note offs are
00385         // in place).
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); // loudest silence you can muster
00397 
00398         m_midiComposition[track].push_back(event);
00399     }
00400     
00401     // Now gnash through the MIDI events and turn the absolute times
00402     // into delta times.
00403     //
00404     for (unsigned int i = 0; i < m_numberOfTracks; i++) {
00405 
00406         unsigned long lastMidiTime = 0;
00407 
00408         // First sort the track with the MIDIEvent comparator.  Use
00409         // stable_sort so that events with equal times are maintained
00410         // in their current order.
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         // Insert end of track event (delta time = 0)
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 

Generated on Wed Feb 20 15:45:26 2008 for SonicVisualiser by  doxygen 1.5.1