|
The TSE3 library has a single header file providing definitions for the
elements of the standard MIDI specification. This is the file
Midi.h
(see the reference at the bottom of this page).
It defines the following elements:
Clock
data value type.Clock::PPQN
. (A quarter note is another
name for a crotchet.)Event
template data value type.Clock
s.MidiCommands
and other similar
definitions.MidiCommand_NoteOn
, system commands, controller
values etc. These are: MidiCommands
,
MidiSystemCommands
, and MidiControlChanges
.
MidiCommand
data value class.MidiEvent
data value class.MidiCommand
class, but associates
it with an event time in Clocks.
Note that here we do not use the Event
template
class as a definition. This is because the MidiEvent
contains a second MidiCommand
, used if the first is a
MidiCommand_NoteOn
. If so, the second command holds the
related MidiCommand_NoteOff
event. This can be used by
the TSE3 library to ensure that all MIDI note off events are sent for
any scheduled note on event.
TSE3MetaMidiCommand
definitions.Used together, these definitions describe standard MIDI data, scheduled to some clock source. The TSE3 song components generate musical data in this format.
The kdoc documentation describes these classes. You can find further information about them there.
Each component that comprises a Song can produce some sort of stream of
MIDI data that needs to be scheduled to a timed output. To simplify this
process they implement the Playable
interface. This
makes the Song structure use a form of the composite design pattern
(GoF book).
The Playable
PlayableIterator
that can iterate over the
MIDI data in the output stream. This is a form of the iterator
design pattern.
Each different kind of Playable
object provides it's own
implementation of the PlayableIterator
interface that knows
how to generate the MIDI events.
The user can ignore the individual song components Playable
interface, and meerly use the Song
's
PlayableIterator
that will in turn use the
Playable
iterators of all sub components to create a MIDI data
stream for the whole song.
+------------\ +------------+ creates +--------------------+ | Interface |_\ | Playable |--------------| PlayableIterator | | | +------------+ +--------------------+ +------------- + ^ ^ | | +------------\ | | | Example |_\ +------------+ +--------------------+ | Implemenation | | Song | | SongIterator | +---------------+ +------------+ +--------------------+
The data generated by a PlayableIterator
object is in the form
described above, as defined by the Midi.h
header file.
System exclusive MIDI data is a particular nuisance. All sequencer systems have this problem. If you don't care about, or know about system exclusive MIDI data (or sysex data) then you can skip this section.
Any other form of MIDI data, for example MidiCommand_NoteOn
events and the like, can be interspersed in any order. They are sent
in whole atomic units (the MidiEvent
or
MidiCommand
classes). They are easy to handle and stream around
the system in PlaybaleIterator
objects.
However, sysex events break this simple atomic data structure. They can be of arbitrary size, with a single start of sysex system byte at the start, and an end of sysex status byte at the end.
So how do we stream these around using the PlayableIterator
class? Carefully, is the answer.
Sysex data has been designed to fit into the Playable
architecture rather than be handled as a special case. However, there are
certain restrictions involved in their use.
The start of a sysex block is naturally defined by a
MidiCommand
with
MidiComand_System
status byte and reason code
MidiSystem_SysExStart
. The data1
byte contains
the first data byte. data2
is not used. If the event
is held in a MidiEvent
(rather than a single
MidiCommand
- this will be true if it is streamed from a
PlayableIterator
) then the second (note off) field is not
used to hold extra values.
The next MidiCommand
may need to be another sysex data byte. In
this case the same status information is put in the MidiComand
- although the playing
MidiScheduler
object knows not to send this again.
data1
contains this next data byte. More sysex data bytes
may follow.
The stream carries on in this manner until the end of the sysex data block,
when a MidiComand
containing the
MidiSystem_SysExEnd
status information is put in the stream.
As stated above, sysex data cannot be rearranged. Nor can other MIDI
events occur in the middle of them (you cannot shove a note on in the
middle of a block of sysex). For this reason any
PlayableIterator
must take care to give each sysex
MidiCommand
exactly the same clock time to ensure no other events can get into
the middle of the sysex stream.
The standard TSE3 PlayableIterator
objects are designed in
such a way that if all events have the same event time, events from
other sources will not interrupt the stream.
Midi.h
for
descriptions of the TSE3 representation of MIDI data.
Playable.h
for
the definition of the Playable
class.
|