MKInstrument ClassMKInstruments 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
MKNotes. The
MusicKit includes a number of
MKInstrument subclasses (and
pseudo-MKInstruments) that realize
MKNotes by synthesizing them on the
DSP or an external MIDI
synthesizer, and that store MKNotes 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 MKNotes that it realizes. Instead, it
realizes MKNotes that are sent to it by another
object or by your application. The machinery by which an
MKInstrument receives
MKNotes 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 MKNotes. The latter
topic centers upon descriptions of the
MKInstrument subclasses included in the
MusicKit, followed by an example of
MKInstrument subclass design.
MKNotesAn MKInstrument receives
MKNotes through the same method that defines
their realization, realizeNote:fromNoteReceiver:.
However, you don't send MKNotes to an
MKInstrument by invoking this method directly;
instead, you send the MKNotes 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
MKNotes (in other words, methods that invoke
MKInstrument's realizeNote:fromNoteReceiver:).
It lets you control the stream of
MKNotes into an
MKInstrument by allowing you to toggle its
ability to forward MKNotes 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
MKNotes 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 MKPerformers and
MKNoteSenders.
Creating and adding MKNoteReceivers is
usually part of the design of the MKInstrument
subclass, although some subclasses require you to create and add
MKNoteReceivers 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
MKNotes―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
MKNoteReceivers 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
MKInstruments create and add
MKNoteReceivers for you. Any
MKInstrument's
MKNoteReceivers 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 MKInstruments 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
MKNotes 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 MKConductors and
MKPerformers, 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
MKNoteReceivers to the
MKInstruments that you allocate, even if these
MKInstruments create
MKNoteReceivers 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 MKPerformers or different
mechanisms in your application―can all send receiveNote: to the same
MKNoteReceiver. In general, adding a gaggle of
MKNoteReceivers to an
MKInstrument doesn't make the
MKInstrument more interesting, it simply makes
it bigger.
MKNoteReceiverYou 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 MKNoteReceivers for a single
MKInstrument. For example, you can create an
application in which MKNotes 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 MKNoteReceivers,
one for either source, so you can independently squelch the two
streams of MKNotes.
An MKInstrument is considered to be in
performance from the time that you send receiveNote: to any of its unsquelched
MKNoteReceivers 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).
MKNotesThere are very few rules when it comes to realizing
MKNotes: You can implement realizeNote:fromNoteReceiver:
to realize a MKNote in almost any arbitrary
manner. As mentioned earlier, the MusicKit includes
MKInstruments that synthesize and store
MKNotes as their forms of realization. For
many performance applications, the
MKInstruments provided by the MusicKit are
sufficient. The following sections describe these subclasses.
MKSynthInstrumentA MKSynthInstrument is by far the most complicated
MKInstrument; it realizes
MKNotes 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 MKNotes 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
MKNotes, tagging each MKSynthPatch with the
note tag of the MKNote for which it was
allocated. As it receives subsequent MKNotes,
the MKSynthInstrument compares each MKNote's
note tag to those of the MKSynthPatches 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
MKNotes begin to arrive. The number of
MKSynthPatches to allocate is set through the method setSynthPatchCount:. The number of
MKSynthPatches 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
MKSynthPatches 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
MKSynthPatches 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
MKSynthPatches 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
MKSynthPatches are allocated.
/* Allocate three MKSynthPatches. */ [aSynthIns setSynthPatchCount: 3]; . . . /* Allocate one more. */ [aSynthIns setSynthPatchCount: 4]; |
The synthPatchCount method
returns the number of manually allocated MKSynthPatches. 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
MKSynthPatches, 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.
MKPatchTemplateSome MKSynthPatches 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
MKSynthPatches 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 MKSynthPatches are created using the
default MKPatchTemplate.
Each subclass of MKSynthPatch designates one of
its MKPatchTemplates 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 MKSynthPatches that use
different MKPatchTemplates. 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
MKSynthPatches.
The MKSynthInstrument keeps a count of
the number of MKSynthPatches 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 MKNotes it receives.
MKSynthPatchesA MKSynthInstrument's primary task is to
forward MKNotes to its
MKSynthPatches. 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
MKNotes. Mutes are simply ignored.
Besides forwarding MKNotes, 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
MKEnvelopes. |
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 MKEnvelopes 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
MKEnvelopes 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
MKSynthPatches (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
MKSynthPatches. 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.
MKSynthPatchWhile 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 MKNotes than it can
accommodate. The number of MKNotes 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
MKSynthPatches 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
MKSynthPatches, the oldest
MKSynthPatch is chosen.
If a MKSynthPatch with the
appropriate MKPatchTemplate isn't available,
the MKSynthInstrument tries other
MKSynthPatches until one is found that has
sufficient resources to synthesize the new
MKNote.
A MKSynthInstrument object can only
preempt its own MKSynthPatches―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
MKSynthPatches, 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
MKSynthPatches
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.
MKMidiThe MKMidi class isn't a true
MKInstrument―it inherits from NSObject.
However, it creates MKNoteReceivers and
implements realizeNote:fromNoteReceiver:
and so can be used as an MKInstrument.
A MKMidi object creates 17
MKNoteReceivers, 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 MKNotes to a
MKMidi object (more accurately, before the
object will do anything with these MKNotes),
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
MKScoreRecorderMKPartRecorder is a fairly
straightforward MKInstrument: The
MKNotes 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 MKPartRecorders, one for each
MKPart in its
MKScore.
MKScorefileWriterA MKScorefileWriter object realizes
MKNotes by writing them to a scorefile. It's
the one MKInstrument defined by the MusicKit
that requires you to create and add
MKNoteReceivers 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
MKNoteReceivers 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
MKNotes as it receives them. It's possible to
design an MKInstrument that, for instance,
reschedules its MKNotes to be realized later,
but for the sake of generality, an MKInstrument
should act immediately upon the MKNotes 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 MKNotes. The task of generating new
MKNotes 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
MKNotes, 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 MKNoteReceivers,
usually in its init method.