MKInstrument
ClassMKInstrument
s are the agents through
which MKNote
objects are realized. Since
realization is the ultimate destiny of a MKNote
object, the MKInstrument
class is the ultimate
focus of a MusicKit performance: Every performance involves at least
one MKInstrument
.
At the heart of an MKInstrument
is
its realizeNote:fromNoteReceiver:
method. The MKInstrument
class itself is
abstract and doesn't implement this method; it's the responsibility of
each MKInstrument
subclass to provide an
implementation of realizeNote:fromNoteReceiver: to establish the
manner in which instances of the subclass realize
MKNote
s. The
MusicKit includes a number of
MKInstrument
subclasses (and
pseudo-MKInstrument
s) that realize
MKNote
s by synthesizing them on the
DSP or an external MIDI
synthesizer, and that store MKNote
s by adding
them to a MKPart
or
MKScore
or by writing them to a file.
One of the principals of MKInstrument
design is that an MKInstrument
doesn't create
the MKNote
s that it realizes. Instead, it
realizes MKNote
s that are sent to it by another
object or by your application. The machinery by which an
MKInstrument
receives
MKNote
s is well defined by the MusicKit; unlike
that for MKNote
-realization, you don't reinvent
the MKNote
-reception mechanism for each
MKInstrument
subclass.
The following sections examine the
MKInstrument
class according to its two tasks:
receiving and realizing MKNote
s. The latter
topic centers upon descriptions of the
MKInstrument
subclasses included in the
MusicKit, followed by an example of
MKInstrument
subclass design.
MKNote
sAn MKInstrument
receives
MKNote
s through the same method that defines
their realization, realizeNote:fromNoteReceiver:.
However, you don't send MKNote
s to an
MKInstrument
by invoking this method directly;
instead, you send the MKNote
s to one of the
MKInstrument
's
MKNoteReceiver
objects which, in turn, invokes
realizeNote:fromNoteReceiver:
for you.
MKNoteReceiver
is an auxiliary
class that acts as a “MKNote
port”
for an MKInstrument
. This is manifested as the
three features that a MKNoteReceiver
brings to
an MKInstrument
:
It provides methods through which an
MKInstrument
receives
MKNote
s (in other words, methods that invoke
MKInstrument
's realizeNote:fromNoteReceiver:).
It lets you control the stream of
MKNote
s into an
MKInstrument
by allowing you to toggle its
ability to forward MKNote
s to an
MKInstrument
.
It acts as a jack into which you can plug a
MKPerformer
object's
MKNoteSender
. This connects the
MKPerformer
that owns the
MKNoteSender
to the
MKInstrument
that owns the
MKNoteReceiver
, such that the
MKNote
s generated by the
MKPerformer
are automatically delivered to and
realized on the connected MKInstrument
. The
methods by which you marry a MKNoteReceiver
to
a MKNoteSender
are described later in the
section on MKPerformer
s and
MKNoteSender
s.
Creating and adding MKNoteReceiver
s is
usually part of the design of the MKInstrument
subclass, although some subclasses require you to create and add
MKNoteReceiver
s yourself. In either case, the
method by which a MKNoteReceiver
is added to an
MKInstrument
is the same:
MKInstrument
's addNoteReceiver:.
While an MKInstrument
can own more than one
MKNoteReceiver
, a single
MKNoteReceiver
can belong to only one
MKInstrument
: An invocation of addNoteReceiver:
removes the argument (the MKNoteReceiver
) from
its current owner.
The fundamental method through which a
MKNoteReceiver
itself receives
MKNote
s―and thereby forwards them to its
owning MKInstrument
―is called receiveNote:. However, before you can send
receiveNote: to a
MKNoteReceiver
, you must be able to locate the
object. If you created and added
MKNoteReceiver
s to an
MKInstrument
yourself, finding these objects
shouldn't pose a problem―you made them, you should know where
they are. But keep in mind that many
MKInstrument
s create and add
MKNoteReceiver
s for you. Any
MKInstrument
's
MKNoteReceiver
s can be retrieved as a
NSArray
object by sending the noteReceivers message to the
MKInstrument
; members of this
NSArray
can then be sent the receiveNote: message. As a convenience, the
first MKNoteReceiver
in this
NSArray
can be retrieved through the noteReceiver method. This is particularly
handy for MKInstrument
s that define only one
MKNoteReceiver
, or in situations where you
don't care which MKNoteReceiver
you send the
MKNote
to. In the following example, an
instance of MKSynthInstrument
is created and a
MKNote
is sent to its
MKNoteReceiver
. MKSynthInstrument
is a
subclass of MKInstrument
that realizes
MKNote
s by synthesizing them on the DSP; it
creates a single MKNoteReceiver
as part of its
init method:
/* Create a MKSynthInstrument and send it a MKNote (assumed to exist). */ aSynthIns = [[MKSynthInstrument alloc] init]; [MKConductor lockPerformance]; [[aSynthIns noteReceiver] receiveNote:aNote]; [MKConductor unlockPerformance]; |
You'll note the presence of the
MKConductor
class methods lockPerformance and unlockPerformance; invocations of these two
methods should bracket all invocations of receiveNote: (with some exceptions, as noted in
the sections on MKConductor
s and
MKPerformer
s, later in this chapter). Briefly,
these methods ensure that your cast of characters are in synch and
primed for timely MKNote
realization. A fuller
explanation of lockPerformance and
unlockPerformance is given in
the Section called Locking the Performance later in this chapter.
You can create and add any number of
MKNoteReceiver
s to the
MKInstrument
s that you allocate, even if these
MKInstrument
s create
MKNoteReceiver
s themselves. However, with some
exceptions―notably that described in the next
section―there isn't much reason to do so: One
MKNoteReceiver
is as good as the next. Any
number of MKNote
-producing
agents―whether MKPerformer
s or different
mechanisms in your application―can all send receiveNote: to the same
MKNoteReceiver
. In general, adding a gaggle of
MKNoteReceiver
s to an
MKInstrument
doesn't make the
MKInstrument
more interesting, it simply makes
it bigger.
MKNoteReceiver
You can throttle a MKNoteReceiver
's
ability to invoke realizeNote:fromNoteReceiver:
by sending it the squelch message.
To release this clench, you invoke unsquelch. Through this simple feature, you
can easily and quickly mute an
MKInstrument
.
While a MKNoteReceiver
is squelched, the
receiveNote: method ceases to
function: It immediately returns nil
without forwarding the argument MKNote
to the
MKNoteReceiver
's owner (unsquelched, receiveNote: returns self). Realization of the
MKNote
isn't merely deferred, it's abandoned
for good. You can determine whether a
MKNoteReceiver
is squelched by sending it the
isSquelched message: If the message
returns YES
, the object is squelched.
The squelch feature, in certain applications, argues for the use
of multiple MKNoteReceiver
s for a single
MKInstrument
. For example, you can create an
application in which MKNote
s are generated from
MIDI input and read from a MKPart
object at the
same time. If you feed these two MKNote
sources to the same MKInstrument
, you may want
to create and add two distinct MKNoteReceiver
s,
one for either source, so you can independently squelch the two
streams of MKNote
s.
An MKInstrument
is considered to be in
performance from the time that you send receiveNote: to any of its unsquelched
MKNoteReceiver
s until the performance is
over―in other words, until the
MKConductor
class receives the finishPerformance message. You can query an
MKInstrument
's performance status by sending it
the inPerformance message, where a
return of YES
signifies that the object is currently in
performance.
The performance status of an MKInstrument
is significant because some MKInstrument
methods aren't effective while the MKInstrument
is in performance. This can be a particularly devilish source of
confusion since the state of an MKInstrument
's
performance doesn't require a performance, in the larger sense, to be
in progress. Specifically, if you send receiveNote: to an
MKInstrument
's
MKNoteReceiver
before the
MKConductor
class receives startPerformance, the
MKInstrument
will, nonetheless, be considered
to be performing (and thus the aforementioned performance-status
dependent methods will have no effect).
MKNote
sThere are very few rules when it comes to realizing
MKNote
s: You can implement realizeNote:fromNoteReceiver:
to realize a MKNote
in almost any arbitrary
manner. As mentioned earlier, the MusicKit includes
MKInstrument
s that synthesize and store
MKNote
s as their forms of realization. For
many performance applications, the
MKInstrument
s provided by the MusicKit are
sufficient. The following sections describe these subclasses.
MKSynthInstrument
A MKSynthInstrument
is by far the most complicated
MKInstrument
; it realizes
MKNote
s by causing them to be synthesized on
the DSP. It operates on three basic principles:
Every instance of MKSynthInstrument
is associated
with a single subclass of MKSynthPatch
.
Before or during a performance, a MKSynthInstrument
object allocates (through requests to the MKOrchestra
) and manages
instances of its MKSynthPatch
subclass. These MKSynthPatch
objects
are used to synthesize the MKNote
s that the
MKSynthInstrument
receives through its
MKNoteReceiver
.
Following these principles, the primary decisions you need to
make regarding a MKSynthInstrument
are which MKSynthPatch
subclass to
assign it and which of two schemes it should use to allocate instances
of the MKSynthPatch
subclass.
MKSynthPatch
SubclassYou set a MKSynthInstrument
's MKSynthPatch
subclass by invoking
the setSynthPatchClass: method. As
the method's argument you specify one of the MKSynthPatch
classes
included in the MusicKit, or one of your own. If you have a
multiple-DSP system, the MKSynthInstrument
will allocate its
MKSynthPatch
objects on the first DSP that has sufficient available
resources. You can restrict allocation to a specific DSP by invoking
setSynthPatchClass:orchestra:,
passing an MKOrchestra
object as the second argument.
MKNote
:
If you use a MKSynthPatch
class included in the MusicKit, you must
import the file <musickit/synthpatches/ClassName.h>,
where ClassName is the name of the class you wish
to use. Alternatively, the file <musickit/synthpatches/synthpatches.h>
will import all the MusicKit MKSynthPatch
interface files.
The setSynthPatchClass: method
(and the ...orchestra: version)
checks its (first) argument to ensure that it's a class that inherits
from MKSynthPatch
, returning nil if it doesn't.
You can also change the synthPatchClass by sending it a mute note with a parameter synthPatch:. The value of this parameter should be the name of the class (a string.)
MKSynthInstrument
defines two allocation modes:
In automatic allocation mode
the MKSynthInstrument
allocates MKSynthPatch
objects as it receives
MKNote
s, tagging each MKSynthPatch
with the
note tag of the MKNote
for which it was
allocated. As it receives subsequent MKNote
s,
the MKSynthInstrument
compares each MKNote
's
note tag to those of the MKSynthPatch
es it has already allocated. If
it finds a match, the MKNote
is sent to that
MKSynthPatch
. If it doesn't match, the MKSynthInstrument
allocates a
new MKSynthPatch
.
In manual allocation mode the MKSynthPatch
objects are allocated all at once, before
MKNote
s begin to arrive. The number of
MKSynthPatch
es to allocate is set through the method setSynthPatchCount:. The number of
MKSynthPatch
es that are actually allocated may be less than the number
requested, depending on the availability of DSP resources at the time
that the message is sent. The method returns the number of
MKSynthPatch
es that were actually allocated.
By default, a MKSynthInstrument
is in automatic allocation mode.
Simply sending setSynthPatchCount:places it in manual mode. To set it back to automatic
mode, you send it the autoAlloc
message.
You can query a MKSynthInstrument
's
allocation mode by invoking allocMode, a method that returns one of the
following integer constants:
MKSynthPatch
CountYou can change the number of manually allocated
MKSynthPatch
es at any time―even during a
performance―simply by resending the setSynthPatchCount: message. Notice, however,
that the argument is always taken as the total number of
MKSynthPatch
es that are allocated to the
MKSynthInstrument
―it doesn't represent
the number of new objects to allocate. For example, in the following
sequence of messages, a total of four
MKSynthPatch
es are allocated.
/* Allocate three MKSynthPatches. */ [aSynthIns setSynthPatchCount: 3]; . . . /* Allocate one more. */ [aSynthIns setSynthPatchCount: 4]; |
The synthPatchCount method
returns the number of manually allocated MKSynthPatch
es. Thus, the
previous example can be rewritten as:
/* Allocate three MKSynthPatches. */ [aSynthIns setSynthPatchCount: 3]; . . . /* Allocate one more. */ [aSynthIns setSynthPatchCount: [aSynthIns synthPatchCount]+1]; |
If the MKSynthInstrument
is in automatic
mode, synthPatchCount returns
0.
Deallocating MKSynthPatch
objects is also
possible:
/* Allocate three MKSynthPatches. */ [aSynthIns setSynthPatchCount: 3]; . . . /* Deallocate two of them. */ [aSynthIns setSynthPatchCount: [aSynthIns synthPatchCount]-2]; |
If the argument signifies a deallocation, the
MKSynthInstrument
's idle
MKSynthPatch
es, if any, are deallocated first;
the balance are deallocated as they become inactive.
You can also change the synthPatchCount by sending it a mute note with a parameter synthPatchCount.
MKPatchTemplate
Some MKSynthPatch
es come in a variety of
configurations. For example, the Fm1vi
MKSynthPatch
(frequency modulation with
optional vibrato) is configured differently depending on whether the
MKNote
it's synthesizing specifies vibrato. It
does this to be as efficient as possible―excluding vibrato from
Fm1vi
's configuration means that it uses less
of the MKOrchestra
's resources.
A MKSynthPatch
represents each of its
configurations as a different MKPatchTemplate
object. When you set a MKSynthInstrument
to
manual allocation mode, you can specify the number of
MKSynthPatch
es with a particular
MKPatchTemplate
by invoking the setSynthPatchCount:forPatchTemplate: method.
The second argument is the id of the
MKPatchTemplate
that you want. This is
returned by sending the patchTemplateFor: message to the
MKSynthInstrument
's
MKSynthPatch
class, with a
MKNote
object as the argument. This is best
explained by example:
/* Create a MKSynthInstrument. */ id aSynthIns = [[MKSynthInstrument alloc] init]; /* Create a variable to store the MKPatchTemplate. */ id noVibTemplate; /* Create a dummy MKNote. */ id aNote = [[MKNote alloc] init]; /* Set its vibrato amplitudes to 0.0. */ [aNote setPar: MK_svibAmp toDouble: 0.0]; [aNote setPar: MK_rvibAmp toDouble: 0.0]; /* Retrieve the MKPatchTemplate senza vibrato. */ noVibTemplate = [[aSynthIns synthPatchClass] patchTemplateFor:aNote]; /* Allocate three vibratoless MKSynthPatches. */ [aSynthIns setSynthPatchCount: 3 forPatchTemplate: noVibTemplate]; |
If the MKPatchTemplate
isn't specified,
the MKSynthPatch
es are created using the
default MKPatchTemplate
.
Each subclass of MKSynthPatch
designates one of
its MKPatchTemplate
s as the default for that
class; by convention, the most extravagant
MKPatchTemplate
is provided as the default.
Thus, the default Fm1vi
MKPatchTemplate
includes vibrato. In the
example, the Fm1vi
MKPatchTemplate
without vibrato is retrieved by
passing (as the second argument to patchTemplateFor:) a
MKNote
that explicitly sets the vibrato
parameters to 0.0.
Within the same MKSynthInstrument
, you
can manually allocate MKSynthPatch
es that use
different MKPatchTemplate
s. The following
extension of the previous example demonstrates this:
/* Allocate one vibratoless MKSynthPatch. */ [aSynthIns setSynthPatchCount: 1 forPatchTemplate: noVibTemplate]; /* And two with vibrato. */ [aSynthIns setSynthPatchCount: 2]; |
setSynthPatchCount: always uses
the default MKPatchTemplate
(which, as
mentioned earlier, includes vibrato for Fm1vi
).
When the MKSynthInstrument
in the example
receives a MKNote
that initiates a new phrase,
it automatically forwards the MKNote
to the
proper MKSynthPatch
: If the
MKNote
contains vibrato parameters with zero
values (similar to the dummy MKNote
used in the
previous example), it's forwarded to the
MKSynthPatch
that excludes vibrato; otherwise,
it's sent to one of the other two
MKSynthPatch
es.
The MKSynthInstrument
keeps a count of
the number of MKSynthPatch
es it has manually
allocated for each MKPatchTemplate
. The count
for a particular MKPatchTemplate
is returned by
the method synthPatchCountForPatchTemplate:. The
synthPatchCount method, used in an
example in the previous section, returns the count for the default
MKPatchTemplate
.
If the MKSynthPatch
is in automatic mode,
you don't need to specify which MKPatchTemplate
to use. The MKSynthInstrument
automatically
creates a MKSynthPatch
with the correct
MKPatchTemplate
to accommodate the parameters
in the MKNote
s it receives.
MKSynthPatch
esA MKSynthInstrument
's primary task is to
forward MKNote
s to its
MKSynthPatch
es. The
MKSynthPatch
class defines three methods that
are designed to be invoked by a
MKSynthInstrument
for this purpose:
noteOn: is used to
forward a noteOn type MKNote
.
noteUpdate: forwards noteUpdates.
noteOff:, likely enough, forwards noteOffs.
The MKNote
itself is passed as the
argument to the method. Invocation of these methods is automatic;
when a MKSynthInstrument
receives a
MKNote
during a performance, it automatically
invokes the appropriate method.
Notice that noteDurs and mutes aren't included in this scheme.
A noteDur is split into a noteOn/noteOff pair. If the noteDur doesn't
have a noteTag, a unique noteTag is created and given to the two new
MKNote
s. Mutes are simply ignored.
Besides forwarding MKNote
s, the noteOn: and noteOff: methods also set a
MKSynthPatch
's synthesis
status. This describes the object's current synthesis
state as one of the following MKSynthStatus constants:
Table 6-2. MKSynthInstrument
's synthesis states
Constant | Meaning |
---|---|
MK_idle | The MKSynthPatch is currently inactive. |
MK_running | The
MKSynthPatch is synthesizing
the body of a musical note. |
MK_finishing | The
MKSynthPatch is in the
release portions of its
MKEnvelope s. |
The noteOn: message sets the
synthesis status to MK_running
; noteOff: sets it to
MK_finishing
. In either of these states, the
MKSynthPatch
is considered to be active. The
status is set to MK_idle
when the release portion of the
MKSynthPatch
's amplitude
MKEnvelope
(in other words, the object set as
the value of the MK_ampEnv parameter of the
MKNote
that the
MKSynthPatch
is realizing) is complete. None
of the other MKEnvelope
s are taken into
consideration when determining if the
MKSynthPatch
is idle: It's assumed that the
amplitude MKEnvelope
will ultimately fall to an
amplitude of 0.0, thus whatever course the other
MKEnvelope
s take after that point is for nought
since the MKSynthPatch
will no longer be making
any noise.
The MKSynthInstrument
class gives special
consideration to a noteUpdate that doesn't have a note tag:
The noteUpdate is forwarded to all the active
MKSynthPatch
es (within the MKSynthInstrument
that received the
MKNote
).
The MKNote
's parameters are
stored in the MKSynthInstrument
's update
state.
Every MKSynthInstrument
has an update
state. When a MKSynthInstrument
begins a new
phrase, the parameters in the update state are merged into the
MKNote
that initiated the phrase (always a
noteOn, whether by nature or due to a noteDur split). The update
state parameters never overwrite the value of a parameter already
present in a noteOn―in the case of a parameter collision, the
noteOn's parameter takes precedence. Conversely, a noteOn's
parameters never affect the update state, it can only be changed by
another noteUpdate with no note tag.
As a demonstration of these principles, consider the following scorefile excerpt:
/* Scorefile body excerpt. */ t 0.0; part1 (noteUpdate) amp:.25; t 1.0; part1 (noteOn 1) freq:c4; /* amplitude is .25 */ t 2.0; part1 (noteOff 1); t 3.0; part1 (noteOn 2) freq:d4 amp:.75; /* amplitude is .75 */ t 4.0; part1 (noteOff 2); t 5.0; part1 (noteOn 3) freq:e4; /* amplitude is, once again, .25 */ t 6.0; part1 (noteUpdate) amp:.5; t 7.0; part1 (noteUpdate 3) amp:.25; t 8.0; part1 (noteOff 3); t 9.0; part1 (noteOn 4) freq: f4; /* amplitude is .5 */ t 10.0; part1 (noteOff 4); |
The initial note tag-less noteUpdate sets the amplitude
parameter in the MKSynthInstrument
's update
state; notice that the update state is set even though the
MKSynthInstrument
doesn't have any active
MKSynthPatch
es. Of the four subsequent
noteOns, the first, third, and fourth don't have amplitude parameters
so they inherit the one in the update state. The second noteOn has
its own amplitude; it ignores the parameter in the update
state.
While the third musical note is sounding, two more noteUpdates
arrive. The first has no note tag, so it affects both the active
MKSynthPatch
and the update state. The second
noteUpdate's note tag matches that of the active
MKSynthPatch
; it's forwarded to the
MKSynthPatch
but doesn't affect the update
state.
MKSynthPatch
While the DSP makes a great synthesizer, its resources
are by no means unlimited. It's possible to ask it to synthesize, at
the same time, more MKNote
s than it can
accommodate. The number of MKNote
s that can be
synthesized at one time depends on a number of factors, the most
significant being the sampling rate and the requirements of the
MKSynthPatch
es that are being used. There
sometimes comes a time in the life of a
MKSynthInstrument
when it tries to allocate
just one more MKSynthPatch
and finds that the
well is empty.
When a MKSynthInstrument
can't get the
resources to synthesize a new MKNote
, it tries
to preempt an active MKSynthPatch
rather than
lose the MKNote
. The following steps are taken
to determine which MKSynthPatch
to
preempt:
The preempted MKSynthPatch
should have sufficient resources to synthesize the
MKNote
, thus the
MKSynthInstrument
first looks for a
MKSynthPatch
that uses the same
MKPatchTemplate
that's needed to synthesize the
new MKNote
.
If there's more than one such
MKSynthPatch
, the one that first received a
noteOff: message, if any, is
preempted. In other words, the preemption scheme first looks for a
MKSynthPatch
whose synthesis status is
MK_finishing
.
If there aren't any finishing
MKSynthPatch
es, the oldest
MKSynthPatch
is chosen.
If a MKSynthPatch
with the
appropriate MKPatchTemplate
isn't available,
the MKSynthInstrument
tries other
MKSynthPatch
es until one is found that has
sufficient resources to synthesize the new
MKNote
.
A MKSynthInstrument
object can only
preempt its own MKSynthPatch
es―it can't
steal one from another MKSynthInstrument
. The
search for a preemptible MKSynthPatch
is
sometimes unsuccessful; for example, if there are no more resources to
build a new MKSynthPatch
, the new
MKNote
can't be synthesized.
The determination of which MKSynthPatch
to preempt is performed in the preemptSynthPatchFor:patches: method: The
method's return value is taken as the
MKSynthPatch
to preempt. If you want to
provide your own system for preempting
MKSynthPatch
es, you have to create your own
subclass of MKSynthInstrument
in which to
reimplement this method.
The method is automatically invoked when the
MKSynthInstrument
has to preempt a
MKSynthPatch
. The two arguments are:
The newly arrived
MKNote
The first in a list of candidate
MKSynthPatch
es
Notice that the second argument is a single
MKSynthPatch
object. To get to the next object
in the list, you send next to the
MKSynthPatch
at hand.
MKMidi
The MKMidi
class isn't a true
MKInstrument
―it inherits from NSObject
.
However, it creates MKNoteReceiver
s and
implements realizeNote:fromNoteReceiver:
and so can be used as an MKInstrument
.
A MKMidi
object creates 17
MKNoteReceiver
s, one for the Basic Channel and
one each for the 16 Voice Channels. You can retrieve the
MKNoteReceiver
that corresponds to a particular
channel through the channelNoteReceiver:
message, passing the channel number that you want: 0 retrieves the
MKNoteReceiver
for System and Channel Mode
Messages; 1-16 retrieves the MKNoteReceiver
for
the corresponding Voice Channel. You create a
MKMidi
object to correspond to a MIDI device (a
serial port on NeXT hardware, MIDI card on Intel-based hardware, CoreMIDI
port on MacOS X, DirectMusic port on Windows etc.),
specified as “midi0”, “midi1,” etc., in the
midiOnDevice: method.
When it receives a MKNote
, the
MKMidi
object translates it into a series of
MIDI messages, based on the MKNote
's note type
and parameters that it contains. It then sends the messages out the
serial port.
A MKMidi
object has a device status
that's much like the device status of the
MKOrchestra
object, as described in
Chapter 5
Before you can send MKNote
s to a
MKMidi
object (more accurately, before the
object will do anything with these MKNote
s),
you must open and run it, through the open and run
methods.
A MKMidi
object can be made to receive
MIDI time code and provide it to a MKConductor
so that a performance can be synchronized to MIDI time code. For
further information, see the MKMidi
and
MKConductor
class descriptions, as well as
Appendix B.
MKPartRecorder
and
MKScoreRecorder
MKPartRecorder
is a fairly
straightforward MKInstrument
: The
MKNote
s that it receives through its single
MKNoteReceiver
are copied and added to the
MKPart
with which it's associated. The
MKNoteReceiver
is created automatically; the
MKPart
must be set by your application, through
the setMKPart
:
method.
A MKPartRecorder
sets a
MKNote
's time tag to the current time as it
receives the MKNote
; the measure of the current
time is either in beats or seconds, depending on the value of its
“time unit.” You can set the time unit through the
setTimeUnit: method, passing either
MK_second, MK_beat or MK_timeTag as the argument. Other than the
manipulation of the time tag, the MKNote
is
unchanged by the MKPartRecorder
.
MKScoreRecorder
isn't actually an
MKInstrument
; it's used to correspond to
MKScore
just as a
MKPartRecorder
corresponds to a
MKPart
. A
MKScoreRecorder
actually creates and controls
some number of MKPartRecorder
s, one for each
MKPart
in its
MKScore
.
MKScorefileWriter
A MKScorefileWriter
object realizes
MKNote
s by writing them to a scorefile. It's
the one MKInstrument
defined by the MusicKit
that requires you to create and add
MKNoteReceiver
s from your application. Each
MKNoteReceiver
that you add corresponds to a
MKPart
that will be represented in the
scorefile that's written. Typically, you name the
MKNoteReceiver
s that you create; these names
are used to identify the corresponding
MKPart
-representations in the scorefile:
/* Create a MKScorefileWriter and add to it 3 MKNoteReceivers. */ id aSFWriter = [[MKScorefileWriter alloc] init]; id fordReceiver = [[MKNoteReceiver alloc] init]; id pageReceiver = [[MKNoteReceiver alloc] init]; id quicklyReceiver = [[MKNoteReceiver alloc] init]; /* Name the MKNoteReceivers. */ MKNameObject("Ford", fordReceiver); MKNameObject("Page", pageReceiver); MKNameObject("Quickly", quicklyReceiver); /* Add the MKNoteReceivers. */ [aSFWriter addNoteReceiver: fordReceiver]; [aSFWriter addNoteReceiver: pageReceiver]; [aSFWriter addNoteReceiver: quicklyReceiver]; /* Set the MKScorefileWriter's file by name. */ [aSFWriter setFile: "Falstaff.score"]; |
MKInstrument
SubclassWhile there are no strict rules governing realization,
intelligent MKInstrument
design should follow
these guidelines:
An MKInstrument
should realize
MKNote
s as it receives them. It's possible to
design an MKInstrument
that, for instance,
reschedules its MKNote
s to be realized later,
but for the sake of generality, an MKInstrument
should act immediately upon the MKNote
s it
receives.
If an MKInstrument
needs to
alter or store a MKNote
, it should create a
copy of the MKNote
and act upon the
copy.
An MKInstrument
shouldn't be a
source of MKNote
s. The task of generating new
MKNote
s belongs to a
MKPerformer
or to your application. The role
of an MKInstrument
is to respond, not to
conceive. This doesn't mean that an
MKInstrument
can't create
MKNote
s, but it should only do so in response
to receiving a MKNote
.
Along with these guidelines, keep in mind that an
MKInstrument
should, if possible, create and
add to itself some number of MKNoteReceiver
s,
usually in its init method.