ScummVM API documentation
MidiParser Class Referenceabstract

#include <midiparser.h>

Inheritance diagram for MidiParser:
MidiParser_QT MidiParser_SMF Sci::MidiParser_SCI Sherlock::MidiParser_SH Ultima::Nuvie::MidiParser_M AGOS::MidiParser_GMF AGOS::MidiParser_SimonWin

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 MidiParsercreateParser_SMF (int8 source=-1)
 
static MidiParsercreateParser_XMIDI (XMidiCallbackProc proc=defaultXMidiCallback, void *refCon=0, int source=-1)
 
static MidiParsercreateParser_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
 

Detailed Description

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:

  • loadMusic
  • parseNextEvent

In addition to the above functions, the derived class may also override the default MidiParser behavior for the following methods:

  • resetTracking
  • getTick
  • jumpToIndex
  • hasJumpIndex
  • allNotesOff
  • unloadMusic
  • property
  • processEvent
  • onTrackStart
  • sendToDriver
  • sendMetaEventToDriver
  • setMidiDriver

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.)

Member Enumeration Documentation

◆ anonymous enum

anonymous enum

Configuration options for MidiParser The following options can be set to modify MidiParser's behavior.

Enumerator
mpAutoLoop 

Sets auto-looping, which can be used by lightweight clients that don't provide their own flow control.

mpSmartJump 

Sets smart jumping, which intelligently expires notes that are active when a jump is made, rather than just cutting them off.

mpCenterPitchWheelOnUnload 

Center the pitch wheels when unloading music in preparation for the next piece of music.

mpSendSustainOffOnNotesOff 

Sends a sustain off event when a notes off event is triggered. Stops hanging notes.

mpDisableAllNotesOffMidiEvents 

Prevent sending out all notes off events on all channels when playback of a track is stopped. This option is useful when multiple sources are used; otherwise stopping playback of one source will interrupt playback of the other sources. Any active notes registered by this parser will still be turned off.

mpDisableAutoStartPlayback 

Does not automatically start playback after parsing MIDI data or setting the track. Use startPlaying to start playback. Note that not every parser implementation might support this.

Member Function Documentation

◆ onTrackStart()

virtual void MidiParser::onTrackStart ( uint8  track)
inlineprotectedvirtual

Called before starting playback of a track. Can be implemented by subclasses if they need to perform actions at this point.

◆ read4high()

uint32 MidiParser::read4high ( byte *&  data)
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.

◆ read2low()

uint16 MidiParser::read2low ( byte *&  data)
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.

◆ determineDataSize()

virtual int32 MidiParser::determineDataSize ( Common::SeekableReadStream stream)
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.

◆ startPlaying()

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.

◆ stopPlaying()

void MidiParser::stopPlaying ( )

Stops playback. This resets the current playback position.

◆ pausePlaying()

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.

◆ resumePlaying()

void MidiParser::resumePlaying ( )

Resumes playback at the current track position. If the parser is not paused, this function does nothing.

◆ hasJumpIndex()

virtual bool MidiParser::hasJumpIndex ( uint8  index)
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.

◆ jumpToIndex()

virtual bool MidiParser::jumpToIndex ( uint8  index,
bool  stopNotes = true 
)
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.

Member Data Documentation

◆ _hangingNotes

NoteTimer MidiParser::_hangingNotes[32]
protected

Used for "Smart Jump" and MIDI formats that do not include explicit Note Off events.

Maintains expiration info for up to 32 notes.

◆ _nextEvent

EventInfo MidiParser::_nextEvent
protected

The next event to transmit. Events are preparsed so each event is parsed only once; this permits simulated events in certain formats.

◆ _source

int8 MidiParser::_source
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.


The documentation for this class was generated from the following file: