MusicKit
0.0.0
|
A MKNote object represents a musical sound or event by describing its attributes. More...
#import <MKNote.h>
A MKNote object represents a musical sound or event by describing its attributes.
MKNote objects are containers of musical information. The amount and type of information that a MKNote can hold is practically unlimited; however, you should keep in mind that MKNotes haven't the ability to act on this information, but merely store it. It's left to other objects to read and process the information in a MKNote. Most of the other MusicKit classes are designed around MKNote objects, treating them as common currency. For example, MKPart objects store MKNotes, MKPerformers acquire them and pass them to MKInstruments, MKInstruments read the contents of MKNotes and apply the information therein to particular styles of realization, and so on.
The information that comprises a MKNote defines the attributes of a particular musical event. Typically, an object that uses MKNotes plucks from them just those bits of information in which it's interested. Thus you can create MKNotes that are meaningful in more than one application. For example, a MKNote object that's realized as synthesis on the DSP would contain many particles of information that are used to drive the synthesis machinery; however, this doesn't mean that the MKNote can't also contain graphical information, such as how the MKNote would be rendered when drawn on the screen. The objects that provide the DSP synthesis realization (MKSynthPatch objects, as defined by the MusicKit) are designed to read just those bits of information that have to do with synthesis, and ignore anything else the MKNote contains. Likewise, a notation application would read the attributes that tell it how to render the MKNote graphically, and ignore all else. Of course, some information, such as the pitch and duration of the MKNote, would most likely be read and applied in both applications.
Most of the methods defined by the MKNote class are designed to let you set and retrieve information in the form of parameters. A parameter consists of a tag, a name, a value, and a data type:
A parameter tag is a unique integer used to catalog the parameter within the MKNote; the MusicKit defines a number of parameter tags such as MK_freq (for frequency) and MK_amp (for amplitude).
The parameter's name is used primarily to identify the parameter in a scorefile. The names of the MusicKit parameters are the same as the tag constants, but without the "MK_" prefix. You can also use a parameter's name to retrieve its tag, by passing the name to MKNote's parTagForName: class method. (As explained in its descriptions below, it's through this method that you create your own parameter tags.) Similarly, you can get a name from a tag with MKNote's parNameForTag: class method.
A parameter's value can be a double, int, string (char *), or an object (id). The method you invoke to set a parameter value depends on the type of the value. To set a double value, for example, you would invoke the setPar:toDouble: method. Analogous methods exist for the other types. You can retrieve the value of a double-, int-, or string-valued parameter as any of these three types, regardless of the actual type of the value. For example, you can set the frequency of a MKNote as a double, thus:
[aNote setPar: MK_freq toDouble: 440.0]
and then retrieve it as an int:
int freq = [aNote parAsInt: MK_freq]
The type conversion is done automatically.
Object-valued parameters are treated differently from the other value types. The only MusicKit objects that are designed to be used as parameter values are MKEnvelopes and MKWaveTables (and the MKWaveTable descendants MKPartials and MKSamples). Special methods are provided for setting and retrieving these objects. Other objects, most specifically, objects of your own classes, are set through the setPar:toObject: method. While an instance of any class may be set as a parameter's value through this method, you should note well that only those objects that respond to the writeASCIIStream: and readASCIIstream: messages can be written to and read from a scorefile. None of the MusicKit classes implement these methods and so their instances can't be written to a scorefile as parameter values (MKEnvelopes and MKWaveTables are written and read through a different mechanism).
A parameter is said to be present within a MKNote once its value has been set. You can determine whether a parameter is present in one of four ways:
The easiest way is to invoke the boolean method isParPresent:, passing the parameter tag as the argument. An equivalent C function, MKIsNoteParPresent() is also provided for greater efficiency.
At a lower lever, you can invoke the parVector: method to retrieve one of a MKNote's “parameter bit vectors”, integers that the MKNote uses internally to indicate which parameters are present. You query a parameter bit vector by masking it with the parameter's tag:
// A MKNote may have more then one bit vector to accommodate all // its parameters.
int parVector = [aNote parVector: (MK_amp / 32)];
// If MK_amp is present, the predicate will be true. if (parVector & (1 << (MK_amp % 32)))
If you plan on retrieving the value of the parameter after you've checked for the parameter's presence, then it's generally more efficient to go ahead and retrieve the value and then determine if the parameter is actually set by comparing its value to the appropriate parameter-not-set value, as given below:
Retrieval type | No-set value |
int | MAXINT |
double | MK_NODVAL (but see below) |
NSString | @“” |
id | nil |
Unfortunately, you can't use MK_NODVAL in a simple comparison predicate. To check for this return value, you must call the in-line function MKIsNoDVal(); the function returns 0 if its argument is MK_NODVAL and nonzero if not:
// Retrieve the value of the amplitude parameter. double amp = [aNote parAsDouble: MK_amp];
// Test for the parameter's existence. if (!MKIsNoDVal(amp)) ... // do something with the parameter
A MKNote has two special timing attributes: A MKNote's time tag corresponds, conceptually, to the time during a performance that the MKNote is performed. Time tags are set through the setTimeTag: method. The other timing attribute is the MKNote's duration, a value that indicates how long the MKNote will endure once it has been struck. It's set through setDur:. A single MKNote can have only one time tag and one duration. Keep in mind, however, that not all MKNotes need a time tag and a duration. For example, if you realize a MKNote by sending it directly to an MKInstrument, then the MKNote's time tag - indeed, whether it even has a time tag - is of no consequence; the MKNote's performance time is determined by when the MKInstrument receives it (although see the MKScorefileWriter, MKScoreRecorder, and MKPartRecorder class descriptions for alternatives to this edict). Similarly, a MKNote that merely initiates an event, relying on a subsequent MKNote to halt the festivities, as described in the discussion of note types, below, doesn't need and actually mustn't be given a duration value.
During a performance, time tag and duration values are measured in time units called beats. The size of a beat is determined by the tempo of the MKNote's MKConductor. You can set the MKNote's conductor directory with the method setConductor:. However, if the MKNote is in the process of being sent by a MKPerformer (or MKMidi), the MKPerformer's MKConductor is used instead. Hence, MKNote's conductor method returns the MKPerformer's MKConductor if the MKNote is in the process of being sent by a MKPerformer, or the MKNote's conductor otherwise. If no MKConductor is set, then its MKConductor is the defaultConductor, which has a default (but not immutable) tempo of 60.0 beats per minute.
Keep in mind that if you send a MKNote directly to an MKInstrument, then the MKNote's time tag is (usually) ignored, as described above, but its duration may be considered and employed by the MKInstrument.
A MKNote has a note type that casts it into one of five roles:
Only noteDurs may have duration values; the very act of setting a MKNote's duration changes it to a noteDur.
You match the two MKNotes in a noteOn/noteOff pair by giving them the same note tag value; a note tag is an integer that identifies two or more MKNotes as part of the same musical event or phrase. In addition to coining noteOn/noteOff pairs, note tags are used to associate a noteUpdate with a noteDur or noteOn that's in the process of being performed. The C function MKNoteTag() is provided to generate note tag values that are guaranteed to be unique across your entire application - you should never create a new note tag except through this function.
Instead of or in addition to being actively realized, a MKNote object can be stored. In a running application, MKNotes are stored within MKPart objects through the addToPart: method. A MKNote can only be added to one MKPart at a time; adding it to a MKPart automatically removes it from its previous MKPart. Within a MKPart object, MKNotes are sorted according to their time tag values.
For long-term storage, MKNotes can be written to a scorefile. There are two "safe" ways to write a scorefile: You can add a MKNote-filled MKPart to a MKScore and then write the MKScore to a scorefile, or you can send MKNotes during a performance to a MKScorefileWriter MKInstrument. The former of these two methods is generally easier and more flexible since it's done statically and allows random access to the MKNotes within a MKPart. The latter allows MKNote objects to be reused since the file is written dynamically; it also lets you record interactive performances.
You can also write individual MKNotes in scorefile format to an open stream by sending writeScorefileStream: to the MKNotes. This can be convenient while debugging, but keep in mind, however, that the method is designed primarily for use by MKScore and MKScorefileWriter objects; if you write MKNotes directly to a stream that's open to a file, the file isn't guaranteed to be recognized by methods that read scorefiles, such as MKScore's readScorefile:.
MKNote are automatically created by the MusicKit in a number of circumstances, such as when reading a MKScorefile. The function MKSetNoteClass() allows you to specify that your own subclass of MKNote be used when MKNotes are automatically created. You retrieve the MKNote class with MKGetNoteClass().