diff options
-rw-r--r-- | sound/midiparser.h | 153 |
1 files changed, 115 insertions, 38 deletions
diff --git a/sound/midiparser.h b/sound/midiparser.h index d12664da93..be0b3e39a3 100644 --- a/sound/midiparser.h +++ b/sound/midiparser.h @@ -19,6 +19,8 @@ * */ +//! \brief Declarations related to the MidiParser class + #ifndef INCLUDED_MIDIPARSER #define INCLUDED_MIDIPARSER @@ -36,16 +38,27 @@ class MidiDriver; // ////////////////////////////////////////////////// +//! Maintains time and position state within a MIDI stream. +/*! A single Tracker struct is used by MidiParser to keep track + * of its current position in the MIDI stream. The Tracker + * struct, however, allows alternative locations to be cached. + * See MidiParser::jumpToTick() for an example of tracking + * multiple locations within a MIDI stream. NOTE: It is + * important to also maintain pre-parsed EventInfo data for + * each Tracker location. + */ + struct Tracker { - byte * _play_pos; - uint32 _play_time; - uint32 _play_tick; - uint32 _last_event_time; - uint32 _last_event_tick; - byte _running_status; + byte * _play_pos; //!< A pointer to the next event to be parsed + uint32 _play_time; //!< Current time in microseconds; may be in between event times + uint32 _play_tick; //!< Current MIDI tick; may be in between event ticks + uint32 _last_event_time; //!< The time, in microseconds, of the last event that was parsed + uint32 _last_event_tick; //!< The tick at which the last parsed event occurs + byte _running_status; //!< Cached MIDI command, for MIDI streams that rely on implied event codes Tracker() { clear(); } + //! Copy constructor for each duplication of Tracker information. Tracker (const Tracker ©) : _play_pos (copy._play_pos), _play_time (copy._play_time), @@ -55,6 +68,7 @@ struct Tracker { _running_status (copy._running_status) { } + //! Clears all data; used by the constructor for initialization. void clear() { _play_pos = 0; _play_time = 0; @@ -65,30 +79,47 @@ struct Tracker { } }; +//! Provides comprehensive information on the next event in the MIDI stream. +/*! An EventInfo struct is instantiated by format-specific implementations + * of MidiParser::parseNextEvent() each time another event is needed. + */ + struct EventInfo { - byte * start; // Points to delta - uint32 delta; - byte event; + byte * start; //!< Position in the MIDI stream where the event starts. + //!< For delta-based MIDI streams (e.g. SMF and XMIDI), this points to the delta. + uint32 delta; //!< The number of ticks after the previous event that this event should occur. + byte event; //!< Upper 4 bits are the command code, lower 4 bits are the MIDI channel. + //!< For META, event == 0xFF. For SysEx, event == 0xF0. union { struct { - byte param1; - byte param2; + byte param1; //!< The first parameter in a simple MIDI message. + byte param2; //!< The second parameter in a simple MIDI message. } basic; struct { - byte type; // Used for METAs - byte * data; // Used for SysEx and METAs + byte type; //!< For META events, this indicates the META type. + byte * data; //!< For META and SysEx events, this points to the start of the data. } ext; }; - uint32 length; // Used for SysEx and METAs, and note lengths + uint32 length; //!< For META and SysEx blocks, this indicates the length of the data. + //!< For Note On events, a non-zero value indicates that no Note Off event + //!< will occur, and the MidiParser will have to generate one itself. + //!< For all other events, this value should always be zero. - byte channel() { return event & 0x0F; } - byte command() { return event >> 4; } + byte channel() { return event & 0x0F; } //!< Separates the MIDI channel from the event. + byte command() { return event >> 4; } //!< Separates the command code from the event. }; +//! Provides expiration tracking for hanging notes. +/*! Hanging notes are used when a MIDI format does not include explicit Note Off + * events, or when "Smart Jump" is enabled so that active notes are intelligently + * expired when a jump occurs. The NoteTimer struct keeps track of how much + * longer a note should remain active before being turned off. + */ + struct NoteTimer { - byte channel; - byte note; - uint32 time_left; + byte channel; //!< The MIDI channel on which the note was played + byte note; //!< The note number for the active note + uint32 time_left; //!< The time, in microseconds, remaining before the note should be turned off NoteTimer() : channel(0), note(0), time_left(0) {} }; @@ -101,28 +132,48 @@ struct NoteTimer { // ////////////////////////////////////////////////// +//! A framework and common functionality for parsing MIDI streams. +/*! The MidiParser provides a framework in which to load + * parse and traverse MIDI streams. Loading and parsing + * events for specific formats requires a class to be + * derived from MidiParser. + * + * The MidiParser must be provided with a MidiDriver interface, + * which it uses to transmit events. However, it does NOT + * automatically hook into the MidiDriver's timer callback + * or set up its timer rate from the MidiDriver. The client + * using the MidiParser must set the timer rate provide + * a means of firing the MidiParser's onTimer() method at + * appropriate intervals. The onTimer() event may be called + * by the client or by manually hooking and unhooking the + * MidiParser to the MidiDriver's timer callback. + */ + class MidiParser { private: - uint16 _active_notes[128]; // Each uint16 is a bit mask for channels that have that note on - NoteTimer _hanging_notes[32]; // Supports "smart" jump with notes still playing - byte _hanging_notes_count; + uint16 _active_notes[128]; //!< Each uint16 is a bit mask for channels that have that note on. + NoteTimer _hanging_notes[32]; //!< Maintains expiration info for up to 32 notes. + //!< Used for "Smart Jump" and MIDI formats that do not include explicit Note Off events. + byte _hanging_notes_count; //!< Count of hanging notes, used to optimize expiration. protected: - MidiDriver *_driver; - uint32 _timer_rate; - uint32 _ppqn; // Pulses (ticks) Per Quarter Note - uint32 _tempo; // Microseconds per quarter note - uint32 _psec_per_tick; // Microseconds per tick (_tempo / _ppqn) - bool _autoLoop; // For lightweight clients that don't monitor events - bool _smartJump; // Support smart expiration of hanging notes when jumping - - byte * _tracks[32]; - byte _num_tracks; - byte _active_track; - - Tracker _position; - EventInfo _next_event; - bool _abort_parse; // If a jump or some other interruption occurs + MidiDriver *_driver; //!< The device to which all events will be transmitted. + uint32 _timer_rate; //!< The time in microseconds between onTimer() calls. Obtained from the MidiDriver. + uint32 _ppqn; //!< Pulses Per Quarter Note. (We refer to "pulses" as "ticks".) + uint32 _tempo; //!< Microseconds per quarter note. + uint32 _psec_per_tick; //!< Microseconds per tick (_tempo / _ppqn). + bool _autoLoop; //!< For lightweight clients that don't provide their own flow control. + bool _smartJump; //!< Support smart expiration of hanging notes when jumping + + byte * _tracks[32]; //!< Multi-track MIDI formats are supported, up to 32 tracks. + byte _num_tracks; //!< Count of total tracks for multi-track MIDI formats. 1 for single-track formats. + byte _active_track; //!< Keeps track of the currently active track, in multi-track formats. + + Tracker _position; //!< The current time/position in the active track. + EventInfo _next_event; //!< The next event to transmit. Events are preparsed + //!< so each event is parsed only once; this permits + //!< simulated events in certain formats. + bool _abort_parse; //!< If a jump or other operation interrupts parsing, flag to abort. protected: static uint32 readVLQ (byte * &data); @@ -134,13 +185,23 @@ protected: void hangingNote (byte channel, byte note, uint32 ticks_left); void hangAllActiveNotes(); - // Multi-byte read helpers + //! Platform independent BE uint32 read-and-advance. + /*! This help function reads Big Endian 32-bit numbers + * from a memory pointer, at the same time advancing + * the pointer. + */ uint32 read4high (byte * &data) { uint32 val = 0; int i; for (i = 0; i < 4; ++i) val = (val << 8) | *data++; return val; } + + //! Platform independent LE uint16 read-and-advance. + /*! This help function reads Little Endian 16-bit numbers + * from a memory pointer, at the same time advancing + * the pointer. + */ uint16 read2low (byte * &data) { uint16 val = 0; int i; @@ -149,6 +210,22 @@ protected: } public: + //! Configuration options for MidiParser + /*! The following options can be set to modify MidiParser's + * behavior. + * + * \b mpMalformedPitchBends - Events containing a pitch bend + * command should be treated as single-byte padding before the + * real event. This allows the MidiParser to work with some + * malformed SMF files from Simon 1/2. + * + * \b mpAutoLoop - Sets auto-looping, which can be used by + * lightweight clients that don't provide their own flow control. + * + * \b mpSmartJump - Sets smart jumping, which intelligently + * expires notes that are active when a jump is made, rather + * than just cutting them off. + */ enum { mpMalformedPitchBends = 1, mpAutoLoop = 2, |