MKNotesThe MKPart and
MKScore classes are designed to group
MKNote objects. As their names imply, a
MKPart represents a series of
MKNotes that are realized on the same
MKInstrument; a MKScore
represents all or part of a composition and consists of some number of
MKParts. 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
MKNotes to MKParts,
MKParts to MKScores, and
to write a MKScore as a file. The second part,
the Section called Retrieving MKScores,
MKParts, and MKNotes, presents methods
and techniques for retrieving, querying, and further manipulating
MKParts, MKScores, and
the MKNotes they contain. Finally, the special
MKNotes called MKPart
info and MKScore info are described.
MKScoreMKNote to a MKPartA MKPart is a time tag sorted collection
of MKNote objects. A
MKPart can contain any number of
MKNotes. While the
MKNotes 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) MKNotes. 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 MKNotes
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
MKNotes 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 MKNotes.
MKPartA 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
MKScoreTo 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
MKParts; 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
MKNotes, 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 MKNotes 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 MKNotes are
time-shifted as they're represented in the file. Only the file
representation is affected by this value; in other words, the
MKNotes 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.
MKScores,
MKParts, and MKNotesYou 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
MKScoreThe complete set of a MKScore's
MKParts, regardless of how the
MKScore was created, can be retrieved through
MKScore's part
method. The method returns the MKParts in a
NSArray. The MKScore method partCount gives the number of
MKParts that it contains.
MKNote from a
MKPartThere 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,
MKNotes are ordered by their time tag values;
the order of MKNotes with equivalent time tags
reflects the order in which they were added to the
MKPart.
The time tag and the ordinal methods of retrieving
MKNotes 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
MKNotes 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 MKNotes 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
MKPartThe MKPart class defines two methods for
removing individual MKNotes from a
MKPart object:
removeNote: removes
the MKNote object specified in the
argument.
removeNotes: removes
all MKNotes 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.
MKNotesThe MKPart class defines methods that
allow you to add and remove MKNotes as a
collection. There are two methods for adding a collection of
MKNotes:
addNotes:timeShift:
addNoteCopies:timeShift:
The first argument is a NSArray of
MKNotes. 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
MKNotes, you can invoke the method removeNotes:, which takes a
NSArray of MKNotes and
removes each one from the receiver.
Finally, you can remove all the MKNotes
from a MKPart through the empty method.