Chris@301: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@301: 
Chris@301: /*
Chris@301:     Sonic Visualiser
Chris@301:     An audio file viewer and annotation editor.
Chris@301:     Centre for Digital Music, Queen Mary, University of London.
Chris@301:     
Chris@301:     This program is free software; you can redistribute it and/or
Chris@301:     modify it under the terms of the GNU General Public License as
Chris@301:     published by the Free Software Foundation; either version 2 of the
Chris@301:     License, or (at your option) any later version.  See the file
Chris@301:     COPYING included with this distribution for more information.
Chris@301: */
Chris@301: 
Chris@301: 
Chris@301: /*
Chris@301:    This is a modified version of a source file from the 
Chris@301:    Rosegarden MIDI and audio sequencer and notation editor.
Chris@301:    This file copyright 2000-2006 Richard Bown and Chris Cannam.
Chris@301: */
Chris@301: 
Chris@1581: #ifndef SV_MIDI_EVENT_H
Chris@1581: #define SV_MIDI_EVENT_H
Chris@301: 
Chris@301: #include <QString>
Chris@301: #include <string>
Chris@301: #include <iostream>
Chris@1038: #include <stdexcept>
Chris@1038: 
Chris@687: #include "base/Debug.h"
Chris@301: 
Chris@301: typedef unsigned char MIDIByte;
Chris@301: 
Chris@301: namespace MIDIConstants
Chris@301: {
Chris@301:     static const char *const MIDI_FILE_HEADER         = "MThd";
Chris@301:     static const char *const MIDI_TRACK_HEADER        = "MTrk";
Chris@301: 
Chris@301:     static const MIDIByte MIDI_STATUS_BYTE_MASK       = 0x80;
Chris@301:     static const MIDIByte MIDI_MESSAGE_TYPE_MASK      = 0xF0;
Chris@301:     static const MIDIByte MIDI_CHANNEL_NUM_MASK       = 0x0F;
Chris@301: 
Chris@301:     static const MIDIByte MIDI_NOTE_OFF               = 0x80;
Chris@301:     static const MIDIByte MIDI_NOTE_ON                = 0x90;
Chris@301:     static const MIDIByte MIDI_POLY_AFTERTOUCH        = 0xA0;
Chris@301:     static const MIDIByte MIDI_CTRL_CHANGE            = 0xB0;
Chris@301:     static const MIDIByte MIDI_PROG_CHANGE            = 0xC0;
Chris@301:     static const MIDIByte MIDI_CHNL_AFTERTOUCH        = 0xD0;
Chris@301:     static const MIDIByte MIDI_PITCH_BEND             = 0xE0;
Chris@301:     static const MIDIByte MIDI_SELECT_CHNL_MODE       = 0xB0;
Chris@301:     static const MIDIByte MIDI_SYSTEM_EXCLUSIVE       = 0xF0;
Chris@301:     static const MIDIByte MIDI_TC_QUARTER_FRAME       = 0xF1;
Chris@301:     static const MIDIByte MIDI_SONG_POSITION_PTR      = 0xF2;
Chris@301:     static const MIDIByte MIDI_SONG_SELECT            = 0xF3;
Chris@301:     static const MIDIByte MIDI_TUNE_REQUEST           = 0xF6;
Chris@301:     static const MIDIByte MIDI_END_OF_EXCLUSIVE       = 0xF7;
Chris@301:     static const MIDIByte MIDI_TIMING_CLOCK           = 0xF8;
Chris@301:     static const MIDIByte MIDI_START                  = 0xFA;
Chris@301:     static const MIDIByte MIDI_CONTINUE               = 0xFB;
Chris@301:     static const MIDIByte MIDI_STOP                   = 0xFC;
Chris@301:     static const MIDIByte MIDI_ACTIVE_SENSING         = 0xFE;
Chris@301:     static const MIDIByte MIDI_SYSTEM_RESET           = 0xFF;
Chris@301:     static const MIDIByte MIDI_SYSEX_NONCOMMERCIAL    = 0x7D;
Chris@301:     static const MIDIByte MIDI_SYSEX_NON_RT           = 0x7E;
Chris@301:     static const MIDIByte MIDI_SYSEX_RT               = 0x7F;
Chris@301:     static const MIDIByte MIDI_SYSEX_RT_COMMAND       = 0x06;
Chris@301:     static const MIDIByte MIDI_SYSEX_RT_RESPONSE      = 0x07;
Chris@301:     static const MIDIByte MIDI_MMC_STOP               = 0x01;
Chris@301:     static const MIDIByte MIDI_MMC_PLAY               = 0x02;
Chris@301:     static const MIDIByte MIDI_MMC_DEFERRED_PLAY      = 0x03;
Chris@301:     static const MIDIByte MIDI_MMC_FAST_FORWARD       = 0x04;
Chris@301:     static const MIDIByte MIDI_MMC_REWIND             = 0x05;
Chris@301:     static const MIDIByte MIDI_MMC_RECORD_STROBE      = 0x06;
Chris@301:     static const MIDIByte MIDI_MMC_RECORD_EXIT        = 0x07;
Chris@301:     static const MIDIByte MIDI_MMC_RECORD_PAUSE       = 0x08;
Chris@301:     static const MIDIByte MIDI_MMC_PAUSE              = 0x08;
Chris@301:     static const MIDIByte MIDI_MMC_EJECT              = 0x0A;
Chris@301:     static const MIDIByte MIDI_MMC_LOCATE             = 0x44;
Chris@301:     static const MIDIByte MIDI_FILE_META_EVENT        = 0xFF;
Chris@301:     static const MIDIByte MIDI_SEQUENCE_NUMBER        = 0x00;
Chris@301:     static const MIDIByte MIDI_TEXT_EVENT             = 0x01;
Chris@301:     static const MIDIByte MIDI_COPYRIGHT_NOTICE       = 0x02;
Chris@301:     static const MIDIByte MIDI_TRACK_NAME             = 0x03;
Chris@301:     static const MIDIByte MIDI_INSTRUMENT_NAME        = 0x04;
Chris@301:     static const MIDIByte MIDI_LYRIC                  = 0x05;
Chris@301:     static const MIDIByte MIDI_TEXT_MARKER            = 0x06;
Chris@301:     static const MIDIByte MIDI_CUE_POINT              = 0x07;
Chris@301:     static const MIDIByte MIDI_CHANNEL_PREFIX         = 0x20;
Chris@301:     static const MIDIByte MIDI_CHANNEL_PREFIX_OR_PORT = 0x21;
Chris@301:     static const MIDIByte MIDI_END_OF_TRACK           = 0x2F;
Chris@301:     static const MIDIByte MIDI_SET_TEMPO              = 0x51;
Chris@301:     static const MIDIByte MIDI_SMPTE_OFFSET           = 0x54;
Chris@301:     static const MIDIByte MIDI_TIME_SIGNATURE         = 0x58;
Chris@301:     static const MIDIByte MIDI_KEY_SIGNATURE          = 0x59;
Chris@301:     static const MIDIByte MIDI_SEQUENCER_SPECIFIC     = 0x7F;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_BANK_MSB      = 0x00;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_VOLUME        = 0x07;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_BANK_LSB      = 0x20;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_MODULATION    = 0x01;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_PAN           = 0x0A;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_SUSTAIN       = 0x40;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_RESONANCE     = 0x47;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_RELEASE       = 0x48;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_ATTACK        = 0x49;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_FILTER        = 0x4A;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_REVERB        = 0x5B;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_CHORUS        = 0x5D;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_NRPN_1        = 0x62;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_NRPN_2        = 0x63;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_RPN_1         = 0x64;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_RPN_2         = 0x65;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_SOUNDS_OFF    = 0x78;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_RESET         = 0x79;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_LOCAL         = 0x7A;
Chris@301:     static const MIDIByte MIDI_CONTROLLER_ALL_NOTES_OFF = 0x7B;
Chris@301:     static const MIDIByte MIDI_PERCUSSION_CHANNEL       = 9;
Chris@301: }
Chris@301: 
Chris@301: class MIDIEvent
Chris@301: {
Chris@301: public:
Chris@301:     MIDIEvent(unsigned long deltaTime,
Chris@1038:               int eventCode,
Chris@1038:               int data1 = 0,
Chris@1038:               int data2 = 0) :
Chris@1429:         m_deltaTime(deltaTime),
Chris@1429:         m_duration(0),
Chris@1429:         m_metaEventCode(0)
Chris@1038:     {
Chris@1038:         if (eventCode < 0 || eventCode > 0xff ||
Chris@1038:             data1 < 0 || data1 > 0xff ||
Chris@1038:             data2 < 0 || data2 > 0xff) {
Chris@1038:             throw std::domain_error("not all args within byte range");
Chris@1038:         }
Chris@1038:         m_eventCode = MIDIByte(eventCode);
Chris@1038:         m_data1 = MIDIByte(data1);
Chris@1038:         m_data2 = MIDIByte(data2);
Chris@1038:     }
Chris@301: 
Chris@301:     MIDIEvent(unsigned long deltaTime,
Chris@301:               MIDIByte eventCode,
Chris@301:               MIDIByte metaEventCode,
Chris@301:               const std::string &metaMessage) :
Chris@1429:         m_deltaTime(deltaTime),
Chris@1429:         m_duration(0),
Chris@1429:         m_eventCode(eventCode),
Chris@1429:         m_data1(0),
Chris@1429:         m_data2(0),
Chris@1429:         m_metaEventCode(metaEventCode),
Chris@1429:         m_metaMessage(metaMessage)
Chris@301:     { }
Chris@301: 
Chris@301:     MIDIEvent(unsigned long deltaTime,
Chris@301:               MIDIByte eventCode,
Chris@301:               const std::string &sysEx) :
Chris@1429:         m_deltaTime(deltaTime),
Chris@1429:         m_duration(0),
Chris@1429:         m_eventCode(eventCode),
Chris@1429:         m_data1(0),
Chris@1429:         m_data2(0),
Chris@1429:         m_metaEventCode(0),
Chris@1429:         m_metaMessage(sysEx)
Chris@301:     { }
Chris@301: 
Chris@1733:     MIDIEvent(const MIDIEvent &) =default;
Chris@1733:     MIDIEvent& operator=(const MIDIEvent &) =default;
Chris@1733:     
Chris@301:     ~MIDIEvent() { }
Chris@301: 
Chris@301:     void setTime(const unsigned long &time) { m_deltaTime = time; }
Chris@301:     void setDuration(const unsigned long& duration) { m_duration = duration;}
Chris@301:     unsigned long addTime(const unsigned long &time) {
Chris@1429:         m_deltaTime += time;
Chris@1429:         return m_deltaTime;
Chris@301:     }
Chris@301: 
Chris@301:     MIDIByte getMessageType() const
Chris@301:         { return (m_eventCode & MIDIConstants::MIDI_MESSAGE_TYPE_MASK); }
Chris@301: 
Chris@301:     MIDIByte getChannelNumber() const
Chris@301:         { return (m_eventCode & MIDIConstants::MIDI_CHANNEL_NUM_MASK); }
Chris@301: 
Chris@301:     unsigned long getTime() const { return m_deltaTime; }
Chris@301:     unsigned long getDuration() const { return m_duration; }
Chris@301: 
Chris@301:     MIDIByte getPitch() const { return m_data1; }
Chris@301:     MIDIByte getVelocity() const { return m_data2; }
Chris@301:     MIDIByte getData1() const { return m_data1; }
Chris@301:     MIDIByte getData2() const { return m_data2; }
Chris@301:     MIDIByte getEventCode() const { return m_eventCode; }
Chris@301: 
Chris@301:     bool isMeta() const { return (m_eventCode == MIDIConstants::MIDI_FILE_META_EVENT); }
Chris@301: 
Chris@301:     MIDIByte getMetaEventCode() const { return m_metaEventCode; }
Chris@301:     std::string getMetaMessage() const { return m_metaMessage; }
Chris@301:     void setMetaMessage(const std::string &meta) { m_metaMessage = meta; }
Chris@301: 
Chris@301:     friend bool operator<(const MIDIEvent &a, const MIDIEvent &b);
Chris@301: 
Chris@301: private:
Chris@301:     unsigned long  m_deltaTime;
Chris@301:     unsigned long  m_duration;
Chris@301:     MIDIByte       m_eventCode;
Chris@301:     MIDIByte       m_data1;         // or Note
Chris@301:     MIDIByte       m_data2;         // or Velocity
Chris@301:     MIDIByte       m_metaEventCode;
Chris@301:     std::string    m_metaMessage;
Chris@301: };
Chris@301: 
Chris@301: // Comparator for sorting
Chris@301: //
Chris@301: struct MIDIEventCmp
Chris@301: {
Chris@301:     bool operator()(const MIDIEvent &mE1, const MIDIEvent &mE2) const
Chris@301:     { return mE1.getTime() < mE2.getTime(); }
Chris@301: 
Chris@301:     bool operator()(const MIDIEvent *mE1, const MIDIEvent *mE2) const
Chris@301:     { return mE1->getTime() < mE2->getTime(); }
Chris@301: };
Chris@301: 
Chris@301: class MIDIException : virtual public std::exception
Chris@301: {
Chris@301: public:
Chris@301:     MIDIException(QString message) throw() : m_message(message) {
Chris@301:         std::cerr << "WARNING: MIDI exception: "
Chris@1429:                   << message.toLocal8Bit().data() << std::endl;
Chris@301:     }
Chris@301:     virtual ~MIDIException() throw() { }
Chris@301: 
Chris@1580:     const char *what() const throw() override {
Chris@1429:         return m_message.toLocal8Bit().data();
Chris@301:     }
Chris@301: 
Chris@301: protected:
Chris@301:     QString m_message;
Chris@301: };
Chris@301: 
Chris@301: #endif