MIDIFileWriter.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2 of the
11  License, or (at your option) any later version. See the file
12  COPYING included with this distribution for more information.
13 */
14 
15 
16 /*
17  This is a modified version of a source file from the
18  Rosegarden MIDI and audio sequencer and notation editor.
19  This file copyright 2000-2007 Richard Bown and Chris Cannam
20  and copyright 2007 QMUL.
21 */
22 
23 #include "MIDIFileWriter.h"
24 
25 #include "data/midi/MIDIEvent.h"
26 
27 #include "base/NoteData.h"
28 #include "base/NoteExportable.h"
29 #include "base/Pitch.h"
30 
31 #include <QCoreApplication>
32 
33 #include <algorithm>
34 #include <fstream>
35 
36 //#define DEBUG_MIDI_FILE_WRITER 1
37 
38 using std::ofstream;
39 using std::string;
40 using std::ios;
41 
42 using namespace MIDIConstants;
43 
44 MIDIFileWriter::MIDIFileWriter(QString path, const NoteExportable *exportable,
45  sv_samplerate_t sampleRate, float tempo) :
46  m_path(path),
47  m_exportable(exportable),
48  m_sampleRate(sampleRate),
49  m_tempo(tempo),
50  m_midiFile(nullptr)
51 {
52  if (!convert()) {
53  m_error = "Conversion from model to internal MIDI format failed";
54  }
55 }
56 
58 {
59  for (MIDIComposition::iterator i = m_midiComposition.begin();
60  i != m_midiComposition.end(); ++i) {
61 
62  for (MIDITrack::iterator j = i->second.begin();
63  j != i->second.end(); ++j) {
64  delete *j;
65  }
66 
67  i->second.clear();
68  }
69 
70  m_midiComposition.clear();
71 }
72 
73 bool
75 {
76  return m_error == "";
77 }
78 
79 QString
81 {
82  return m_error;
83 }
84 
85 void
87 {
89 }
90 
91 string
93 {
94  MIDIByte upper;
95  MIDIByte lower;
96 
97  upper = MIDIByte((number & 0xFF00) >> 8);
98  lower = MIDIByte( number & 0x00FF);
99 
100  string rv;
101  rv += upper;
102  rv += lower;
103  return rv;
104 }
105 
106 string
107 MIDIFileWriter::longToMIDIBytes(unsigned long number) const
108 {
109  MIDIByte upper1;
110  MIDIByte lower1;
111  MIDIByte upper2;
112  MIDIByte lower2;
113 
114  upper1 = MIDIByte((number & 0xff000000) >> 24);
115  lower1 = MIDIByte((number & 0x00ff0000) >> 16);
116  upper2 = MIDIByte((number & 0x0000ff00) >> 8);
117  lower2 = MIDIByte((number & 0x000000ff));
118 
119  string rv;
120  rv += upper1;
121  rv += lower1;
122  rv += upper2;
123  rv += lower2;
124  return rv;
125 }
126 
127 // Turn a delta time into a MIDI time - overlapping into
128 // a maximum of four bytes using the MSB as the carry on
129 // flag.
130 //
131 string
132 MIDIFileWriter::longToVarBuffer(unsigned long number) const
133 {
134  string rv;
135 
136  long inNumber = number;
137  long outNumber;
138 
139  // get the lowest 7 bits of the number
140  outNumber = number & 0x7f;
141 
142  // Shift and test and move the numbers
143  // on if we need them - setting the MSB
144  // as we go.
145  //
146  while ((inNumber >>= 7 ) > 0) {
147  outNumber <<= 8;
148  outNumber |= 0x80;
149  outNumber += (inNumber & 0x7f);
150  }
151 
152  // Now move the converted number out onto the buffer
153  //
154  while (true) {
155  rv += (MIDIByte)(outNumber & 0xff);
156  if (outNumber & 0x80)
157  outNumber >>= 8;
158  else
159  break;
160  }
161 
162  return rv;
163 }
164 
165 bool
167 {
169 
170  // Number of bytes in header
171  *m_midiFile << (MIDIByte) 0x00;
172  *m_midiFile << (MIDIByte) 0x00;
173  *m_midiFile << (MIDIByte) 0x00;
174  *m_midiFile << (MIDIByte) 0x06;
175 
176  // File format
177  *m_midiFile << (MIDIByte) 0x00;
179 
181 
183 
184  return true;
185 }
186 
187 bool
189 {
190  bool retOK = true;
191  MIDIByte eventCode = 0;
192  MIDITrack::iterator midiEvent;
193 
194  // First we write into the trackBuffer, then write it out to the
195  // file with its accompanying length.
196  //
197  string trackBuffer;
198 
199  for (midiEvent = m_midiComposition[trackNumber].begin();
200  midiEvent != m_midiComposition[trackNumber].end();
201  midiEvent++) {
202 
203  // Write the time to the buffer in MIDI format
204  trackBuffer += longToVarBuffer((*midiEvent)->getTime());
205 
206  if ((*midiEvent)->isMeta()) {
207  trackBuffer += MIDI_FILE_META_EVENT;
208  trackBuffer += (*midiEvent)->getMetaEventCode();
209 
210  // Variable length number field
211  trackBuffer += longToVarBuffer((*midiEvent)->
212  getMetaMessage().length());
213 
214  trackBuffer += (*midiEvent)->getMetaMessage();
215  } else {
216  // Send the normal event code (with encoded channel information)
217  if (((*midiEvent)->getEventCode() != eventCode) ||
218  ((*midiEvent)->getEventCode() == MIDI_SYSTEM_EXCLUSIVE)) {
219  trackBuffer += (*midiEvent)->getEventCode();
220  eventCode = (*midiEvent)->getEventCode();
221  }
222 
223  // Send the relevant data
224  //
225  switch ((*midiEvent)->getMessageType()) {
226  case MIDI_NOTE_ON:
227  case MIDI_NOTE_OFF:
229  trackBuffer += (*midiEvent)->getData1();
230  trackBuffer += (*midiEvent)->getData2();
231  break;
232 
233  case MIDI_CTRL_CHANGE:
234  trackBuffer += (*midiEvent)->getData1();
235  trackBuffer += (*midiEvent)->getData2();
236  break;
237 
238  case MIDI_PROG_CHANGE:
239  trackBuffer += (*midiEvent)->getData1();
240  break;
241 
243  trackBuffer += (*midiEvent)->getData1();
244  break;
245 
246  case MIDI_PITCH_BEND:
247  trackBuffer += (*midiEvent)->getData1();
248  trackBuffer += (*midiEvent)->getData2();
249  break;
250 
252  // write out message length
253  trackBuffer +=
254  longToVarBuffer((*midiEvent)->getMetaMessage().length());
255 
256  // now the message
257  trackBuffer += (*midiEvent)->getMetaMessage();
258  break;
259 
260  default:
261  break;
262  }
263  }
264  }
265 
266  // Now we write the track - First the standard header..
267  //
269 
270  // ..now the length of the buffer..
271  //
272  *m_midiFile << longToMIDIBytes((long)trackBuffer.length());
273 
274  // ..then the buffer itself..
275  //
276  *m_midiFile << trackBuffer;
277 
278  return retOK;
279 }
280 
281 bool
283 {
284  bool retOK = true;
285 
286  m_midiFile =
287  new ofstream(m_path.toLocal8Bit().data(), ios::out | ios::binary);
288 
289  if (!(*m_midiFile)) {
290  m_error = "Can't open file for writing.";
291  delete m_midiFile;
292  m_midiFile = nullptr;
293  return false;
294  }
295 
296  if (!writeHeader()) {
297  retOK = false;
298  }
299 
300  for (unsigned int i = 0; i < m_numberOfTracks; i++) {
301  if (!writeTrack(i)) {
302  retOK = false;
303  }
304  }
305 
306  m_midiFile->close();
307  delete m_midiFile;
308  m_midiFile = nullptr;
309 
310  if (!retOK) {
311  m_error = "MIDI file write failed";
312  }
313 
314  return retOK;
315 }
316 
317 bool
319 {
320  m_timingDivision = 480;
322  m_numberOfTracks = 1;
323 
324  int track = 0;
325 
326  MIDIEvent *event;
327 
328  event = new MIDIEvent
330  ("Exported from " + qApp->applicationName()).toStdString());
331  m_midiComposition[track].push_back(event);
332 
333  long tempoValue = long(60000000.0 / m_tempo + 0.01);
334  string tempoString;
335  tempoString += (MIDIByte)(tempoValue >> 16 & 0xFF);
336  tempoString += (MIDIByte)(tempoValue >> 8 & 0xFF);
337  tempoString += (MIDIByte)(tempoValue & 0xFF);
338 
340  tempoString);
341  m_midiComposition[track].push_back(event);
342 
343  // Omit time signature
344 
346 
347  for (NoteList::const_iterator i = notes.begin(); i != notes.end(); ++i) {
348 
349  sv_frame_t frame = i->start;
350  sv_frame_t duration = i->duration;
351  int pitch = i->midiPitch;
352  int velocity = i->velocity;
353  int channel = i->channel;
354 
355  if (pitch < 0) pitch = 0;
356  if (pitch > 127) pitch = 127;
357 
358  if (channel < 0) channel = 0;
359  if (channel > 15) channel = 0;
360 
361  // Convert frame to MIDI time
362 
363  double seconds = double(frame) / m_sampleRate;
364  double quarters = (seconds * m_tempo) / 60.0;
365  unsigned long midiTime = int(quarters * m_timingDivision + 0.5);
366 
367  // Get the sounding time for the matching NOTE_OFF
368  seconds = double(frame + duration) / m_sampleRate;
369  quarters = (seconds * m_tempo) / 60.0;
370  unsigned long endTime = int(quarters * m_timingDivision + 0.5);
371 
372  // At this point all the notes we insert have absolute times
373  // in the delta time fields. We resolve these into delta
374  // times further down (can't do it until all the note offs are
375  // in place).
376 
377  event = new MIDIEvent(midiTime,
378  MIDI_NOTE_ON | channel,
379  pitch,
380  velocity);
381  m_midiComposition[track].push_back(event);
382 
383  event = new MIDIEvent(endTime,
384  MIDI_NOTE_OFF | channel,
385  pitch,
386  127); // loudest silence you can muster
387 
388  m_midiComposition[track].push_back(event);
389 
390 #ifdef DEBUG_MIDI_FILE_WRITER
391  cerr << "midiTime = " << midiTime << ", endTime = " << endTime << endl;
392 #endif
393  }
394 
395  // Now gnash through the MIDI events and turn the absolute times
396  // into delta times.
397  //
398  for (unsigned int i = 0; i < m_numberOfTracks; i++) {
399 
400  unsigned long lastMidiTime = 0;
401 
402  // First sort the track with the MIDIEvent comparator. Use
403  // stable_sort so that events with equal times are maintained
404  // in their current order.
405  //
406  std::stable_sort(m_midiComposition[i].begin(),
407  m_midiComposition[i].end(),
408  MIDIEventCmp());
409 
410  for (MIDITrack::iterator it = m_midiComposition[i].begin();
411  it != m_midiComposition[i].end(); it++) {
412  unsigned long deltaTime = (*it)->getTime() - lastMidiTime;
413 #ifdef DEBUG_MIDI_FILE_WRITER
414  cerr << "time = " << (*it)->getTime() << ", lastMidiTime = " << lastMidiTime << ", deltaTime = " << deltaTime << endl;
415 #endif
416  lastMidiTime = (*it)->getTime();
417  (*it)->setTime(deltaTime);
418  }
419 
420  // Insert end of track event (delta time = 0)
421  //
422  event = new MIDIEvent(0, MIDI_FILE_META_EVENT,
423  MIDI_END_OF_TRACK, "");
424 
425  m_midiComposition[i].push_back(event);
426  }
427 
428  return true;
429 }
430 
double sv_samplerate_t
Sample rate.
Definition: BaseTypes.h:51
unsigned int m_numberOfTracks
static const MIDIByte MIDI_END_OF_TRACK
Definition: MIDIEvent.h:90
int64_t sv_frame_t
Frame index, the unit of our time axis.
Definition: BaseTypes.h:31
MIDIComposition m_midiComposition
static const char *const MIDI_TRACK_HEADER
Definition: MIDIEvent.h:37
bool writeTrack(int track)
std::string longToVarBuffer(unsigned long number) const
std::string longToMIDIBytes(unsigned long number) const
std::string intToMIDIBytes(int number) const
static const MIDIByte MIDI_CUE_POINT
Definition: MIDIEvent.h:87
static const MIDIByte MIDI_FILE_META_EVENT
Definition: MIDIEvent.h:79
static const MIDIByte MIDI_SET_TEMPO
Definition: MIDIEvent.h:91
std::ofstream * m_midiFile
MIDIFileFormatType m_format
static QString notes[]
Definition: Pitch.cpp:91
static const MIDIByte MIDI_PITCH_BEND
Definition: MIDIEvent.h:49
bool isOK() const
virtual NoteList getNotes() const =0
Get all notes in the exportable object.
const NoteExportable * m_exportable
std::vector< NoteData > NoteList
Definition: NoteData.h:48
MIDIFileWriter(QString path, const NoteExportable *exportable, sv_samplerate_t sampleRate, float tempo=120.f)
static const MIDIByte MIDI_CTRL_CHANGE
Definition: MIDIEvent.h:46
static const MIDIByte MIDI_POLY_AFTERTOUCH
Definition: MIDIEvent.h:45
static const MIDIByte MIDI_SYSTEM_EXCLUSIVE
Definition: MIDIEvent.h:51
static const MIDIByte MIDI_NOTE_OFF
Definition: MIDIEvent.h:43
QString getError() const
static const char *const MIDI_FILE_HEADER
Definition: MIDIEvent.h:36
static const MIDIByte MIDI_PROG_CHANGE
Definition: MIDIEvent.h:47
static const MIDIByte MIDI_NOTE_ON
Definition: MIDIEvent.h:44
unsigned char MIDIByte
sv_samplerate_t m_sampleRate
static const MIDIByte MIDI_CHNL_AFTERTOUCH
Definition: MIDIEvent.h:48