andrew@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
andrew@0
|
2
|
andrew@0
|
3 /*
|
andrew@0
|
4 This is a modified version of a source file from the
|
andrew@0
|
5 Rosegarden MIDI and audio sequencer and notation editor.
|
andrew@0
|
6 This file copyright 2000-2010 Richard Bown and Chris Cannam.
|
andrew@0
|
7
|
andrew@0
|
8 Permission is hereby granted, free of charge, to any person
|
andrew@0
|
9 obtaining a copy of this software and associated documentation
|
andrew@0
|
10 files (the "Software"), to deal in the Software without
|
andrew@0
|
11 restriction, including without limitation the rights to use, copy,
|
andrew@0
|
12 modify, merge, publish, distribute, sublicense, and/or sell copies
|
andrew@0
|
13 of the Software, and to permit persons to whom the Software is
|
andrew@0
|
14 furnished to do so, subject to the following conditions:
|
andrew@0
|
15
|
andrew@0
|
16 The above copyright notice and this permission notice shall be
|
andrew@0
|
17 included in all copies or substantial portions of the Software.
|
andrew@0
|
18
|
andrew@0
|
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
andrew@0
|
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
andrew@0
|
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
andrew@0
|
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
andrew@0
|
23 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
andrew@0
|
24 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
andrew@0
|
25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
andrew@0
|
26
|
andrew@0
|
27 Except as contained in this notice, the names of the authors
|
andrew@0
|
28 shall not be used in advertising or otherwise to promote the sale,
|
andrew@0
|
29 use or other dealings in this Software without prior written
|
andrew@0
|
30 authorization.
|
andrew@0
|
31 */
|
andrew@0
|
32
|
andrew@0
|
33
|
andrew@0
|
34 #include <iostream>
|
andrew@0
|
35 #include <fstream>
|
andrew@0
|
36 #include <string>
|
andrew@0
|
37 #include <cstdio>
|
andrew@0
|
38
|
andrew@0
|
39 #include "MIDIFileReader.h"
|
andrew@0
|
40 #include "MIDIEvent.h"
|
andrew@0
|
41
|
andrew@0
|
42 #include <sstream>
|
andrew@0
|
43
|
andrew@0
|
44 using std::string;
|
andrew@0
|
45 using std::ifstream;
|
andrew@0
|
46 using std::stringstream;
|
andrew@0
|
47 using std::cerr;
|
andrew@0
|
48 using std::endl;
|
andrew@0
|
49 using std::ends;
|
andrew@0
|
50 using std::ios;
|
andrew@0
|
51
|
andrew@0
|
52 using namespace MIDIConstants;
|
andrew@0
|
53
|
andrew@0
|
54 //#define DEBUG_MIDI_FILE_READER 1
|
andrew@0
|
55
|
andrew@0
|
56 #define throw_exception(...) do { \
|
andrew@0
|
57 char message[128]; \
|
andrew@0
|
58 snprintf(message, 128, __VA_ARGS__); \
|
andrew@0
|
59 throw MIDIException(std::string(message)); \
|
andrew@0
|
60 } while (0)
|
andrew@0
|
61
|
andrew@0
|
62
|
andrew@0
|
63
|
andrew@0
|
64 MIDIFileReader::MIDIFileReader(std::string path) :
|
andrew@0
|
65 m_timingDivision(0),
|
andrew@0
|
66 m_format(MIDI_FILE_BAD_FORMAT),
|
andrew@0
|
67 m_numberOfTracks(0),
|
andrew@0
|
68 m_trackByteCount(0),
|
andrew@0
|
69 m_decrementCount(false),
|
andrew@0
|
70 m_path(path),
|
andrew@0
|
71 m_midiFile(0),
|
andrew@0
|
72 m_fileSize(0)
|
andrew@0
|
73 {
|
andrew@0
|
74 if (parseFile()) {
|
andrew@0
|
75 m_error = "";
|
andrew@0
|
76 }
|
andrew@0
|
77 }
|
andrew@0
|
78
|
andrew@0
|
79 MIDIFileReader::~MIDIFileReader()
|
andrew@0
|
80 {
|
andrew@0
|
81 }
|
andrew@0
|
82
|
andrew@0
|
83 bool
|
andrew@0
|
84 MIDIFileReader::isOK() const
|
andrew@0
|
85 {
|
andrew@0
|
86 return (m_error == "");
|
andrew@0
|
87 }
|
andrew@0
|
88
|
andrew@0
|
89 std::string
|
andrew@0
|
90 MIDIFileReader::getError() const
|
andrew@0
|
91 {
|
andrew@0
|
92 return m_error;
|
andrew@0
|
93 }
|
andrew@0
|
94
|
andrew@0
|
95 long
|
andrew@0
|
96 MIDIFileReader::midiBytesToLong(const string& bytes)
|
andrew@0
|
97 {
|
andrew@0
|
98 if (bytes.length() != 4) {
|
andrew@0
|
99 throw_exception("Wrong length for long data in MIDI stream (%d, should be %d)", (int)bytes.length(), 4);
|
andrew@0
|
100 }
|
andrew@0
|
101
|
andrew@0
|
102 long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) |
|
andrew@0
|
103 ((long)(((MIDIByte)bytes[1]) << 16)) |
|
andrew@0
|
104 ((long)(((MIDIByte)bytes[2]) << 8)) |
|
andrew@0
|
105 ((long)((MIDIByte)(bytes[3])));
|
andrew@0
|
106
|
andrew@0
|
107 return longRet;
|
andrew@0
|
108 }
|
andrew@0
|
109
|
andrew@0
|
110 int
|
andrew@0
|
111 MIDIFileReader::midiBytesToInt(const string& bytes)
|
andrew@0
|
112 {
|
andrew@0
|
113 if (bytes.length() != 2) {
|
andrew@0
|
114 throw_exception("Wrong length for int data in MIDI stream (%d, should be %d)", (int)bytes.length(), 2);
|
andrew@0
|
115 }
|
andrew@0
|
116
|
andrew@0
|
117 int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) |
|
andrew@0
|
118 ((int)(((MIDIByte)bytes[1])));
|
andrew@0
|
119 return(intRet);
|
andrew@0
|
120 }
|
andrew@0
|
121
|
andrew@0
|
122
|
andrew@0
|
123 // Gets a single byte from the MIDI byte stream. For each track
|
andrew@0
|
124 // section we can read only a specified number of bytes held in
|
andrew@0
|
125 // m_trackByteCount.
|
andrew@0
|
126 //
|
andrew@0
|
127 MIDIByte
|
andrew@0
|
128 MIDIFileReader::getMIDIByte()
|
andrew@0
|
129 {
|
andrew@0
|
130 if (!m_midiFile) {
|
andrew@0
|
131 throw_exception("getMIDIByte called but no MIDI file open");
|
andrew@0
|
132 }
|
andrew@0
|
133
|
andrew@0
|
134 if (m_midiFile->eof()) {
|
andrew@0
|
135 throw_exception("End of MIDI file encountered while reading");
|
andrew@0
|
136 }
|
andrew@0
|
137
|
andrew@0
|
138 if (m_decrementCount && m_trackByteCount <= 0) {
|
andrew@0
|
139 throw_exception("Attempt to get more bytes than expected on Track");
|
andrew@0
|
140 }
|
andrew@0
|
141
|
andrew@0
|
142 char byte;
|
andrew@0
|
143 if (m_midiFile->read(&byte, 1)) {
|
andrew@0
|
144 --m_trackByteCount;
|
andrew@0
|
145 return (MIDIByte)byte;
|
andrew@0
|
146 }
|
andrew@0
|
147
|
andrew@0
|
148 throw_exception("Attempt to read past MIDI file end");
|
andrew@0
|
149 }
|
andrew@0
|
150
|
andrew@0
|
151
|
andrew@0
|
152 // Gets a specified number of bytes from the MIDI byte stream. For
|
andrew@0
|
153 // each track section we can read only a specified number of bytes
|
andrew@0
|
154 // held in m_trackByteCount.
|
andrew@0
|
155 //
|
andrew@0
|
156 string
|
andrew@0
|
157 MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes)
|
andrew@0
|
158 {
|
andrew@0
|
159 if (!m_midiFile) {
|
andrew@0
|
160 throw_exception("getMIDIBytes called but no MIDI file open");
|
andrew@0
|
161 }
|
andrew@0
|
162
|
andrew@0
|
163 if (m_midiFile->eof()) {
|
andrew@0
|
164 throw_exception("End of MIDI file encountered while reading");
|
andrew@0
|
165 }
|
andrew@0
|
166
|
andrew@0
|
167 if (m_decrementCount && (numberOfBytes > (unsigned long)m_trackByteCount)) {
|
andrew@0
|
168 throw_exception("Attempt to get more bytes than available on Track (%lu, only have %ld)", numberOfBytes, m_trackByteCount);
|
andrew@0
|
169 }
|
andrew@0
|
170
|
andrew@0
|
171 string stringRet;
|
andrew@0
|
172 char fileMIDIByte;
|
andrew@0
|
173
|
andrew@0
|
174 while (stringRet.length() < numberOfBytes &&
|
andrew@0
|
175 m_midiFile->read(&fileMIDIByte, 1)) {
|
andrew@0
|
176 stringRet += fileMIDIByte;
|
andrew@0
|
177 }
|
andrew@0
|
178
|
andrew@0
|
179 // if we've reached the end of file without fulfilling the
|
andrew@0
|
180 // quota then panic as our parsing has performed incorrectly
|
andrew@0
|
181 //
|
andrew@0
|
182 if (stringRet.length() < numberOfBytes) {
|
andrew@0
|
183 stringRet = "";
|
andrew@0
|
184 throw_exception("Attempt to read past MIDI file end");
|
andrew@0
|
185 }
|
andrew@0
|
186
|
andrew@0
|
187 // decrement the byte count
|
andrew@0
|
188 if (m_decrementCount)
|
andrew@0
|
189 m_trackByteCount -= stringRet.length();
|
andrew@0
|
190
|
andrew@0
|
191 return stringRet;
|
andrew@0
|
192 }
|
andrew@0
|
193
|
andrew@0
|
194
|
andrew@0
|
195 // Get a long number of variable length from the MIDI byte stream.
|
andrew@0
|
196 //
|
andrew@0
|
197 long
|
andrew@0
|
198 MIDIFileReader::getNumberFromMIDIBytes(int firstByte)
|
andrew@0
|
199 {
|
andrew@0
|
200 if (!m_midiFile) {
|
andrew@0
|
201 throw_exception("getNumberFromMIDIBytes called but no MIDI file open");
|
andrew@0
|
202 }
|
andrew@0
|
203
|
andrew@0
|
204 long longRet = 0;
|
andrew@0
|
205 MIDIByte midiByte;
|
andrew@0
|
206
|
andrew@0
|
207 if (firstByte >= 0) {
|
andrew@0
|
208 midiByte = (MIDIByte)firstByte;
|
andrew@0
|
209 } else if (m_midiFile->eof()) {
|
andrew@0
|
210 return longRet;
|
andrew@0
|
211 } else {
|
andrew@0
|
212 midiByte = getMIDIByte();
|
andrew@0
|
213 }
|
andrew@0
|
214
|
andrew@0
|
215 longRet = midiByte;
|
andrew@0
|
216 if (midiByte & 0x80) {
|
andrew@0
|
217 longRet &= 0x7F;
|
andrew@0
|
218 do {
|
andrew@0
|
219 midiByte = getMIDIByte();
|
andrew@0
|
220 longRet = (longRet << 7) + (midiByte & 0x7F);
|
andrew@0
|
221 } while (!m_midiFile->eof() && (midiByte & 0x80));
|
andrew@0
|
222 }
|
andrew@0
|
223
|
andrew@0
|
224 return longRet;
|
andrew@0
|
225 }
|
andrew@0
|
226
|
andrew@0
|
227
|
andrew@0
|
228 // Seek to the next track in the midi file and set the number
|
andrew@0
|
229 // of bytes to be read in the counter m_trackByteCount.
|
andrew@0
|
230 //
|
andrew@0
|
231 bool
|
andrew@0
|
232 MIDIFileReader::skipToNextTrack()
|
andrew@0
|
233 {
|
andrew@0
|
234 if (!m_midiFile) {
|
andrew@0
|
235 throw_exception("skipToNextTrack called but no MIDI file open");
|
andrew@0
|
236 }
|
andrew@0
|
237
|
andrew@0
|
238 string buffer, buffer2;
|
andrew@0
|
239 m_trackByteCount = -1;
|
andrew@0
|
240 m_decrementCount = false;
|
andrew@0
|
241
|
andrew@0
|
242 while (!m_midiFile->eof() && (m_decrementCount == false)) {
|
andrew@0
|
243 buffer = getMIDIBytes(4);
|
andrew@0
|
244 if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) {
|
andrew@0
|
245 m_trackByteCount = midiBytesToLong(getMIDIBytes(4));
|
andrew@0
|
246 m_decrementCount = true;
|
andrew@0
|
247 }
|
andrew@0
|
248 }
|
andrew@0
|
249
|
andrew@0
|
250 if (m_trackByteCount == -1) { // we haven't found a track
|
andrew@0
|
251 return false;
|
andrew@0
|
252 } else {
|
andrew@0
|
253 return true;
|
andrew@0
|
254 }
|
andrew@0
|
255 }
|
andrew@0
|
256
|
andrew@0
|
257
|
andrew@0
|
258 // Read in a MIDI file. The parsing process throws exceptions back up
|
andrew@0
|
259 // here if we run into trouble which we can then pass back out to
|
andrew@0
|
260 // whoever called us using a nice bool.
|
andrew@0
|
261 //
|
andrew@0
|
262 bool
|
andrew@0
|
263 MIDIFileReader::parseFile()
|
andrew@0
|
264 {
|
andrew@0
|
265 m_error = "";
|
andrew@0
|
266
|
andrew@0
|
267 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
268 cerr << "MIDIFileReader::open() : fileName = " << m_path.toStdString() << endl;
|
andrew@0
|
269 #endif
|
andrew@0
|
270
|
andrew@0
|
271 // Open the file
|
andrew@0
|
272 m_midiFile = new ifstream(m_path.c_str(), ios::in | ios::binary);
|
andrew@0
|
273
|
andrew@0
|
274 if (!*m_midiFile) {
|
andrew@0
|
275 m_error = "File not found or not readable.";
|
andrew@0
|
276 m_format = MIDI_FILE_BAD_FORMAT;
|
andrew@0
|
277 delete m_midiFile;
|
andrew@0
|
278 m_midiFile = 0;
|
andrew@0
|
279 return false;
|
andrew@0
|
280 }
|
andrew@0
|
281
|
andrew@0
|
282 bool retval = false;
|
andrew@0
|
283
|
andrew@0
|
284 try {
|
andrew@0
|
285
|
andrew@0
|
286 // Set file size so we can count it off
|
andrew@0
|
287 //
|
andrew@0
|
288 m_midiFile->seekg(0, ios::end);
|
andrew@0
|
289 m_fileSize = m_midiFile->tellg();
|
andrew@0
|
290 m_midiFile->seekg(0, ios::beg);
|
andrew@0
|
291
|
andrew@0
|
292 // Parse the MIDI header first. The first 14 bytes of the file.
|
andrew@0
|
293 if (!parseHeader(getMIDIBytes(14))) {
|
andrew@0
|
294 m_format = MIDI_FILE_BAD_FORMAT;
|
andrew@0
|
295 m_error = "Not a MIDI file.";
|
andrew@0
|
296 goto done;
|
andrew@0
|
297 }
|
andrew@0
|
298
|
andrew@0
|
299 for (unsigned int j = 0; j < m_numberOfTracks; ++j) {
|
andrew@0
|
300
|
andrew@0
|
301 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
302 cerr << "Parsing Track " << j << endl;
|
andrew@0
|
303 #endif
|
andrew@0
|
304
|
andrew@0
|
305 if (!skipToNextTrack()) {
|
andrew@0
|
306 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
307 cerr << "Couldn't find Track " << j << endl;
|
andrew@0
|
308 #endif
|
andrew@0
|
309 m_error = "File corrupted or in non-standard format?";
|
andrew@0
|
310 m_format = MIDI_FILE_BAD_FORMAT;
|
andrew@0
|
311 goto done;
|
andrew@0
|
312 }
|
andrew@0
|
313
|
andrew@0
|
314 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
315 cerr << "Track has " << m_trackByteCount << " bytes" << endl;
|
andrew@0
|
316 #endif
|
andrew@0
|
317
|
andrew@0
|
318 // Run through the events taking them into our internal
|
andrew@0
|
319 // representation.
|
andrew@0
|
320 if (!parseTrack(j)) {
|
andrew@0
|
321 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
322 cerr << "Track " << j << " parsing failed" << endl;
|
andrew@0
|
323 #endif
|
andrew@0
|
324 m_error = "File corrupted or in non-standard format?";
|
andrew@0
|
325 m_format = MIDI_FILE_BAD_FORMAT;
|
andrew@0
|
326 goto done;
|
andrew@0
|
327 }
|
andrew@0
|
328 }
|
andrew@0
|
329
|
andrew@0
|
330 retval = true;
|
andrew@0
|
331
|
andrew@0
|
332 } catch (MIDIException e) {
|
andrew@0
|
333
|
andrew@0
|
334 cerr << "MIDIFileReader::open() - caught exception - " << e.what() << endl;
|
andrew@0
|
335 m_error = e.what();
|
andrew@0
|
336 }
|
andrew@0
|
337
|
andrew@0
|
338 done:
|
andrew@0
|
339 m_midiFile->close();
|
andrew@0
|
340 delete m_midiFile;
|
andrew@0
|
341
|
andrew@0
|
342 for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
|
andrew@0
|
343
|
andrew@0
|
344 // Convert the deltaTime to an absolute time since the track
|
andrew@0
|
345 // start. The addTime method returns the sum of the current
|
andrew@0
|
346 // MIDI Event delta time plus the argument.
|
andrew@0
|
347
|
andrew@0
|
348 unsigned long acc = 0;
|
andrew@0
|
349
|
andrew@0
|
350 for (MIDITrack::iterator i = m_midiComposition[track].begin();
|
andrew@0
|
351 i != m_midiComposition[track].end(); ++i) {
|
andrew@0
|
352 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
353 cerr << "converting delta time " << i->getTime();
|
andrew@0
|
354 #endif
|
andrew@0
|
355 acc = i->addTime(acc);
|
andrew@0
|
356 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
357 cerr << " to " << i->getTime() << endl;
|
andrew@0
|
358 #endif
|
andrew@0
|
359 }
|
andrew@0
|
360
|
andrew@0
|
361 consolidateNoteOffEvents(track);
|
andrew@0
|
362 }
|
andrew@0
|
363
|
andrew@0
|
364 return retval;
|
andrew@0
|
365 }
|
andrew@0
|
366
|
andrew@0
|
367 // Parse and ensure the MIDI Header is legitimate
|
andrew@0
|
368 //
|
andrew@0
|
369 bool
|
andrew@0
|
370 MIDIFileReader::parseHeader(const string &midiHeader)
|
andrew@0
|
371 {
|
andrew@0
|
372 if (midiHeader.size() < 14) {
|
andrew@0
|
373 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
374 cerr << "MIDIFileReader::parseHeader() - file header undersized" << endl;
|
andrew@0
|
375 #endif
|
andrew@0
|
376 return false;
|
andrew@0
|
377 }
|
andrew@0
|
378
|
andrew@0
|
379 if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) {
|
andrew@0
|
380 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
381 cerr << "MIDIFileReader::parseHeader()"
|
andrew@0
|
382 << "- file header not found or malformed"
|
andrew@0
|
383 << endl;
|
andrew@0
|
384 #endif
|
andrew@0
|
385 return false;
|
andrew@0
|
386 }
|
andrew@0
|
387
|
andrew@0
|
388 if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) {
|
andrew@0
|
389 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
390 cerr << "MIDIFileReader::parseHeader()"
|
andrew@0
|
391 << " - header length incorrect"
|
andrew@0
|
392 << endl;
|
andrew@0
|
393 #endif
|
andrew@0
|
394 return false;
|
andrew@0
|
395 }
|
andrew@0
|
396
|
andrew@0
|
397 m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2));
|
andrew@0
|
398 m_numberOfTracks = midiBytesToInt(midiHeader.substr(10,2));
|
andrew@0
|
399 m_timingDivision = midiBytesToInt(midiHeader.substr(12,2));
|
andrew@0
|
400
|
andrew@0
|
401 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
402 if (m_timingDivision < 0) {
|
andrew@0
|
403 cerr << "MIDIFileReader::parseHeader()"
|
andrew@0
|
404 << " - file uses SMPTE timing"
|
andrew@0
|
405 << endl;
|
andrew@0
|
406 }
|
andrew@0
|
407 #endif
|
andrew@0
|
408
|
andrew@0
|
409 return true;
|
andrew@0
|
410 }
|
andrew@0
|
411
|
andrew@0
|
412 // Extract the contents from a MIDI file track and places it into
|
andrew@0
|
413 // our local map of MIDI events.
|
andrew@0
|
414 //
|
andrew@0
|
415 bool
|
andrew@0
|
416 MIDIFileReader::parseTrack(unsigned int trackNum)
|
andrew@0
|
417 {
|
andrew@0
|
418 MIDIByte midiByte, metaEventCode, data1, data2;
|
andrew@0
|
419 MIDIByte eventCode = 0x80;
|
andrew@0
|
420 string metaMessage;
|
andrew@0
|
421 unsigned int messageLength;
|
andrew@0
|
422 unsigned long deltaTime;
|
andrew@0
|
423 unsigned long accumulatedTime = 0;
|
andrew@0
|
424
|
andrew@0
|
425 // Remember the last non-meta status byte (-1 if we haven't seen one)
|
andrew@0
|
426 int runningStatus = -1;
|
andrew@0
|
427
|
andrew@0
|
428 while (!m_midiFile->eof() && (m_trackByteCount > 0)) {
|
andrew@0
|
429
|
andrew@0
|
430 if (eventCode < 0x80) {
|
andrew@0
|
431 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
432 cerr << "WARNING: Invalid event code " << eventCode
|
andrew@0
|
433 << " in MIDI file" << endl;
|
andrew@0
|
434 #endif
|
andrew@0
|
435 throw_exception("Invalid event code %d found", int(eventCode));
|
andrew@0
|
436 }
|
andrew@0
|
437
|
andrew@0
|
438 deltaTime = getNumberFromMIDIBytes();
|
andrew@0
|
439
|
andrew@0
|
440 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
441 cerr << "read delta time " << deltaTime << endl;
|
andrew@0
|
442 #endif
|
andrew@0
|
443
|
andrew@0
|
444 // Get a single byte
|
andrew@0
|
445 midiByte = getMIDIByte();
|
andrew@0
|
446
|
andrew@0
|
447 if (!(midiByte & MIDI_STATUS_BYTE_MASK)) {
|
andrew@0
|
448
|
andrew@0
|
449 if (runningStatus < 0) {
|
andrew@0
|
450 throw_exception("Running status used for first event in track");
|
andrew@0
|
451 }
|
andrew@0
|
452
|
andrew@0
|
453 eventCode = (MIDIByte)runningStatus;
|
andrew@0
|
454 data1 = midiByte;
|
andrew@0
|
455
|
andrew@0
|
456 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
457 cerr << "using running status (byte " << int(midiByte) << " found)" << endl;
|
andrew@0
|
458 #endif
|
andrew@0
|
459 } else {
|
andrew@0
|
460 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
461 cerr << "have new event code " << int(midiByte) << endl;
|
andrew@0
|
462 #endif
|
andrew@0
|
463 eventCode = midiByte;
|
andrew@0
|
464 data1 = getMIDIByte();
|
andrew@0
|
465 }
|
andrew@0
|
466
|
andrew@0
|
467 if (eventCode == MIDI_FILE_META_EVENT) {
|
andrew@0
|
468
|
andrew@0
|
469 metaEventCode = data1;
|
andrew@0
|
470 messageLength = getNumberFromMIDIBytes();
|
andrew@0
|
471
|
andrew@0
|
472 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
473 cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl;
|
andrew@0
|
474 #endif
|
andrew@0
|
475 metaMessage = getMIDIBytes(messageLength);
|
andrew@0
|
476
|
andrew@0
|
477 accumulatedTime += deltaTime;
|
andrew@0
|
478
|
andrew@0
|
479 MIDIEvent e(deltaTime,
|
andrew@0
|
480 MIDI_FILE_META_EVENT,
|
andrew@0
|
481 metaEventCode,
|
andrew@0
|
482 metaMessage);
|
andrew@0
|
483
|
andrew@0
|
484 m_midiComposition[trackNum].push_back(e);
|
andrew@0
|
485
|
andrew@0
|
486 if (metaEventCode == MIDI_TRACK_NAME) {
|
andrew@0
|
487 m_trackNames[trackNum] = metaMessage.c_str();
|
andrew@0
|
488 }
|
andrew@0
|
489
|
andrew@0
|
490 } else { // non-meta events
|
andrew@0
|
491
|
andrew@0
|
492 runningStatus = eventCode;
|
andrew@0
|
493
|
andrew@0
|
494 int channel = (eventCode & MIDI_CHANNEL_NUM_MASK);
|
andrew@0
|
495
|
andrew@0
|
496 accumulatedTime += deltaTime;
|
andrew@0
|
497
|
andrew@0
|
498 switch (eventCode & MIDI_MESSAGE_TYPE_MASK) {
|
andrew@0
|
499
|
andrew@0
|
500 case MIDI_NOTE_ON:
|
andrew@0
|
501 case MIDI_NOTE_OFF:
|
andrew@0
|
502 case MIDI_POLY_AFTERTOUCH:
|
andrew@0
|
503 case MIDI_CTRL_CHANGE:
|
andrew@0
|
504 data2 = getMIDIByte();
|
andrew@0
|
505
|
andrew@0
|
506 {
|
andrew@0
|
507 // create and store our event
|
andrew@0
|
508 MIDIEvent midiEvent(deltaTime, eventCode, data1, data2);
|
andrew@0
|
509
|
andrew@0
|
510 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
511 cerr << "MIDI event for channel " << channel << " (track "
|
andrew@0
|
512 << trackNum << ") with delta time " << deltaTime << endl;
|
andrew@0
|
513 #endif
|
andrew@0
|
514
|
andrew@0
|
515 m_midiComposition[trackNum].push_back(midiEvent);
|
andrew@0
|
516 }
|
andrew@0
|
517 break;
|
andrew@0
|
518
|
andrew@0
|
519 case MIDI_PITCH_BEND:
|
andrew@0
|
520 data2 = getMIDIByte();
|
andrew@0
|
521
|
andrew@0
|
522 {
|
andrew@0
|
523 // create and store our event
|
andrew@0
|
524 MIDIEvent midiEvent(deltaTime, eventCode, data1, data2);
|
andrew@0
|
525 m_midiComposition[trackNum].push_back(midiEvent);
|
andrew@0
|
526 }
|
andrew@0
|
527 break;
|
andrew@0
|
528
|
andrew@0
|
529 case MIDI_PROG_CHANGE:
|
andrew@0
|
530 case MIDI_CHNL_AFTERTOUCH:
|
andrew@0
|
531
|
andrew@0
|
532 {
|
andrew@0
|
533 // create and store our event
|
andrew@0
|
534 MIDIEvent midiEvent(deltaTime, eventCode, data1);
|
andrew@0
|
535 m_midiComposition[trackNum].push_back(midiEvent);
|
andrew@0
|
536 }
|
andrew@0
|
537 break;
|
andrew@0
|
538
|
andrew@0
|
539 case MIDI_SYSTEM_EXCLUSIVE:
|
andrew@0
|
540 messageLength = getNumberFromMIDIBytes(data1);
|
andrew@0
|
541
|
andrew@0
|
542 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
543 cerr << "SysEx of " << messageLength << " bytes found" << endl;
|
andrew@0
|
544 #endif
|
andrew@0
|
545
|
andrew@0
|
546 metaMessage= getMIDIBytes(messageLength);
|
andrew@0
|
547
|
andrew@0
|
548 if (MIDIByte(metaMessage[metaMessage.length() - 1]) !=
|
andrew@0
|
549 MIDI_END_OF_EXCLUSIVE)
|
andrew@0
|
550 {
|
andrew@0
|
551 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
552 cerr << "MIDIFileReader::parseTrack() - "
|
andrew@0
|
553 << "malformed or unsupported SysEx type"
|
andrew@0
|
554 << endl;
|
andrew@0
|
555 #endif
|
andrew@0
|
556 continue;
|
andrew@0
|
557 }
|
andrew@0
|
558
|
andrew@0
|
559 // chop off the EOX
|
andrew@0
|
560 // length fixed by Pedro Lopez-Cabanillas (20030523)
|
andrew@0
|
561 //
|
andrew@0
|
562 metaMessage = metaMessage.substr(0, metaMessage.length()-1);
|
andrew@0
|
563
|
andrew@0
|
564 {
|
andrew@0
|
565 MIDIEvent midiEvent(deltaTime,
|
andrew@0
|
566 MIDI_SYSTEM_EXCLUSIVE,
|
andrew@0
|
567 metaMessage);
|
andrew@0
|
568 m_midiComposition[trackNum].push_back(midiEvent);
|
andrew@0
|
569 }
|
andrew@0
|
570 break;
|
andrew@0
|
571
|
andrew@0
|
572 case MIDI_END_OF_EXCLUSIVE:
|
andrew@0
|
573 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
574 cerr << "MIDIFileReader::parseTrack() - "
|
andrew@0
|
575 << "Found a stray MIDI_END_OF_EXCLUSIVE" << endl;
|
andrew@0
|
576 #endif
|
andrew@0
|
577 break;
|
andrew@0
|
578
|
andrew@0
|
579 default:
|
andrew@0
|
580 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
581 cerr << "MIDIFileReader::parseTrack()"
|
andrew@0
|
582 << " - Unsupported MIDI Event Code: "
|
andrew@0
|
583 << (int)eventCode << endl;
|
andrew@0
|
584 #endif
|
andrew@0
|
585 break;
|
andrew@0
|
586 }
|
andrew@0
|
587 }
|
andrew@0
|
588 }
|
andrew@0
|
589
|
andrew@0
|
590 return true;
|
andrew@0
|
591 }
|
andrew@0
|
592
|
andrew@0
|
593 // Delete dead NOTE OFF and NOTE ON/Zero Velocity Events after
|
andrew@0
|
594 // reading them and modifying their relevant NOTE ONs. Return true
|
andrew@0
|
595 // if there are some notes in this track.
|
andrew@0
|
596 //
|
andrew@0
|
597 bool
|
andrew@0
|
598 MIDIFileReader::consolidateNoteOffEvents(unsigned int track)
|
andrew@0
|
599 {
|
andrew@0
|
600 bool notesOnTrack = false;
|
andrew@0
|
601 bool noteOffFound;
|
andrew@0
|
602
|
andrew@0
|
603 MIDITrack &t = m_midiComposition[track];
|
andrew@0
|
604
|
andrew@0
|
605 for (MIDITrack::iterator i = t.begin(); i != t.end(); ++i) {
|
andrew@0
|
606
|
andrew@0
|
607 if (i->getMessageType() == MIDI_NOTE_ON && i->getVelocity() > 0) {
|
andrew@0
|
608
|
andrew@0
|
609 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
610 cerr << "Looking for note-offs for note at " << i->getTime() << " (pitch " << (int)i->getPitch() << ")" << endl;
|
andrew@0
|
611 #endif
|
andrew@0
|
612
|
andrew@0
|
613 notesOnTrack = true;
|
andrew@0
|
614 noteOffFound = false;
|
andrew@0
|
615
|
andrew@0
|
616 for (MIDITrack::iterator j = i; j != t.end(); ++j) {
|
andrew@0
|
617
|
andrew@0
|
618 if ((j->getChannelNumber() == i->getChannelNumber()) &&
|
andrew@0
|
619 (j->getPitch() == i->getPitch()) &&
|
andrew@0
|
620 (j->getMessageType() == MIDI_NOTE_OFF ||
|
andrew@0
|
621 (j->getMessageType() == MIDI_NOTE_ON &&
|
andrew@0
|
622 j->getVelocity() == 0x00))) {
|
andrew@0
|
623
|
andrew@0
|
624 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
625 cerr << "Found note-off at " << j->getTime() << " for note at " << i->getTime() << endl;
|
andrew@0
|
626 #endif
|
andrew@0
|
627
|
andrew@0
|
628 i->setDuration(j->getTime() - i->getTime());
|
andrew@0
|
629
|
andrew@0
|
630 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
631 cerr << "Duration is now " << i->getDuration() << endl;
|
andrew@0
|
632 #endif
|
andrew@0
|
633
|
andrew@0
|
634 t.erase(j);
|
andrew@0
|
635
|
andrew@0
|
636 noteOffFound = true;
|
andrew@0
|
637 break;
|
andrew@0
|
638 }
|
andrew@0
|
639 }
|
andrew@0
|
640
|
andrew@0
|
641 // If no matching NOTE OFF has been found then set
|
andrew@0
|
642 // Event duration to length of track
|
andrew@0
|
643 //
|
andrew@0
|
644 if (!noteOffFound) {
|
andrew@0
|
645 #ifdef DEBUG_MIDI_FILE_READER
|
andrew@0
|
646 cerr << "Failed to find note-off for note at " << i->getTime() << endl;
|
andrew@0
|
647 #endif
|
andrew@0
|
648 MIDITrack::iterator j = t.end();
|
andrew@0
|
649 --j;
|
andrew@0
|
650 i->setDuration(j->getTime() - i->getTime());
|
andrew@0
|
651 }
|
andrew@0
|
652 }
|
andrew@0
|
653 }
|
andrew@0
|
654
|
andrew@0
|
655 return notesOnTrack;
|
andrew@0
|
656 }
|
andrew@0
|
657
|
andrew@0
|
658 MIDIComposition
|
andrew@0
|
659 MIDIFileReader::load() const
|
andrew@0
|
660 {
|
andrew@0
|
661 return m_midiComposition;
|
andrew@0
|
662 }
|
andrew@0
|
663
|
andrew@0
|
664
|