MKNote
sThe MKPart
and
MKScore
classes are designed to group
MKNote
objects. As their names imply, a
MKPart
represents a series of
MKNote
s that are realized on the same
MKInstrument
; a MKScore
represents all or part of a composition and consists of some number of
MKPart
s. For storage, a
MKScore
can be written to the disk as a
scorefile or a midifile.
The first part of this section explains the methods used to add
MKNote
s to MKPart
s,
MKPart
s to MKScore
s, and
to write a MKScore
as a file. The second part,
the Section called Retrieving MKScore
s,
MKPart
s, and MKNotes
, presents methods
and techniques for retrieving, querying, and further manipulating
MKPart
s, MKScore
s, and
the MKNote
s they contain. Finally, the special
MKNote
s called MKPart
info and MKScore
info are described.
MKScore
MKNote
to a MKPart
A MKPart
is a time tag sorted collection
of MKNote
objects. A
MKPart
can contain any number of
MKNote
s. While the
MKNote
s within a MKPart
are sorted by time tag value, every MKNote
in
the MKPart
needn't have a unique time tag; a
MKPart
can represent simultaneous (and
otherwise overlapping) MKNote
s. However, a
single MKNote
can only belong to one
MKPart
at a time.
There are three methods for adding a
MKNote
to a
MKPart
:
addToPart:, a
MKNote
method
addNote:, a
MKPart
method
addNoteCopy:, also a
MKPart
method
The first two are functionally equivalent, allowing you to add a
MKNote
to a MKPart
by
messaging either object:
[myNote addToPart: myPart]; /* is the same as */ [myPart addNote: myNote]; |
Since a MKNote
object can only belong to
one MKPart
at a time, when you add a
MKNote
to a MKPart
, it's
first removed from the MKPart
that it's
currently a member of, if any. Both addNote: and addToPart: return the id of the MKNote
's old
MKPart
(the MKPart
from
which the MKNote
was removed).
addNoteCopy: creates (and
returns) a new MKNote
object as a copy of its
argument and adds the copy to the receiver. The argument
MKNote
itself isn't removed from its
MKPart
. The method creates the
MKNote
copy by invoking
MKNote
's copy method.
You can continue to modify a MKNote
(setting its parameter values, note type, time tag, and so on) after
it has been added to a MKPart
object. A
MKPart
sorts its MKNote
s
automatically, so you can add them in any order―you don't have to
add them by order of their time tag values. If you change the time
tag of a MKNote
after you add it to a
MKPart
, the MKNote
is
automatically repositioned in the MKPart
.
Methods for adding a collection of
MKNote
s to a
MKPart
―for instance, adding the contents
of one MKPart
to another
MKPart
―are described below, in
the Section called Adding and Removing Groups of MKNote
s.
MKPart
A MKPart
object has a print name that's
used when writing it to a scorefile (or, more accurately, when the
MKScore
to which the
MKPart
belongs is written to a scorefile). The
MKNameObject()
function is used to
name a MKPart
; like all MusicKit names, a
MKPart
's name is case-sensitive and consists of
a letter followed by a string of alphanumeric characters:
MKNameObject("Solo", aPart); |
To retrieve a MKPart
's name, call the C
function MKGetObjectName()
:
char *aName = MKGetObjectName(aPart); |
MKPart
to a
MKScore
To add a MKPart
to a
MKScore
, invoke one of these methods:
addToScore:, a
MKPart
method
addPart:, a
MKScore
method
A MKScore
can contain any number of
MKPart
s; a MKPart
can
belong to only one MKScore
at a time. Both of
the MKPart
-adding methods first remove the
MKPart
from its present
MKScore
, if any.
Just as you can modify a MKNote
after it
has been added to a MKPart
, you can continue to
modify a MKPart
(by adding and removing
MKNote
s, for example) after it has been added
to a MKScore
.
MKScore
to a FileYou write a scorefile by sending one of the following messages
to a MKScore
object:
writeScorefile: takes a file name (char *) argument.
writeScorefileStream: takes a stream pointer
(NSMutableData
*) argument.
The first of these opens and closes the file for you. Every time you send the writeScorefile: message, the named file is overwritten. By convention, scorefiles are given a “.score” extension.
writeScorefileStream: expects a stream pointer that's already open for writing. The method leaves the stream open after it returns, allowing you to write additional, application-specific information to the end of the file. It's left to your application to close the stream.
Rather than write an entire MKScore
to a
file, you can specify a particular section by sending the writeScorefile:firstTimeTag:lastTimeTag:timeShift:
message (a similar method, with an initial keyword of writeScorefileStream:, is provided for writing
a section of a MKScore
to a stream). For
example:
[myScore writeScorefile: @"Adagio.score" firstTimeTag: 3.5 lastTimeTag: 10.2 timeShift: 0.0] |
Here, only those MKNote
s with time tag
values between 3.5 and 10.2, inclusive, are written to the file. As a
convenience, you can use the constant
MK_ENDOFTIME
as the lastTimeTag: argument to write from some
position in the MKScore
to its end:
[myScore writeScorefile: mySfile firstTimeTag: 3.5 lastTimeTag: MK_ENDOFTIME timeShift: 0.0] |
To write from the beginning of the
MKScore
, you specify 0.0 as the firstTimeTag: argument. The timeShift: takes a value that specifies the
number of beats by which the MKNote
s are
time-shifted as they're represented in the file. Only the file
representation is affected by this value; in other words, the
MKNote
s themselves aren't time-shifted (their
time tags aren't affected).
For each of the scorefile-writing methods, there is an analogous method that writes a Standard MIDI file.
MKScore
s,
MKPart
s, and MKNotes
You can fill a MKScore
with information
simply by reading a scorefile (or MIDI file; for brevity, scorefiles
are used exclusively in the examples). The methods provided for
reading a scorefile are analogous to those for writing:
readScorefile: takes a file name argument.
readScorefileStream:
takes an NSMutableData
pointer argument.
When you read a scorefile into a MKScore
object, MKPart
and
MKNote
objects are automatically created to
accommodate the information in the file.
You can specify a section of the scorefile and a time shift on that section by adding the firstTimeTag:lastTimeTag:timeShift: keywords. For example:
[myScore readScorefile: @"Adagio.score" firstTimeTag: 6.5 lastTimeTag: MK_ENDOFTIME timeShift: -6.5] |
Notes represented in the file that have time tag values greater
than or equal to 6.5 are read into the MKScore
.
Within the MKScore
, each
MKNote
's time tag is shifted ahead in time
(toward the chronological beginning of a
MKPart
) by 6.5 beats.
MKPart
in a
MKScore
The complete set of a MKScore
's
MKPart
s, regardless of how the
MKScore
was created, can be retrieved through
MKScore
's part
method. The method returns the MKPart
s in a
NSArray
. The MKScore
method partCount gives the number of
MKPart
s that it contains.
MKNote
from a
MKPart
There are a number of ways to retrieve a
MKNote
from a MKPart
.
One way is to access the MKNote
by its time tag
value through one of the following MKPart
methods:
The atTime: method
returns the first MKNote
object with the
specified time tag value.
atOrAfterTime:
returns the first MKNote
with a time tag
greater than or equal to the argument.
Both methods take a double
argument and they both return nil if
they can't find an appropriate MKNote
. The
following example illustrates the difference between the two
methods:
/* Create a MKPart, two MKNotes, and an id for return values. */ MKPart *aPart = [MKPart new], MKNote *aNote = [MKNote newSetTimeTag: 2.0]; MKNote *bNote = [MKNote newSetTimeTag: 3.0]; MKNote *returnNote; /* Add the MKNotes to the MKPart. */ [aPart addNote: bNote]; [aPart addNote: aNote]; /* Retrieve the MKNote at time 1.5. (Returns nil; no such MKNote.) */ returnNote = [aPart atTime: 1.5]; /* Retrieve the MKNote at or after 1.5. (Returns aNote.) */ returnNote = [aPart atOrAfterTime: 1.5]; |
You can also retrieve a MKNote
by its
ordinal position within its MKPart
by sending
the message nth: to the
MKPart
object. It takes an integer argument
n and returns the nth
MKNote
, zero-based, in the
MKPart
. Using the same
MKPart
object from the previous example, the
message
[aPart nth: 1] |
returns bNote, the second
MKNote
in the MKPart
.
Recall that within a MKPart
,
MKNote
s are ordered by their time tag values;
the order of MKNote
s with equivalent time tags
reflects the order in which they were added to the
MKPart
.
The time tag and the ordinal methods of retrieving
MKNote
s are combined in the methods atTime:nth: and atOrAfterTime:nth:. The first of these methods
returns the nth MKNote
with the specified time tag; through this method you can retrieve a
particular MKNote
in a chord. The atOrAfterTime:nth: method returns the
nth MKNote
with a time tag
equal to or greater than the first argument.
The next: method also retrieves
MKNote
s based on ordinal position: It returns
the MKNote
that immediately follows the
MKNote
given as the argument. next: can be used to access each
MKNote
in a MKPart
in
turn:
/* Initially set aNote to the first MKNote in the MKPart. */ MKNote *aNote = [aPart nth: 0]; /* Access each MKNote in the MKPart and change it to MK_mute. */ while(aNote = [aPart next: aNote]) [aNote setNoteType: MK_mute]; |
next: returns nil if there is no next
MKNote
, or if the argument isn't a member of
the MKPart
. A more efficient way of accessing
each MKNote
in a MKPart
is to create a NSArray
of the MKNote
s in a
MKPart
and step down the NSArray
. The previous
example can be rewritten as follows:
/* Create a Sequence over the MKPart's MKNotes. */ NSArray *notes = [aPart notes]; int noteCount = [aPart noteCount]; int i; /* Access each MKNote in the Sequence and change its note type. */ for (i = 0; i < noteCount; i++) [[notes objectAtIndex: i] setNoteType: MK_mute]; |
MKNote
from a
MKPart
The MKPart
class defines two methods for
removing individual MKNote
s from a
MKPart
object:
removeNote: removes
the MKNote
object specified in the
argument.
removeNotes: removes
all MKNote
s common to the receiver and the NSArray
specified in the argument.
You can also remove a MKNote
from its
MKPart
by sending the removeFromPart: message to the
MKNote
object. The
MKPart
object from which the
MKNote
was removed is returned.
MKNote
sThe MKPart
class defines methods that
allow you to add and remove MKNote
s as a
collection. There are two methods for adding a collection of
MKNote
s:
addNotes:timeShift:
addNoteCopies:timeShift:
The first argument is a NSArray
of
MKNote
s. The second argument is an optional
value in beats (a double) that's used
to offset each MKNote
's time tag. The
addNotes:timeShift: method removes
each MKNote
in the collection from the
MKPart
that it's currently a member of before
adding it to the receiver. The addNoteCopies:timeShift: method adds copies of
each MKNote
in the
NSArray
.
To remove a NSArray
of
MKNote
s, you can invoke the method removeNotes:, which takes a
NSArray
of MKNote
s and
removes each one from the receiver.
Finally, you can remove all the MKNote
s
from a MKPart
through the empty method.