#include <midiparser.h>
Public Types | |
enum | { mpAutoLoop = 2, mpSmartJump = 3, mpCenterPitchWheelOnUnload = 4, mpSendSustainOffOnNotesOff = 5, mpDisableAllNotesOffMidiEvents = 6, mpDisableAutoStartPlayback = 7 } |
typedef void(* | XMidiCallbackProc) (byte eventData, void *refCon) |
Public Member Functions | |
MidiParser (int8 source=-1) | |
virtual bool | loadMusic (byte *data, uint32 size)=0 |
virtual void | unloadMusic () |
virtual void | property (int prop, int value) |
virtual int32 | determineDataSize (Common::SeekableReadStream *stream) |
virtual void | setMidiDriver (MidiDriver_BASE *driver) |
void | setTimerRate (uint32 rate) |
virtual void | setTempo (uint32 tempo) |
virtual void | onTimer () |
bool | isPlaying () const |
bool | startPlaying () |
void | stopPlaying () |
void | pausePlaying () |
void | resumePlaying () |
bool | setTrack (int track) |
bool | jumpToTick (uint32 tick, bool fireEvents=false, bool stopNotes=true, bool dontSendNoteOn=false) |
virtual bool | hasJumpIndex (uint8 index) |
virtual bool | jumpToIndex (uint8 index, bool stopNotes=true) |
uint32 | getPPQN () |
virtual uint32 | getTick () |
Static Public Member Functions | |
static void | defaultXMidiCallback (byte eventData, void *refCon) |
static MidiParser * | createParser_SMF (int8 source=-1) |
static MidiParser * | createParser_XMIDI (XMidiCallbackProc proc=defaultXMidiCallback, void *refCon=0, int source=-1) |
static MidiParser * | createParser_QT (int8 source=-1) |
static void | timerCallback (void *data) |
Protected Member Functions | |
virtual void | resetTracking () |
virtual void | allNotesOff () |
virtual void | parseNextEvent (EventInfo &info)=0 |
virtual bool | processEvent (const EventInfo &info, bool fireEvents=true) |
void | activeNote (byte channel, byte note, bool active) |
void | hangingNote (byte channel, byte note, uint32 ticksLeft, bool recycle=true) |
void | hangAllActiveNotes () |
virtual void | onTrackStart (uint8 track) |
virtual void | sendToDriver (uint32 b) |
void | sendToDriver (byte status, byte firstOp, byte secondOp) |
virtual void | sendMetaEventToDriver (byte type, byte *data, uint16 length) |
uint32 | read4high (byte *&data) |
uint16 | read2low (byte *&data) |
Static Protected Member Functions | |
static uint32 | readVLQ (byte *&data) |
Protected Attributes | |
uint16 | _activeNotes [128] |
Each uint16 is a bit mask for channels that have that note on. | |
NoteTimer | _hangingNotes [32] |
Used for "Smart Jump" and MIDI formats that do not include explicit Note Off events. More... | |
byte | _hangingNotesCount |
Count of hanging notes, used to optimize expiration. | |
MidiDriver_BASE * | _driver |
The device to which all events will be transmitted. | |
uint32 | _timerRate |
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 | _psecPerTick |
Microseconds per tick (_tempo / _ppqn). | |
uint32 | _sysExDelay |
Number of microseconds until the next SysEx event can be sent. | |
bool | _autoLoop |
For lightweight clients that don't provide their own flow control. | |
bool | _smartJump |
Support smart expiration of hanging notes when jumping. | |
bool | _centerPitchWheelOnUnload |
Center the pitch wheels when unloading a song. | |
bool | _sendSustainOffOnNotesOff |
Send a sustain off on a notes off event, stopping hanging notes. | |
bool | _disableAllNotesOffMidiEvents |
Don't send All Notes Off MIDI messages. | |
bool | _disableAutoStartPlayback |
Do not automatically start playback after parsing MIDI data or setting the track. | |
byte * | _tracks [MAXIMUM_TRACKS] |
Multi-track MIDI formats are supported, up to 120 tracks. | |
byte | _numTracks |
Count of total tracks for multi-track MIDI formats. 1 for single-track formats. | |
byte | _activeTrack |
Keeps track of the currently active track, in multi-track formats. | |
Tracker | _position |
The current time/position in the active track. | |
EventInfo | _nextEvent |
bool | _abortParse |
If a jump or other operation interrupts parsing, flag to abort. | |
bool | _jumpingToTick |
True if currently inside jumpToTick. | |
bool | _doParse |
True if the parser should be parsing; false if it should not be active. | |
bool | _pause |
True if the parser has paused parsing. | |
int8 | _source |
Static Protected Attributes | |
static const uint8 | MAXIMUM_TRACKS = 120 |
A framework and common functionality for parsing event-based music streams. The MidiParser provides a framework in which to load, parse and traverse event-based music data. Note the avoidance of the phrase "MIDI data." Despite its name, MidiParser derivatives can be used to manage a wide variety of event-based music formats. It is, however, based on the premise that the format in question can be played in the form of specification MIDI events.
In order to use MidiParser to parse your music format, follow these steps:
STEP 1: Write a MidiParser derivative. The MidiParser base class provides functionality considered common to the task of parsing event-based music. In order to parse a particular format, create a derived class that implements, at minimum, the following format-specific methods:
In addition to the above functions, the derived class may also override the default MidiParser behavior for the following methods:
Please see the documentation for these individual functions for more information on their use.
The naming convention for classes derived from MidiParser is MidiParser_XXX, where "XXX" is some short designator for the format the class will support. For instance, the MidiParser derivative for parsing the Standard MIDI File format is MidiParser_SMF.
STEP 2: Create an object of your derived class. Each MidiParser object can parse at most one (1) song at a time. However, a MidiParser object can be reused to play another song once it is no longer needed to play whatever it was playing. In other words, MidiParser objects do not have to be destroyed and recreated from one song to the next.
STEP 3: Specify a MidiDriver to send events to. MidiParser works by sending MIDI and meta events to a MidiDriver. In the simplest configuration, you can plug a single MidiParser directly into the output MidiDriver being used. However, you can only plug in one at a time; otherwise channel conflicts will occur. Multiple parsers can be used if they do not use the same channels, or if they use some form of dynamic channel allocation. Furthermore, meta events that may be needed to interactively control music flow cannot be handled because they are being sent directly to the output device.
If you need more control over the MidiParser while it's playing, you can create your own "pseudo-MidiDriver" and place it in between your MidiParser and the output MidiDriver. The MidiParser will send events to your pseudo-MidiDriver, which in turn must send them to the output MidiDriver (or do whatever special handling is required). Make sure to implement all functions which are necessary for proper functioning of the parser and forward the calls to the real driver (even if you do not want to customize the functionality).
To specify the MidiDriver to send music output to, use the MidiParser::setMidiDriver method.
STEP 4: Specify the onTimer call rate. MidiParser bases the timing of its parsing on an external clock. Every time MidiParser::onTimer is called, a bit more music is parsed. You must specify how many microseconds will occur between each call to onTimer, in order to ensure an accurate music tempo.
To set the onTimer call rate, in microseconds, use the MidiParser::setTimerRate method. The onTimer call rate will typically match the timer rate for the output MidiDriver used. This rate can be obtained by calling MidiDriver::getBaseTempo.
STEP 5: Load the music. MidiParser requires that the music data already be loaded into memory. The client code is responsible for memory management on this block of memory. That means that the client code must ensure that the data remain in memory while the MidiParser is using it, and properly freed after it is no longer needed. Some MidiParser variants may require internal buffers as well; memory management for those buffers is the responsibility of the MidiParser object.
To load the music into the MidiParser, use the MidiParser::loadMusic method, specifying a memory pointer to the music data and the size of the data. (NOTE: Some MidiParser variants don't require a size, and 0 is fine. However, when writing client code to use MidiParser, it is best to assume that a valid size will be required.)
Convention requires that each implementation of MidiParser::loadMusic automatically set up default tempo and current track. This effectively means that the MidiParser will start playing as soon as timer events start coming in. If you want to start playback at a later point, you can specify the mpDisableAutoStartPlayback property. You can then specify the track and/or starting point using setTrack, jumpToTick or jumpToIndex, and then call startPlaying to start playback.
STEP 6: Activate a timer source for the MidiParser. The easiest timer source to use is the timer of the output MidiDriver. You can attach the MidiDriver's timer output directly to a MidiParser by calling MidiDriver::setTimerCallback. In this case, the timer_proc will be the static method MidiParser::timerCallback, and timer_param will be a pointer to your MidiParser object.
This configuration only allows one MidiParser to be driven by the MidiDriver at a time. To drive more MidiParsers, you will need to create a "pseudo-MidiDriver" as described earlier, In such a configuration, the pseudo-MidiDriver should be set as the timer recipient in MidiDriver::setTimerCallback, and could then call MidiParser::onTimer for each MidiParser object.
STEP 7: Music shall begin to play! Congratulations! At this point everything should be hooked up and the MidiParser should generate music. You can pause playback and resume playing from the point you left off using the pausePlaying and resumePlaying functions. (Note that MIDI does not pause very well and active notes will be missing when you resume playback.) You can also "pause" the MidiParser simply by not sending timer events to it. You can stop playback using the stopPlaying function; you can then later play the track again from the start using startPlaying (or select a new track first using setTrack). You can call MidiParser::unloadMusic to permanently stop the music. (This method resets everything and detaches the MidiParser from the memory block containing the music data.)
anonymous enum |
Configuration options for MidiParser The following options can be set to modify MidiParser's behavior.
|
inlineprotectedvirtual |
Called before starting playback of a track. Can be implemented by subclasses if they need to perform actions at this point.
|
inlineprotected |
Platform independent BE uint32 read-and-advance. This helper function reads Big Endian 32-bit numbers from a memory pointer, at the same time advancing the pointer.
|
inlineprotected |
Platform independent LE uint16 read-and-advance. This helper function reads Little Endian 16-bit numbers from a memory pointer, at the same time advancing the pointer.
|
inlinevirtual |
Returns the size in bytes of the MIDI data in the specified stream, or -1 if the size could not be determined. The MIDI data must be in the format handled by the MidiParser subclass that this method is called on. Not every MidiParser subclass has an implementation of this method.
Reimplemented in MidiParser_SMF, and AGOS::MidiParser_SimonWin.
bool MidiParser::startPlaying | ( | ) |
Start playback from the current position in the current track, or at the beginning if there is no current position. If the parser is already playing or there is no valid current track, this function does nothing.
void MidiParser::stopPlaying | ( | ) |
Stops playback. This resets the current playback position.
void MidiParser::pausePlaying | ( | ) |
Pauses playback and stops all active notes. Use resumePlaying to continue playback at the current track position; startPlaying will do nothing if the parser is paused. stopPlaying, unloadMusic, loadMusic and setTrack will unpause the parser. jumpToTick and jumpToIndex do nothing while the parser is paused. If the parser is not playing or already paused, this function does nothing. Note that isPlaying will continue to return true while playback is paused. Not every parser implementation might support pausing properly.
void MidiParser::resumePlaying | ( | ) |
Resumes playback at the current track position. If the parser is not paused, this function does nothing.
|
inlinevirtual |
Returns true if the active track has a jump point defined for the specified index number. Can be implemented for MIDI formats with support for some form of index points.
|
inlinevirtual |
Stops playback and resumes it at the position defined for the specified index number. Can be implemented for MIDI formats with support for some form of index points.
|
protected |
Used for "Smart Jump" and MIDI formats that do not include explicit Note Off events.
Maintains expiration info for up to 32 notes.
|
protected |
The next event to transmit. Events are preparsed so each event is parsed only once; this permits simulated events in certain formats.
|
protected |
The source number to use when sending MIDI messages to the driver. When using multiple sources, use source 0 and higher. This must be used when source volume or channel locking is used. By default this is -1, which means the parser is the only source of MIDI messages and multiple source functionality is disabled.