svcore  1.9
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 #include "model/NoteData.h"
27 
28 #include "base/Pitch.h"
29 
30 #include <algorithm>
31 #include <fstream>
32 
33 using std::ofstream;
34 using std::string;
35 using std::ios;
36 
37 using namespace MIDIConstants;
38 
39 MIDIFileWriter::MIDIFileWriter(QString path, const NoteExportable *exportable,
40  int sampleRate, float tempo) :
41  m_path(path),
42  m_exportable(exportable),
43  m_sampleRate(sampleRate),
44  m_tempo(tempo),
45  m_midiFile(0)
46 {
47  if (!convert()) {
48  m_error = "Conversion from model to internal MIDI format failed";
49  }
50 }
51 
53 {
54  for (MIDIComposition::iterator i = m_midiComposition.begin();
55  i != m_midiComposition.end(); ++i) {
56 
57  for (MIDITrack::iterator j = i->second.begin();
58  j != i->second.end(); ++j) {
59  delete *j;
60  }
61 
62  i->second.clear();
63  }
64 
65  m_midiComposition.clear();
66 }
67 
68 bool
70 {
71  return m_error == "";
72 }
73 
74 QString
76 {
77  return m_error;
78 }
79 
80 void
82 {
84 }
85 
86 string
88 {
89  MIDIByte upper;
90  MIDIByte lower;
91 
92  upper = (number & 0xFF00) >> 8;
93  lower = (number & 0x00FF);
94 
95  string rv;
96  rv += upper;
97  rv += lower;
98  return rv;
99 }
100 
101 string
102 MIDIFileWriter::longToMIDIBytes(unsigned long number) const
103 {
104  MIDIByte upper1;
105  MIDIByte lower1;
106  MIDIByte upper2;
107  MIDIByte lower2;
108 
109  upper1 = (number & 0xff000000) >> 24;
110  lower1 = (number & 0x00ff0000) >> 16;
111  upper2 = (number & 0x0000ff00) >> 8;
112  lower2 = (number & 0x000000ff);
113 
114  string rv;
115  rv += upper1;
116  rv += lower1;
117  rv += upper2;
118  rv += lower2;
119  return rv;
120 }
121 
122 // Turn a delta time into a MIDI time - overlapping into
123 // a maximum of four bytes using the MSB as the carry on
124 // flag.
125 //
126 string
127 MIDIFileWriter::longToVarBuffer(unsigned long number) const
128 {
129  string rv;
130 
131  long inNumber = number;
132  long outNumber;
133 
134  // get the lowest 7 bits of the number
135  outNumber = number & 0x7f;
136 
137  // Shift and test and move the numbers
138  // on if we need them - setting the MSB
139  // as we go.
140  //
141  while ((inNumber >>= 7 ) > 0) {
142  outNumber <<= 8;
143  outNumber |= 0x80;
144  outNumber += (inNumber & 0x7f);
145  }
146 
147  // Now move the converted number out onto the buffer
148  //
149  while (true) {
150  rv += (MIDIByte)(outNumber & 0xff);
151  if (outNumber & 0x80)
152  outNumber >>= 8;
153  else
154  break;
155  }
156 
157  return rv;
158 }
159 
160 bool
162 {
164 
165  // Number of bytes in header
166  *m_midiFile << (MIDIByte) 0x00;
167  *m_midiFile << (MIDIByte) 0x00;
168  *m_midiFile << (MIDIByte) 0x00;
169  *m_midiFile << (MIDIByte) 0x06;
170 
171  // File format
172  *m_midiFile << (MIDIByte) 0x00;
174 
176 
178 
179  return true;
180 }
181 
182 bool
184 {
185  bool retOK = true;
186  MIDIByte eventCode = 0;
187  MIDITrack::iterator midiEvent;
188 
189  // First we write into the trackBuffer, then write it out to the
190  // file with its accompanying length.
191  //
192  string trackBuffer;
193 
194  for (midiEvent = m_midiComposition[trackNumber].begin();
195  midiEvent != m_midiComposition[trackNumber].end();
196  midiEvent++) {
197 
198  // Write the time to the buffer in MIDI format
199  trackBuffer += longToVarBuffer((*midiEvent)->getTime());
200 
201  if ((*midiEvent)->isMeta()) {
202  trackBuffer += MIDI_FILE_META_EVENT;
203  trackBuffer += (*midiEvent)->getMetaEventCode();
204 
205  // Variable length number field
206  trackBuffer += longToVarBuffer((*midiEvent)->
207  getMetaMessage().length());
208 
209  trackBuffer += (*midiEvent)->getMetaMessage();
210  } else {
211  // Send the normal event code (with encoded channel information)
212  if (((*midiEvent)->getEventCode() != eventCode) ||
213  ((*midiEvent)->getEventCode() == MIDI_SYSTEM_EXCLUSIVE)) {
214  trackBuffer += (*midiEvent)->getEventCode();
215  eventCode = (*midiEvent)->getEventCode();
216  }
217 
218  // Send the relevant data
219  //
220  switch ((*midiEvent)->getMessageType()) {
221  case MIDI_NOTE_ON:
222  case MIDI_NOTE_OFF:
224  trackBuffer += (*midiEvent)->getData1();
225  trackBuffer += (*midiEvent)->getData2();
226  break;
227 
228  case MIDI_CTRL_CHANGE:
229  trackBuffer += (*midiEvent)->getData1();
230  trackBuffer += (*midiEvent)->getData2();
231  break;
232 
233  case MIDI_PROG_CHANGE:
234  trackBuffer += (*midiEvent)->getData1();
235  break;
236 
238  trackBuffer += (*midiEvent)->getData1();
239  break;
240 
241  case MIDI_PITCH_BEND:
242  trackBuffer += (*midiEvent)->getData1();
243  trackBuffer += (*midiEvent)->getData2();
244  break;
245 
247  // write out message length
248  trackBuffer +=
249  longToVarBuffer((*midiEvent)->getMetaMessage().length());
250 
251  // now the message
252  trackBuffer += (*midiEvent)->getMetaMessage();
253  break;
254 
255  default:
256  break;
257  }
258  }
259  }
260 
261  // Now we write the track - First the standard header..
262  //
264 
265  // ..now the length of the buffer..
266  //
267  *m_midiFile << longToMIDIBytes((long)trackBuffer.length());
268 
269  // ..then the buffer itself..
270  //
271  *m_midiFile << trackBuffer;
272 
273  return retOK;
274 }
275 
276 bool
278 {
279  bool retOK = true;
280 
281  m_midiFile =
282  new ofstream(m_path.toLocal8Bit().data(), ios::out | ios::binary);
283 
284  if (!(*m_midiFile)) {
285  m_error = "Can't open file for writing.";
286  delete m_midiFile;
287  m_midiFile = 0;
288  return false;
289  }
290 
291  if (!writeHeader()) {
292  retOK = false;
293  }
294 
295  for (unsigned int i = 0; i < m_numberOfTracks; i++) {
296  if (!writeTrack(i)) {
297  retOK = false;
298  }
299  }
300 
301  m_midiFile->close();
302  delete m_midiFile;
303  m_midiFile = 0;
304 
305  if (!retOK) {
306  m_error = "MIDI file write failed";
307  }
308 
309  return retOK;
310 }
311 
312 bool
314 {
315  m_timingDivision = 480;
317  m_numberOfTracks = 1;
318 
319  int track = 0;
320  int midiChannel = 0;
321 
322  MIDIEvent *event;
323 
325  "Exported from Sonic Visualiser");
326  m_midiComposition[track].push_back(event);
327 
329  "http://www.sonicvisualiser.org/");
330  m_midiComposition[track].push_back(event);
331 
332  long tempoValue = long(60000000.0 / m_tempo + 0.01);
333  string tempoString;
334  tempoString += (MIDIByte)(tempoValue >> 16 & 0xFF);
335  tempoString += (MIDIByte)(tempoValue >> 8 & 0xFF);
336  tempoString += (MIDIByte)(tempoValue & 0xFF);
337 
339  tempoString);
340  m_midiComposition[track].push_back(event);
341 
342  // Omit time signature
343 
345 
346  for (NoteList::const_iterator i = notes.begin(); i != notes.end(); ++i) {
347 
348  int frame = i->start;
349  int duration = i->duration;
350  int pitch = i->midiPitch;
351  int velocity = i->velocity;
352 
353  if (pitch < 0) pitch = 0;
354  if (pitch > 127) pitch = 127;
355 
356  // Convert frame to MIDI time
357 
358  double seconds = double(frame) / double(m_sampleRate);
359  double quarters = (seconds * m_tempo) / 60.0;
360  unsigned long midiTime = int(quarters * m_timingDivision + 0.5);
361 
362  // Get the sounding time for the matching NOTE_OFF
363  seconds = double(frame + duration) / double(m_sampleRate);
364  quarters = (seconds * m_tempo) / 60.0;
365  unsigned long endTime = int(quarters * m_timingDivision + 0.5);
366 
367  // At this point all the notes we insert have absolute times
368  // in the delta time fields. We resolve these into delta
369  // times further down (can't do it until all the note offs are
370  // in place).
371 
372  event = new MIDIEvent(midiTime,
373  MIDI_NOTE_ON | midiChannel,
374  pitch,
375  velocity);
376  m_midiComposition[track].push_back(event);
377 
378  event = new MIDIEvent(endTime,
379  MIDI_NOTE_OFF | midiChannel,
380  pitch,
381  127); // loudest silence you can muster
382 
383  m_midiComposition[track].push_back(event);
384  }
385 
386  // Now gnash through the MIDI events and turn the absolute times
387  // into delta times.
388  //
389  for (unsigned int i = 0; i < m_numberOfTracks; i++) {
390 
391  unsigned long lastMidiTime = 0;
392 
393  // First sort the track with the MIDIEvent comparator. Use
394  // stable_sort so that events with equal times are maintained
395  // in their current order.
396  //
397  std::stable_sort(m_midiComposition[i].begin(),
398  m_midiComposition[i].end(),
399  MIDIEventCmp());
400 
401  for (MIDITrack::iterator it = m_midiComposition[i].begin();
402  it != m_midiComposition[i].end(); it++) {
403  unsigned long deltaTime = (*it)->getTime() - lastMidiTime;
404  lastMidiTime = (*it)->getTime();
405  (*it)->setTime(deltaTime);
406  }
407 
408  // Insert end of track event (delta time = 0)
409  //
410  event = new MIDIEvent(0, MIDI_FILE_META_EVENT,
411  MIDI_END_OF_TRACK, "");
412 
413  m_midiComposition[i].push_back(event);
414  }
415 
416  return true;
417 }
418 
std::string longToVarBuffer(unsigned long number) const
std::string longToMIDIBytes(unsigned long number) const
unsigned int m_numberOfTracks
static const MIDIByte MIDI_END_OF_TRACK
Definition: MIDIEvent.h:88
MIDIFileWriter(QString path, const NoteExportable *exportable, int sampleRate, float tempo=120.f)
MIDIComposition m_midiComposition
virtual void write()
static const char *const MIDI_TRACK_HEADER
Definition: MIDIEvent.h:35
bool writeTrack(int track)
static const MIDIByte MIDI_CUE_POINT
Definition: MIDIEvent.h:85
static const MIDIByte MIDI_FILE_META_EVENT
Definition: MIDIEvent.h:77
static const MIDIByte MIDI_SET_TEMPO
Definition: MIDIEvent.h:89
std::ofstream * m_midiFile
MIDIFileFormatType m_format
std::string intToMIDIBytes(int number) const
static QString notes[]
Definition: Pitch.cpp:87
static const MIDIByte MIDI_PITCH_BEND
Definition: MIDIEvent.h:47
virtual NoteList getNotes() const =0
virtual QString getError() const
const NoteExportable * m_exportable
std::vector< NoteData > NoteList
Definition: NoteData.h:44
virtual ~MIDIFileWriter()
static const MIDIByte MIDI_CTRL_CHANGE
Definition: MIDIEvent.h:44
static const MIDIByte MIDI_POLY_AFTERTOUCH
Definition: MIDIEvent.h:43
static const MIDIByte MIDI_SYSTEM_EXCLUSIVE
Definition: MIDIEvent.h:49
static const MIDIByte MIDI_NOTE_OFF
Definition: MIDIEvent.h:41
static const char *const MIDI_FILE_HEADER
Definition: MIDIEvent.h:34
static const MIDIByte MIDI_PROG_CHANGE
Definition: MIDIEvent.h:45
virtual bool isOK() const
static const MIDIByte MIDI_NOTE_ON
Definition: MIDIEvent.h:42
unsigned char MIDIByte
static const MIDIByte MIDI_CHNL_AFTERTOUCH
Definition: MIDIEvent.h:46