The BMidiStore
class defines a MIDI recording and playback mechanism. The
MIDI messages that a
BMidiStore
object receives (at its input) are stored
as events in an event list, allowing a captured performance to be played
back later. The object can also read and write—or import and
export—standard MIDI files.
The ability to record a MIDI performance is vested in
BMidiStore
's
MIDI hook functions (NoteOn(), NoteOff(), etc.). When a MIDI hook is invoked,
the function fabricates a discrete event based on the data it has
received in its arguments, and adds the event to its event list. You
don't need to tell a
BMidiStore
to start recording; it can record from
the moment it's constructed.
For example, to record a performance from an external MIDI keyboard, you
connect a
BMidiStore
to a
BMidiPort
object and then tell the
BMidiStore
to start:
/* Record a keyboard performance. */BMidiStore
MyStore
;BMidiPort
MyPort
;MyPort
.Open
(...);MyPort
.Connect
(MyStore
);MyPort
.Start
(); /* Start playing... */
At the end of the performance, you tell the
BMidiPort
to stop:
MyPort
.Stop()
;
Events are added to a
BMidiStore
's
event list immediately upon arrival.
Each event is given a timestamp as it arrives; the value of the timestamp
is the value of the time argument that was passed to the MIDI hook
function by the "upstream" object's spray function. There's no guarantee
that the time arguments of successive MIDI events will be in
chronological order. To ensure that the events are properly ordered, you
should call
SortEvents()
before you read from the list (note that writing to a
MIDI file automatically sorts the list).
BMidiStore
's
input functions don't call
SnoozeUntil()
: A
BMidiStore
writes to its event list as soon as it gets a new message, it doesn't
wait until the time indicated by the time argument.
You can't. If you make a mistake while you're recording (for example) and
want to try again, you can simulate emptying the object by disconnecting
the input to the
BMidiStore
,
destroying the object, making a new one, and re-connecting. For example:
Editing the events in the event list is less than impossible (were such a
state possible). You can't do it, and you can't simulate it, at least not
with the default implementation of
BMidiStore
.
If you want to edit MIDI data, you have to provide your own
BMidi
-derived
class.
To "play" a
BMidiStore
's
list of events, you call the object's
Start()
function. As described in the
BMidi
class specification,
Start()
invokes
Run()
.
BMidiStore
's
Run()
reads events in the order that they appear in
the event list, and sprays the appropriate messages to the connected
objects. You can interrupt a
BMidiStore
playback by calling
Stop()
;
uninterrupted, the object will stop by itself after it has sprayed the
last event in the list.
The events' timestamps are used as the time arguments in the spray
functions that are called from within
Run()
.
But with a twist: The time
argument that's passed in the first spray call (for a given performance)
is always B_NOW
; subsequent time arguments are re-computed to maintain
the correct timing in relation to the first event. In other words, when
you tell a
BMidiStore
to start playing, the first event is performed
immediately regardless of the actual value of its timestamp.
You can tell the
BMidiStore
to begin playing from somewhere in the middle of the list by calling
SetCurrentEvent()
before starting the playback.
The function takes an index into the list.
If you want to start playing from a particular time offset into the event
list, you first have to figure out which event lies at that time. To do
this, you ask for the event that occurs at or after the time offset (in
milliseconds) through the
EventAtDelta()
function. The value that's
returned by this function is suitable as the argument to
SetCurrentEvent()
.
Keep in mind that
EventAtDelta()
returns the index of the first event at
or after the desired offset. If you need to know the actual offset of the
winning event, you can pass its index to
DeltaOfEvent()
:
longfirstEvent
=MyStore
->EventAtDelta
(3000); longactualDelta
=MyStore
->DeltaOfEvent
(firstEvent);
You add events to a BMidiStore's event list by reading, or importing, a
Standard MIDI File through the
Import()
function. You can import any number of files into the same
BMidiStore
object. After you import a file, the event list is automatically sorted.
One thing you shouldn't do is import a MIDI file into a
BMidiStore
that contains events that were previously recorded from a
BMidiPort
(in an attempt to mix the file and the recording). Nor does the reverse work:
You can't import a file and then record from a
BMidiPort
.
The file's timestamps are incompatible with those that are generated for events that
are received from the
BMidiPort
;
the result certainly won't be satisfactory.
To write the event list as a MIDI file, you call
BMidiStore
's
Export()
function: