To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Revision:

root / MIDIFileReader.cpp @ 7:b9a2f08e2c62

History | View | Annotate | Download (17.5 KB)

1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2

    
3
/*
4
    This is a modified version of a source file from the 
5
    Rosegarden MIDI and audio sequencer and notation editor.
6
    This file copyright 2000-2010 Richard Bown and Chris Cannam.
7
  
8
    Permission is hereby granted, free of charge, to any person
9
    obtaining a copy of this software and associated documentation
10
    files (the "Software"), to deal in the Software without
11
    restriction, including without limitation the rights to use, copy,
12
    modify, merge, publish, distribute, sublicense, and/or sell copies
13
    of the Software, and to permit persons to whom the Software is
14
    furnished to do so, subject to the following conditions:
15

16
    The above copyright notice and this permission notice shall be
17
    included in all copies or substantial portions of the Software.
18

19
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
23
    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24
    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26

27
    Except as contained in this notice, the names of the authors
28
    shall not be used in advertising or otherwise to promote the sale,
29
    use or other dealings in this Software without prior written
30
    authorization.
31
*/
32

    
33

    
34
#include <iostream>
35
#include <fstream>
36
#include <string>
37
#include <cstdio>
38

    
39
#include "MIDIFileReader.h"
40
#include "MIDIEvent.h"
41

    
42
#include <sstream>
43

    
44
using std::string;
45
using std::ifstream;
46
using std::stringstream;
47
using std::cerr;
48
using std::endl;
49
using std::ends;
50
using std::ios;
51

    
52
using namespace MIDIConstants;
53

    
54
//#define DEBUG_MIDI_FILE_READER 1
55

    
56
#define throw_exception(...) do { \
57
        char message[128]; \
58
        snprintf(message, 128, __VA_ARGS__); \
59
        throw MIDIException(std::string(message)); \
60
    } while (0)
61
    
62

    
63

    
64
MIDIFileReader::MIDIFileReader(std::string path) :
65
    m_timingDivision(0),
66
    m_format(MIDI_FILE_BAD_FORMAT),
67
    m_numberOfTracks(0),
68
    m_trackByteCount(0),
69
    m_decrementCount(false),
70
    m_path(path),
71
    m_midiFile(0),
72
    m_fileSize(0)
73
{
74
    if (parseFile()) {
75
        m_error = "";
76
    }
77
}
78

    
79
MIDIFileReader::~MIDIFileReader()
80
{
81
}
82

    
83
bool
84
MIDIFileReader::isOK() const
85
{
86
    return (m_error == "");
87
}
88

    
89
std::string
90
MIDIFileReader::getError() const
91
{
92
    return m_error;
93
}
94

    
95
long
96
MIDIFileReader::midiBytesToLong(const string& bytes)
97
{
98
    if (bytes.length() != 4) {
99
        throw_exception("Wrong length for long data in MIDI stream (%d, should be %d)", (int)bytes.length(), 4);
100
    }
101

    
102
    long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) |
103
                   ((long)(((MIDIByte)bytes[1]) << 16)) |
104
                   ((long)(((MIDIByte)bytes[2]) << 8)) |
105
                   ((long)((MIDIByte)(bytes[3])));
106

    
107
    return longRet;
108
}
109

    
110
int
111
MIDIFileReader::midiBytesToInt(const string& bytes)
112
{
113
    if (bytes.length() != 2) {
114
        throw_exception("Wrong length for int data in MIDI stream (%d, should be %d)", (int)bytes.length(), 2);
115
    }
116

    
117
    int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) |
118
                 ((int)(((MIDIByte)bytes[1])));
119
    return(intRet);
