The MKPerformer Class

A MKPerformer object does two things:

The MKPerformer class itself is abstract; you create a subclass of MKPerformer to correspond to a unique sources of MKNotes. The MusicKit includes subclasses that read MKNotes from a MKPart (MKPartPerformer) or a scorefile (MKScorefilePerformer)―the latter actually inherits from the MKFilePerformer class, a subclass of MKPerformer that defines methods for managing files. The MusicKit also includes pseudo-MKPerformers that fashion MKNotes from MIDI input (MKMidi), and that read MKNotes from a MKScore (MKScorePerformer, which creates a MKPartPerformer for each MKPart in the MKScore). Consult MusicKit Class References for further descriptions of these classes.

Using a MKPerformer object is quite simple; creating your own subclass is a bit more complicated and requires a firm understanding of how a MKPerformer goes about its business. These two topics are presented below.

Using a MKPerformer

To use a MKPerformer, you need to do two things: connect it to an MKInstrument and tell it to go. Every MKPerformer contains some number of MKNoteSenders, auxiliary objects that are created by the MKPerformer to act as MKNote “spigots.” MKNoteSenders are analogous to an MKInstrument's MKNoteReceivers.

To connect a MKPerformer to an MKInstrument, you retrieve a MKNoteSender and MKNoteReceiver from either, respectively, and connect these objects through the connect: method, as defined by both MKNoteSender and MKNoteReceiver. For example, to connect a MKPartPerformer to a MKSynthInstrument, you would do the following:

/* aPartPerformer and aSynthIns are assumed to exist. */ 
[[aMKPartPerformer noteSender] connect:[aSynthIns noteReceiver]];

Since both classes define the connect: method, the following is equivalent:

/* aPartPerformer and aSynthIns are assumed to exist. */
[[aSynthIns noteReceiver] connect: [aPartPerformer noteSender]];

The noteSender method returns one of a MKPerformer's MKNoteSenders, just as the noteReceiver method retrieves one of an MKInstrument's MKNoteReceivers. If you're using a MusicKit MKPerformer subclass, you should refer to its description to determine if it creates more than one MKNoteSender. If it creates only one, then the noteSender method is sufficient. If it creates more than one, you can retrieve the entire set as a NSArray through the noteSenders method and then choose the MKNoteSender that you want by plucking it from the NSArray. A MKScorefilePerformer, for example, creates a MKNoteSender for each MKPart that's represented in its scorefile.

Activating a MKPerformer

To make a MKPerformer run, you send it the activate message. This prepares the object for a performance but it doesn't actually start performing MKNotes until you send startPerformance to the MKConductor class. If you invoke activate while a performance is in progress (in other words, after you send startPerformance), the MKPerformer will immediately start running. In addition, the MKPerformer may require subclass-specific preparation; for example, you have to set a MKPartPerformer's MKPart before you send it the activate message.

While a MKPerformer is running, you can pause and resume its activity through the pause, pauseFor:, and resume methods. To completely stop a MKPerformer you invoke deactivate. In addition, all MKPerformers are automatically deactivated when the MKConductor class receives the finishPerformance message. A MKPerformer can be given a delegate object that can be designed to respond to the messages performerDidActivate:, performerDidPause:, performerDidResume:, and performerDidDeactivate:. These messages are sent by the MKPerformer at the obvious junctures in its performance.

MKPerformers and MKConductors

Every MKPerformer object is associated with a MKConductor. If you don't set a MKPerformer's MKConductor explicitly (through setConductor:), it will be associated with the defaultConductor. The rate at which a MKPerformer performs its MKNotes is controlled by its MKConductor's tempo. In general, all the MKPerformers you create can be associated with the same MKConductor. The only case in which a MKPerformer demands its own MKConductor is if you want the MKPerformer to proceed at a different tempo from its fellow MKPerformers.

Creating a MKPerformer Subclass

The design of a MKPerformer subclass must address three tasks: acquiring a MKNote, sending it into a performance, and scheduling the next MKNote.

Acquiring MKNotes

Each subclass of MKPerformer defines a unique system for acquiring MKNotes. You can design your own MKPerformers that, for example, read MKNotes from a specialized database or create MKNotes algorithmically. Regardless of how a MKPerformer acquires its MKNotes, it does so as part of the implementation of its perform method.

The perform method can be designed to acquire any number of MKNotes with a single invocation.

Sending MKNotes

To send a MKNote into a performance, a MKPerformer relies on its MKNoteSender objects. A MKPerformer creates and adds some number of MKNoteSenders to itself, usually as part of its init method. MKNoteSenders are created through the usual sequence of alloc and init messages; they're added to a MKPerformer through MKPerformer's addNoteSender: method. A MKPerformer can add any number of MKNoteSenders to itself, although it's anticipated that most MKPerformers will need only one.

As part of its implementation of theperform method, a MKPerformer passes the MKNote it has acquired as the argument in a sendNote: message, which it sends to its MKNoteSenders. Each MKNoteSender then relays the MKNote to the MKNoteReceivers to which it's connected; each MKNoteReceiver passes the MKNote to the MKInstrument that it (the MKNoteReceiver) belongs to. Thus, by sending sendNote: to a MKNoteSender, a MKPerformer communicates MKNotes to one or more MKInstruments. If more than one MKNote is acquired in the perform method, each is sent in a separate sendNote: message.

MKNote: Methods that are invoked from within the perform method―and this includes the sendNote: method―shouldn't be bracketed by lockPerformance and unlockPerformance.

Scheduling MKNotes

As described above, every time a MKPerformer receives the perform message it acquires a MKNote and then sends it to its MKNoteSenders. The final obligation of the perform method is to schedule its own next invocation. This is done by setting the value of the nextPerform instance variable. The value of nextPerform is measured in beats according to the tempo of the MKConductor and, most important, it's taken as a time delay: If you set nextPerform to 3.0, for example, the perform method will be invoked after 3.0 beats.

To get things started, a MKPerformer's first perform message is automatically scheduled to be sent just after the MKPerformer is activated. You can delay this initial invocation by setting the nextPerform variable from within the activateSelf method. The default implementation of activateSelf does nothing; a subclass can implement it to provide pre-performance initialization just such as this.

An important implication of this scheduling mechanism is that a MKPerformer must be able to determine when it wants to perform its next MKNote at the time that it acquires and performs its current MKNote.