class MidiScheduler

A MIDI interface providing scheduling facilities. More...

Contains pure virtuals
Full nameTSE3::MidiScheduler
Definition#include <MidiScheduler.h>
InheritsTSE3::Notifier [public ]
List of all Methods
Annotated List
Files
Globals
Hierarchy
Index

Public Methods

Protected Methods

Protected Members


Detailed Description

The MidiScheduler provides the interface to the MIDI system (be it a software or hardware driver). On top of this it implements a timed stream for reception and transmission of MidiEvents.

This is the TSE3 platform independant interface to the underlying MIDI hardware. (In fact, it might not even be hardware, this could be the interface to a software synth).

To create a MidiScheduler you will need to use an object derived from this class. The MidiSchedulerFactory classes help you to create this.

Ports

The interface provides a number of MIDI "ports" (which may be added to or removed at run time, depending on the type of system TSE3 is running on).

These ports are referenced by a unique number. You read or write data from/to ports via the MidiEvent data type. The MidiEvent has a "port" number field which identifies which port the command is destined for.

As well as having numbers, the API allows you read a port name. You can also set a "preferred" name for each port. This allows the user to give a more useful name to the port (perhaps for use in a GUI).

Port numbers are not necessarily contiguous and can have any value - this will depend on the phyiscal MIDI device in use. In order to find out what port numbers are in use use the following APIs:

Note that there are two reserved special port numbers:

Time

As well as managing the MIDI ports, the MidiScheduler looks after a clock that it used to schedule delivery of these events. The API allows you to start/stop/move the time line, as well as set the clock's tempo.

The MidiScheduler can notify back to its clients if the timestream has moved, or been started/stopped by a device on the MIDI connection.

If the timestream is 'stopped' the MidiScheduler class will still remember a 'current' clock value, informally known as the 'resting clock'. When you ask for the time (see MidiScheduler::clock) you will be given this 'resting clock'.

Remote control

Additionally, the MidiScheduler provides a MIDI remote control facility, where certain MIDI notes can trigger transport start/stop.

The keys that are set to be "remote controls" will not be passed up through the API in a MidiEvent if remote control is enabled.