120
}
121

    
122

    
123
// Gets a single byte from the MIDI byte stream.  For each track
124
// section we can read only a specified number of bytes held in
125
// m_trackByteCount.
126
//
127
MIDIByte
128
MIDIFileReader::getMIDIByte()
129
{
130
    if (!m_midiFile) {
131
        throw_exception("getMIDIByte called but no MIDI file open");
132
    }
133

    
134
    if (m_midiFile->eof()) {
135
        throw_exception("End of MIDI file encountered while reading");
136
    }
137

    
138
    if (m_decrementCount && m_trackByteCount <= 0) {
139
        throw_exception("Attempt to get more bytes than expected on Track");
140
    }
141

    
142
    char byte;
143
    if (m_midiFile->read(&byte, 1)) {
144
        --m_trackByteCount;
145
        return (MIDIByte)byte;
146
    }
147

    
148
    throw_exception("Attempt to read past MIDI file end");
149
}
150

    
151

    
152
// Gets a specified number of bytes from the MIDI byte stream.  For
153
// each track section we can read only a specified number of bytes
154
// held in m_trackByteCount.
155
//
156
string
157
MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes)
158
{
159
    if (!m_midiFile) {
160
        throw_exception("getMIDIBytes called but no MIDI file open");
161
    }
162

    
163
    if (m_midiFile->eof()) {
164
        throw_exception("End of MIDI file encountered while reading");
165
    }
166

    
167
    if (m_decrementCount && (numberOfBytes > (unsigned long)m_trackByteCount)) {
168
        throw_exception("Attempt to get more bytes than available on Track (%lu, only have %ld)", numberOfBytes, m_trackByteCount);
169
    }
170

    
171
    string stringRet;
172
    char fileMIDIByte;
173

    
174
    while (stringRet.length() < numberOfBytes &&
175
           m_midiFile->read(&fileMIDIByte, 1)) {
176
        stringRet += fileMIDIByte;
177
    }
178

    
179
    // if we've reached the end of file without fulfilling the
180
    // quota then panic as our parsing has performed incorrectly
181
    //
182
    if (stringRet.length() < numberOfBytes) {
183
        stringRet = "";
184
        throw_exception("Attempt to read past MIDI file end");
185
    }
186

    
187
    // decrement the byte count
188
    if (m_decrementCount)
189
        m_trackByteCount -= stringRet.length();
190

    
191
    return stringRet;
192
}
193

    
194

    
195
// Get a long number of variable length from the MIDI byte stream.
196
//
197
long
198
MIDIFileReader::getNumberFromMIDIBytes(int firstByte)
199
{
200
    if (!m_midiFile) {
201
        throw_exception("getNumberFromMIDIBytes called but no MIDI file open");
202
    }
203

    
204
    long longRet = 0;
205
    MIDIByte midiByte;
206

    
207
    if (firstByte >= 0) {
208
        midiByte = (MIDIByte)firstByte;
209
    } else if (m_midiFile->eof()) {
210
        return longRet;
211
    } else {
212
        midiByte = getMIDIByte();
213
    }
214

    
215
    longRet = midiByte;
216
    if (midiByte & 0x80) {
217
        longRet &= 0x7F;
218
        do {
219
            midiByte = getMIDIByte();
220
            longRet = (longRet << 7) + (midiByte & 0x7F);
221
        } while (!m_midiFile->eof() && (midiByte & 0x80));
222
    }
223

    
224
    return longRet;
225
}
226

    
227

    
228
// Seek to the next track in the midi file and set the number
229
// of bytes to be read in the counter m_trackByteCount.
230
//
231
bool
232
MIDIFileReader::skipToNextTrack()
233
{
234
    if (!m_midiFile) {
235
        throw_exception("skipToNextTrack called but no MIDI file open");
236
    }
237

    
238
    string buffer, buffer2;
239
    m_trackByteCount = -1;
240
    m_decrementCount = false;
241

    
242
    while (!m_midiFile->eof() && (m_decrementCount == false)) {
243
        buffer = getMIDIBytes(4); 
244
        if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) {
245
            m_trackByteCount = midiBytesToLong(getMIDIBytes(4));
246
            m_decrementCount = true;
247
        }
248
    }
249

    
250
    if (m_trackByteCount == -1) { // we haven't found a track
251
        return false;
252
    } else {
253
        return true;
254
    }
255
}
256

    
257

    
258
// Read in a MIDI file.  The parsing process throws exceptions back up
259
// here if we run into trouble which we can then pass back out to
260
// whoever called us using a nice bool.
261
//
262
bool
263
MIDIFileReader::parseFile()
264
{
265
    m_error = "";
266

    
267
#ifdef DEBUG_MIDI_FILE_READER
268
    cerr << "MIDIFileReader::open() : fileName = " << m_path << endl;
269
#endif
270

    
271
    // Open the file
272
    m_midiFile = new ifstream(m_path.c_str(), ios::in | ios::binary);
273

    
274
    if (!*m_midiFile) {
275
        m_error = "File not found or not readable.";
276
        m_format = MIDI_FILE_BAD_FORMAT;
277
        delete m_midiFile;
278
        m_midiFile = 0;
279
        return false;
280
    }
281

    
282
    bool retval = false;
283

    
284
    try {
285

    
286
        // Set file size so we can count it off
287
        //
288
        m_midiFile->seekg(0, ios::end);
289
        m_fileSize = m_midiFile->tellg();
290
        m_midiFile->seekg(0, ios::beg);
291

    
292
        // Parse the MIDI header first.  The first 14 bytes of the file.
293
        if (!parseHeader(getMIDIBytes(14))) {
294
            m_format = MIDI_FILE_BAD_FORMAT;
295
            m_error = "Not a MIDI file.";
296
            goto done;
297
        }
298

    
299
        for (unsigned int j = 0; j < m_numberOfTracks; ++j) {
300

    
301
#ifdef DEBUG_MIDI_FILE_READER
302
            cerr << "Parsing Track " << j << endl;
303
#endif
304

    
305
            if (!skipToNextTrack()) {
306
#ifdef DEBUG_MIDI_FILE_READER
307
                cerr << "Couldn't find Track " << j << endl;
308
#endif
309
                m_error = "File corrupted or in non-standard format?";
310
                m_format = MIDI_FILE_BAD_FORMAT;
311
                goto done;
312
            }
313

    
314
#ifdef DEBUG_MIDI_FILE_READER
315
            cerr << "Track has " << m_trackByteCount << " bytes" << endl;
316
#endif
317

    
318
            // Run through the events taking them into our internal
319
            // representation.
320
            if (!parseTrack(j)) {
321
#ifdef DEBUG_MIDI_FILE_READER
322
                cerr << "Track " << j << " parsing failed" << endl;
323
#endif
324
                m_error = "File corrupted or in non-standard format?";
325
                m_format = MIDI_FILE_BAD_FORMAT;
326
                goto done;
327
            }
328
        }
329
        
330
        retval = true;
331

    
332
    } catch (MIDIException e) {
333

    
334
        cerr << "MIDIFileReader::open() - caught exception - " << e.what() << endl;
335
        m_error = e.what();
336
    }
337
    
338
done:
339
    m_midiFile->close();
340
    delete m_midiFile;
341

    
342
    for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
343

    
344
        // Convert the deltaTime to an absolute time since the track
345
        // start.  The addTime method returns the sum of the current
346
        // MIDI Event delta time plus the argument.
347

    
348
        unsigned long acc = 0;
349

    
350
        for (MIDITrack::iterator i = m_midiComposition[track].begin();
351
             i != m_midiComposition[track].end(); ++i) {
352
#ifdef DEBUG_MIDI_FILE_READER
353
            cerr << "converting delta time " << i->getTime();
354
#endif
355
            acc = i->addTime(acc);
356
#ifdef DEBUG_MIDI_FILE_READER
357
            cerr << " to " << i->getTime() << endl;
358
#endif
359
        }
360

    
361
        consolidateNoteOffEvents(track);
362
    }
363

    
364
    return retval;
365
}
366

    
367
// Parse and ensure the MIDI Header is legitimate
368
//
369
bool
370
MIDIFileReader::parseHeader(const string &midiHeader)
371
{
372
    if (midiHeader.size() < 14) {
373
#ifdef DEBUG_MIDI_FILE_READER
374
        cerr << "MIDIFileReader::parseHeader() - file header undersized" << endl;
375
#endif
376
        return false;
377
    }
378

    
379
    if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) {
380
#ifdef DEBUG_MIDI_FILE_READER
381
        cerr << "MIDIFileReader::parseHeader()"
382
             << "- file header not found or malformed"
383
             << endl;
384
#endif
385
        return false;
386
    }
387

    
388
    if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) {
389
#ifdef DEBUG_MIDI_FILE_READER
390
        cerr << "MIDIFileReader::parseHeader()"
391
             << " - header length incorrect"
392
             << endl;
393
#endif
394
        return false;
395
    }
396

    
397
    m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2));
398
    m_numberOfTracks = midiBytesToInt(midiHeader.substr(10,2));
399
    m_timingDivision = midiBytesToInt(midiHeader.substr(12,2));
400

    
401
#ifdef DEBUG_MIDI_FILE_READER
402
    if (m_timingDivision < 0) {
403
        cerr << "MIDIFileReader::parseHeader()"
404
                  << " - file uses SMPTE timing"
405
                  << endl;
406
    }
407
#endif
408

    
409
    return true; 