If you switch this facility on, you have to bear in mind that the only time the MidiScheduler gets a chance to investigate the input note stream is as you consume events from it. (For simplicity it doesn't have a clever background thread implementation).

Therefore, read events from the MidiScheduler in a timely manner.

That's all folks

The MidiScheduler class does not incorporate a lot of other fancy utilities, like channel/port remapping, for example. Higher level TSE3 components do this, for example the MidiMapper class. The Transport class ties this all together.

Implementing a MidiScheduler

If you are porting TSE3 to another platform, you will need to implement this MidiScheduler interface. It uses the template method pattern (GoF book) - you have to implement a few member functions and the base MidiScheduler class looks after most of the logic.

These template methods are in the protected section near the end and are all pretty easy to understand.

See also: MidiEvent

 MidiScheduler ()

MidiScheduler

 ~MidiScheduler ()

~MidiScheduler

[virtual]

If the MidiScheduler is running, it will be stopped.

const char * implementationName ()

implementationName

[const]

Returns a string describing the particular implementation of the MidiScheduler interface.

Returns: Implementation name string

size_t  numPorts ()

numPorts

[const]

Returns the number of port addressable by this MidiScheduler. If this changes at any point the MidiSchedulerListener::MidiScheduler_Ports event is raised.

When a port is created it is assigned a "number" - this could be any positive integer. This is its permanent reference ID. It is the port number that is specified in the MidiCommand or MidiEvent.

Returns: Number of MIDI ports this MidiScheduler provides

bool  validPort (int port)

validPort

[const]

Returns whether the specified port number is valid.

Parameters:
portMIDI port number

Returns: true if there is currently a port with that number

int  portNumber (size_t index)

portNumber

[const]

Returns the "index"th port, if they were held in a list. So, to get the first port number set index to 0, for the seonc give index 1, and so on.

If you specify a value of numPorts or above, the result is undefined.

Parameters:
indexvalue

Returns: Port number with specified index

See also: numPorts, portNumbers, numberToIndex

size_t  numberToIndex (int number)

numberToIndex

[const]

Converts a port number to an index. This is the reverse operation of portNumber.

If the port number is not valid, this will return zero.

Parameters:
numberPort number

Returns: "Index" of this port

See also: portNumber

void  portNumbers (std::vector &numbers)

portNumbers

[const]

Empties the contents of the supplied std::vector, and inserts into it every available port number.

Parameters:
numbersstd::vector which is given the current MIDI port numbers

const char * portName (int port)

portName

[const]

Returns a string describing the port with the given number.

If the port number is invalid the port name will be a suitable "invalid" string, not a zero pointer.

Parameters:
portMIDI port number

Returns: Port name string

const char * portType (int port)

portType

[const]

Returns a string describing the type of the port with the given number.

If the port number is invalid the port name will be a suitable "invalid" string, not a zero pointer.

Parameters:
portMIDI port number

Returns: Port type string

bool  portReadable (int port)

portReadable

[const]

Returns whether or not the port is readable. If this function returns false, then you will never receive an event from rx from this port.

If the port number is invalid, this will return false.

Parameters:
portMIDI port number

Returns: Whether MidiEvent objects are readable from this port

See also: portWritable

bool  portWriteable (int port)

portWriteable

[const]

Returns whether or not the port is writeable. If this function returns false, then you can't send MidiEvent objects out via this port (with tx). If you specify this port number in a MidiEvent given to tx, the MidiEvent will be ignored.

If the port number is invalid, this will return false.

Parameters:
portMIDI port number

Returns: Whether MidiEvent objects are readable from this port

See also: portReadable

bool  portInternal (int port)

portInternal

[const]

Returns whether or not the port is "internal". If this function returns false, then MidiEvent objects sent here will be transmitted vai MIDI to an external device.

If the port number is invalid, this will return false.

Parameters:
portMIDI port number

Returns: Whether the port connects to an internal or external MIDI device.

See also: defaultInternalPort, defaultExternalPort

int  defaultInternalPort ()

defaultInternalPort

[const]

Returns a "default" internal port number. If you don't know which internal port to use, choose this one! If there are no internal ports, this returns MidiCommand::NoPort.

(The default internal port will in fact be the first registered internal port from the platform implementation of MidiScheduler).

Returns: Default internal port, or MidiCommand::NoPort

See also: defaultExternalPort

int  defaultExternalPort ()

defaultExternalPort

[const]

Returns a "default" external port number. If you don't know which external port to use, choose this one! If there are no external ports, this returns MidiCommand::NoPort.

(The default external port will in fact be the first registered external port from the platform implementation of MidiScheduler).

Returns: Default external port, or MidiCommand::NoPort

See also: defaultInternalPort

void  start (Clock startTime)

start

Start the scheduler clock running, set to the given time.

Parameters:
startTimeThe time to start the scheduler at

void  start ()

start

Start the scheduler clock at the current 'resting time' (as set by a call to stop).

void  stop (Clock stopTime = -1)

stop

Stop the scheduler clock and flush the Tx buffer instantaneously. The stopTime is remembered as a 'resting time' and subsequent calls to clock will return this value.

Parameters:
stopTimeThe time at which to stop (-1 means immediately)

bool  running ()

running

[const]

Enquire whether the scheduler clock is running.

Returns: Whether the clock is running

void  moveTo (Clock moveTime, Clock newTime)

moveTo

Without stopping, move the scheduler clock to the given time newTime at time moveTime. Any further calls to clock() after this will return times in the new time line.

If the scheduler is not running, this just sets the 'resting time'.

Parameters:
moveTimeTime at which to perform the move
newTimeTime to move to

void  moveTo (Clock moveTime)

moveTo

Without stopping, move the scheduler clock immediately.

Parameters:
moveTimeTime to move to

Clock  clock ()

clock

Read the scheduler clock.

This works whether the scheduler is running or not. If it has been stopped then it returns the time the scheduler was stopped at (or has since been moved to) - the 'resting time'.

Returns: MidiScheduler time value

int  msecs ()

msecs

Read the scheduler clock in milliseconds.

Returns: MidiScheduler time value in milliseconds

int  tempo ()

tempo

[const]

Read the tempo.

Returns: Current tempo

See also: setTempo

void  setTempo (int newTempo, Clock changeTime)

setTempo

Set the tempo.

Parameters:
newTempoThe new tempo value in beats per minute (1-256)
changeTimeOnly used if the scheduler is running to indicate when the tempo change occurs. Any further calls to clock() before changeTime will return bogus values, as they will be in the new tempo's time scale.

See also: clock, tempo

bool  eventWaiting ()

eventWaiting

Enquire whether there is any data in the input buffer.

This function will return true if there has been any input from any of the readable MIDI ports that you haven't yet read.

Returns: Whether there is any input data ready to be processed

MidiEvent  rx ()

rx

Return and remove a MidiEvent from scheduler receive buffer.

Note that it is possible for this function to return a MidiEvent with MidiCommand_Invalid status. All TSE3 classes must be able to cope with these events flying around.

If there is no event waiting (see eventWaiting()) then this API will return an invalid MidiEvent.

Returns: A MidiEvent containing the data. If the status of the MidiCommand is zero, there wasn't a whole MidiCommand in the buffer.

void  tx (MidiCommand mc)

tx

Transmit a MidiCommand immediately.

This bypasses every other scheduled MidiEvent and is sent to the hardware before them.

Parameters:
mcThe MidiCommand to transmit

void  tx (MidiEvent event)

tx

Adds an event to scheduler transmit buffer, to be transmitted at the appropriate time.

Events must be given to this method in time order. This is regardless of what port it is destined for.

Note: if this is a MidiCommand_NoteOn, then the matching MidiCommand_NoteOff part of the MidiEvent is ignored, you have to schedule that separately.

If the clock is not running, the event will not be scheduled and will just be ignored.

Parameters:
eventMidiEvent to schedule to transmission

void  txSysEx (int port, const unsigned char *data, size_t size)

txSysEx

bool  remoteControl ()

remoteControl

[const]

Read the remote control status.

Returns: true if remote control enabled, false if disabled

See also: setRemoteControl

void  setRemoteControl (bool s)

setRemoteControl

Sets the remote control status.

Parameters:
sNew remote control status

See also: remoteControl

bool  consumeRemoveEvents ()

consumeRemoveEvents

[const]

unsigned int  startNote ()

startNote

[const]

Returns the start note. When pressed this note will cause the scheduler to start.

Returns: Start note

See also: setStartNote

void  setStartNote (unsigned int n)

setStartNote

Sets the start note.

Parameters:
nNew start note

See also: startNote

unsigned int  stopNote ()

stopNote

[const]

Returns the stop note. When pushed with the shift note held down, this note will cause the scheduler to stop.

Returns: Stop note

See also: setStopNote

void  setStopNote (unsigned int n)

setStopNote

Sets the stop note.

Parameters:
nNew stop note

See also: stopNote

 MidiScheduler (const MidiScheduler &)

MidiScheduler

[protected]

MidiScheduler & operator= (const MidiScheduler &)

operator=

[protected]

Reimplemented from Notifier.

const char * impl_implementationName ()

impl_implementationName

[protected const pure virtual]

Implementation functions

To implement a MidiScheduler for a particular platform you implement the functions below. They are called by the public MidiScheduler functions, using the Template Method design pattern (GoF book).

You can assume that you will never be called with an invalid port number, and a number of other parameters are guaranteed to be valid, as documented.

Functions like impl_start and impl_stop are more requests than commands. The MidiScheduler will not assume that the clock has started until the implementation calls the clockStarted function.

You shouldn't need to perform any notifications, however, you do need to take care to call the next block of protected APIs to inform the MidiScheduler code what's going on. If you don't do this, the MidiScheduler class will not behave properly.

Note that in your destructor you will also want to put the following code first: // if playing, stop first! if (MidiScheduler::running()) stop();

Your implementation of the MidiScheduler will have to deal with timing. You will be sent events (via impl_tx) IN TIME ORDER, for some time in the not-too distant future (as usually specified by the Transport class's look-ahead). You have to do the work of sending the event at the exact timer tick. ***************************************************************

const char * impl_portName (int port)

impl_portName

[protected const pure virtual]

const char * impl_portType (int port)

impl_portType

[protected const pure virtual]

bool  impl_portReadable (int port)

impl_portReadable

[protected const pure virtual]

bool  impl_portWriteable (int port)

impl_portWriteable

[protected const pure virtual]

void  impl_start (Clock clock)

impl_start

[protected pure virtual]

Current state is guaranteed to be stopped.

Don't forget to call clockStarted if the start succeeds.

void  impl_stop (Clock clock)

impl_stop

[protected pure virtual]

Current state is guaranteed to be started. The clock value will not ever be -1.

Don't forget to call clockStopped if the start succeeds.

void  impl_moveTo (Clock moveTime, Clock newTime)

impl_moveTo

[protected pure virtual]

Guaranteed running.

Don't forget to call clockMoved if the start succeeds.

Clock  impl_clock ()

impl_clock

[protected pure virtual]

Guaranteed running.

int  impl_msecs ()

impl_msecs

[protected pure virtual]

Guaranteed running.

void  impl_setTempo (int tempo, Clock changeTime)

impl_setTempo

[protected pure virtual]

Guaranteed tempo > 0. For the duration of this method, tempo() will return the old tempo value.

Don't forget to call clockMoved if the start succeeds.

bool  impl_eventWaiting ()

impl_eventWaiting

[protected pure virtual]

MidiEvent  impl_rx ()

impl_rx

[protected pure virtual]

You'll buffer all MidiEvents recieved. Returns the top buffered MidiEvent (or MidiEvent() if none are buffered).

void  impl_tx (MidiCommand mc)

impl_tx

[protected pure virtual]

Send this MidiCommand NOW. Bypass any queued events.

Timer may be running or not.

void  impl_tx (MidiEvent mc)

impl_tx

[protected pure virtual]

Puts a MidiEvent on the queue ready for transmission. You will be given MidiEvents in time order, so your buffer can be a FIFO rather than a time-ordered queue.

The MidiEvent will be for some time in the future, you will have to arrange to transmit it at the appropriate point.

Timer may be running or not.

void  impl_txSysEx (int port, const unsigned char *data, size_t size)

impl_txSysEx

[protected pure virtual]

Send a sysex package now, bypassing the transsion queue.

int  addPort (int portIndex, bool isInternal, int requestedPort = 0)

addPort

[protected]

Tells the MidiScheduler that your implementation has an available MIDI port. This is safe to call in your ctor.

You can suggest a port number for the MidiScheduler, but you are not necessarily guaranteed to get it.

If you don't want to go to the effort of constructing a port number, always specify zero.

This function returns the actual port number you have been assigned.

Parameters:
portIndexYou implementation's port reference
isInternalSpecify true if this is an internal sound generator, or false if it is a MIDI link to an external device.
requestedPortThe port number you'd like to present to the user

void  removePort (int portIndex)

removePort

[protected]

You don't need to call removePort in your implementation's destructor.

void  clockStarted (Clock startTime)

clockStarted

[protected]

void  clockStopped (Clock stopTime)

clockStopped

[protected]

void  clockMoved (const Clock moveTime, Clock newTime)

clockMoved

[protected]

void  tempoChanged (int tempo, Clock changeTime)

tempoChanged

[protected]

Clock startClock

startClock

[protected]

int  clockToMs (Clock time)

clockToMs

[protected]

An internal method to convert Clock values to millisecond times.

Clock  msToClock (int ms)

msToClock

[protected]

An internal method to convert millisecond time values to Clocks.