410
}
411

    
412
// Extract the contents from a MIDI file track and places it into
413
// our local map of MIDI events.
414
//
415
bool
416
MIDIFileReader::parseTrack(unsigned int trackNum)
417
{
418
    MIDIByte midiByte, metaEventCode, data1, data2;
419
    MIDIByte eventCode = 0x80;
420
    string metaMessage;
421
    unsigned int messageLength;
422
    unsigned long deltaTime;
423
    unsigned long accumulatedTime = 0;
424

    
425
    // Remember the last non-meta status byte (-1 if we haven't seen one)
426
    int runningStatus = -1;
427

    
428
    while (!m_midiFile->eof() && (m_trackByteCount > 0)) {
429

    
430
        if (eventCode < 0x80) {
431
#ifdef DEBUG_MIDI_FILE_READER
432
            cerr << "WARNING: Invalid event code " << eventCode
433
                 << " in MIDI file" << endl;
434
#endif
435
            throw_exception("Invalid event code %d found", int(eventCode));
436
        }
437

    
438
        deltaTime = getNumberFromMIDIBytes();
439

    
440
#ifdef DEBUG_MIDI_FILE_READER
441
        cerr << "read delta time " << deltaTime << endl;
442
#endif
443

    
444
        // Get a single byte
445
        midiByte = getMIDIByte();
446

    
447
        if (!(midiByte & MIDI_STATUS_BYTE_MASK)) {
448

    
449
            if (runningStatus < 0) {
450
                throw_exception("Running status used for first event in track");
451
            }
452

    
453
            eventCode = (MIDIByte)runningStatus;
454
            data1 = midiByte;
455

    
456
#ifdef DEBUG_MIDI_FILE_READER
457
            cerr << "using running status (byte " << int(midiByte) << " found)" << endl;
458
#endif
459
        } else {
460
#ifdef DEBUG_MIDI_FILE_READER
461
            cerr << "have new event code " << int(midiByte) << endl;
462
#endif
463
            eventCode = midiByte;
464
            data1 = getMIDIByte();
465
        }
466

    
467
        if (eventCode == MIDI_FILE_META_EVENT) {
468

    
469
            metaEventCode = data1;
470
            messageLength = getNumberFromMIDIBytes();
471

    
472
#ifdef DEBUG_MIDI_FILE_READER
473
            cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found" << endl;
474
#endif
475
            metaMessage = getMIDIBytes(messageLength);
476

    
477
            accumulatedTime += deltaTime;
478

    
479
            MIDIEvent e(deltaTime,
480
                        MIDI_FILE_META_EVENT,
481
                        metaEventCode,
482
                        metaMessage);
483

    
484
            m_midiComposition[trackNum].push_back(e);
485

    
486
            if (metaEventCode == MIDI_TRACK_NAME) {
487
                m_trackNames[trackNum] = metaMessage.c_str();
488
            }
489

    
490
        } else { // non-meta events
491

    
492
            runningStatus = eventCode;
493

    
494
            int channel = (eventCode & MIDI_CHANNEL_NUM_MASK);
495
            
496
            accumulatedTime += deltaTime;
497

    
498
            switch (eventCode & MIDI_MESSAGE_TYPE_MASK) {
499

    
500
            case MIDI_NOTE_ON:
501
            case MIDI_NOTE_OFF:
502
            case MIDI_POLY_AFTERTOUCH:
503
            case MIDI_CTRL_CHANGE:
504
                data2 = getMIDIByte();
505

    
506
                {
507
                // create and store our event
508
                MIDIEvent midiEvent(deltaTime, eventCode, data1, data2);
509

    
510
#ifdef DEBUG_MIDI_FILE_READER
511
                cerr << "MIDI event for channel " << channel << " (track "
512
                     << trackNum << ") with delta time " << deltaTime << endl;
513
#endif
514

    
515
                m_midiComposition[trackNum].push_back(midiEvent);
516
                }
517
                break;
518

    
519
            case MIDI_PITCH_BEND:
520
                data2 = getMIDIByte();
521

    
522
                {
523
                // create and store our event
524
                MIDIEvent midiEvent(deltaTime, eventCode, data1, data2);
525
                m_midiComposition[trackNum].push_back(midiEvent);
526
                }
527
                break;
528

    
529
            case MIDI_PROG_CHANGE:
530
            case MIDI_CHNL_AFTERTOUCH:
531
                
532
                {
533
                // create and store our event
534
                MIDIEvent midiEvent(deltaTime, eventCode, data1);
535
                m_midiComposition[trackNum].push_back(midiEvent);
536
                }
537
                break;
538

    
539
            case MIDI_SYSTEM_EXCLUSIVE:
540
                messageLength = getNumberFromMIDIBytes(data1);
541

    
542
#ifdef DEBUG_MIDI_FILE_READER
543
                cerr << "SysEx of " << messageLength << " bytes found" << endl;
544
#endif
545

    
546
                metaMessage= getMIDIBytes(messageLength);
547

    
548
                if (MIDIByte(metaMessage[metaMessage.length() - 1]) !=
549
                        MIDI_END_OF_EXCLUSIVE)
550
                {
551
#ifdef DEBUG_MIDI_FILE_READER
552
                    cerr << "MIDIFileReader::parseTrack() - "
553
                              << "malformed or unsupported SysEx type"
554
                              << endl;
555
#endif
556
                    continue;
557
                }
558

    
559
                // chop off the EOX 
560
                // length fixed by Pedro Lopez-Cabanillas (20030523)
561
                //
562
                metaMessage = metaMessage.substr(0, metaMessage.length()-1);
563

    
564
                {
565
                MIDIEvent midiEvent(deltaTime,
566
                                    MIDI_SYSTEM_EXCLUSIVE,
567
                                    metaMessage);
568
                m_midiComposition[trackNum].push_back(midiEvent);
569
                }
570
                break;
571

    
572
            case MIDI_END_OF_EXCLUSIVE:
573
#ifdef DEBUG_MIDI_FILE_READER
574
                cerr << "MIDIFileReader::parseTrack() - "
575
                          << "Found a stray MIDI_END_OF_EXCLUSIVE" << endl;
576
#endif
577
                break;
578

    
579
            default:
580
#ifdef DEBUG_MIDI_FILE_READER
581
                cerr << "MIDIFileReader::parseTrack()" 
582
                          << " - Unsupported MIDI Event Code:  "
583
                          << (int)eventCode << endl;
584
#endif
585
                break;
586
            } 
587
        }
588
    }
589

    
590
    return true;
591
}
592

    
593
// Delete dead NOTE OFF and NOTE ON/Zero Velocity Events after
594
// reading them and modifying their relevant NOTE ONs.  Return true
595
// if there are some notes in this track.
596
//
597
bool
598
MIDIFileReader::consolidateNoteOffEvents(unsigned int track)
599
{
600
    bool notesOnTrack = false;
601
    bool noteOffFound;
602

    
603
    MIDITrack &t = m_midiComposition[track];
604

    
605
    for (MIDITrack::iterator i = t.begin(); i != t.end(); ++i) {
606

    
607
        if (i->getMessageType() == MIDI_NOTE_ON && i->getVelocity() > 0) {
608

    
609
#ifdef DEBUG_MIDI_FILE_READER
610
            cerr << "Looking for note-offs for note at " << i->getTime() << " (pitch " << (int)i->getPitch() << ")" <<  endl;
611
#endif
612

    
613
            notesOnTrack = true;
614
            noteOffFound = false;
615

    
616
            for (MIDITrack::iterator j = i; j != t.end(); ++j) {
617

    
618
                if ((j->getChannelNumber() == i->getChannelNumber()) &&
619
                    (j->getPitch() == i->getPitch()) &&
620
                    (j->getMessageType() == MIDI_NOTE_OFF ||
621
                    (j->getMessageType() == MIDI_NOTE_ON &&
622
                     j->getVelocity() == 0x00))) {
623

    
624
#ifdef DEBUG_MIDI_FILE_READER
625
                    cerr << "Found note-off at " << j->getTime() << " for note at " << i->getTime() << endl;
626
#endif
627

    
628
                    i->setDuration(j->getTime() - i->getTime());
629

    
630
#ifdef DEBUG_MIDI_FILE_READER
631
                    cerr << "Duration is now " << i->getDuration() << endl;
632
#endif
633

    
634
                    t.erase(j);
635

    
636
                    noteOffFound = true;
637
                    break;
638
                }
639
            }
640

    
641
            // If no matching NOTE OFF has been found then set
642
            // Event duration to length of track
643
            //
644
            if (!noteOffFound) {
645
#ifdef DEBUG_MIDI_FILE_READER
646
                cerr << "Failed to find note-off for note at " << i->getTime() << endl;
647
#endif
648
                MIDITrack::iterator j = t.end();
649
                --j;
650
                i->setDuration(j->getTime() - i->getTime());
651
            }
652
        }
653
    }
654

    
655
    return notesOnTrack;
656
}
657

    
658
MIDIComposition
659
MIDIFileReader::load() const
660
{
661
    return m_midiComposition;
662
}
663

    
664