MusicKit  0.0.0
MKConductor Class Reference

The MKConductor class defines the mechanism that controls the timing of a MusicKit performance. More...

#import <MKConductor.h>


Detailed Description

The MKConductor class defines the mechanism that controls the timing of a MusicKit performance.

A MKConductor's most important tasks are to schedule the sending of MKNotes by MKPerformers (and MKMidi), and to control the timing of MKEnvelope objects during DSP synthesis. Even in the absence of MKPerformers and MKEnvelopes, you may want to use a MKConductor to take advantage of the convenient scheduling mechanism that it provides.

Each instance of MKConductor contains a message request queue, a list of messages that are to be sent to particular objects at specific times. To enqueue a message request with a MKConductor, you invoke the sel:to:atTime:argCount: or sel:to:withDelay:argCount: method. The former sends a message at a specific time measured in beats from the time the MKConductor started performing, while the latter sends the message a specified number of beats after the request is received. Once you have made a message request through these methods, you can't rescind the action; if you need more control over message requests - for example, if you need to be able to reschedule or remove a request - you should use the following C functions:

For more information on these functions, see Chapter 3, "C Functions."

The MKConductor class provides two special message request queues, one that contains messages that are sent at the beginning of a performance and another for messages that are sent after a performance ends. The class methods beforePerformanceSel:to:argCount: and afterPerformanceSel:to:argCount: enqueue message requests in the before- and after-performance queues, respectively.

A MusicKit performance starts when the MKConductor class receives the startPerformance message. At that time, the MKConductor class sends the messages in its before-performance queue and then the MKConductor instances start processing their individual message request queues. As a message is sent, the request that prompted the message is removed from its queue. The performance ends when the MKConductor class receives finishPerformance, at which time the after-performance messages are sent. Any message requests that remain in the individual MKConductors' message request queues are removed. Note, however, that the before-performance queue isn't similarly cleared. If you invoke beforePerformanceSel:to:argCount: during a performance, the message request will survive a subsequent finishPerformance and will affect the next performance.

By default, if all the MKConductors' queues become empty at the same time (not including the before- and after-performance queues), finishPerformance is invoked automatically. This is convenient if you're performing a MKPart or MKScore and you want the performance to end when all the MKNotes have been played. However, for many applications, such as those that create and perform MKNotes in response to a user's actions, universally empty queues isn't necessarily an indication that the performance is over. To allow a performance to continue even if all the queues are empty, send setFinishWhenEmpty:NO to the MKConductor class.

You can pause and resume an entire performance through methods sent to the MKConductor class:

These messages are ignored if a performance isn't in progress.

You can pause and resume individual MKConductor objects through the pause and resume methods. In addition, you can pause a MKConductor object for a predetermined number of seconds (not beats) through pauseFor:. To offset the begin time of a MKConductor object before a performance starts, invoke setTimeOffset:. Here again, the arguments is taken as seconds. You can also offset the begin time of a MKConductor object by an indeterminate amount of time by sending it the pause message before a performance begins and then sending it resume while the performance is in progress. After a performance has ended, all currently paused MKConductor objects are (virtually) resumed. Thus, a MKConductor object is guaranteed not to be paused when a performance starts (unless, of course, you have specifically sent it the pause message since finishPerformance was last sent).

A MKConductor object can be given a delegate that's sent the hasPaused: message when the MKConductor is paused and hasResumed: when the MKConductor resumes. As in the AppKit's delegate paradigm, a delegate messages is sent only if the delegate responds to it.

The rate at which a MKConductor object processes its message request queue can be set through either the Tempo Protocol or the Time Map Protocol. The Tempo Protocol consists of the following two methods (you may use either):

You can change a MKConductor's tempo anytime, even during a performance. If your application requires multiple simultaneous tempi, you need to create more than one MKConductor, one for each tempo. A MKConductor's tempo is initialized to 60.0 beats per minute.

An alternative way to modify tempo is to use a tempo track or "Time Map". This protocol relies on the MKConductor's delegate to implement two methods that specify the mapping between "beat time" and "clock time." If the delegate implements one of these methods, it must implement both. By implementing these methods, the delegate specifies that it is using the Time Map Protocol. The two methods are beatToClock:from: and clockToBeat:from:. These methods map from pre-tempo to post-tempo time. For details, see the section entitled Music Performance

The responsiveness of a performance to the user's actions depends on whether the MKConductor class is clocked and upon the value of the performance's delta time. By default, the MKConductor class is clocked which means that message request queues are processed in a timely fashion: If, for example, two requests are specified to be sent one beat apart, then the message sending mechanism sends the first message and then, one beat later, sends the second message. When the MKConductor class is clocked, a running NSApplication object is assumed to be present. If you don't need interactive control over a performance, you may find it beneficial to have the messages in the message request queues sent one after another as quickly as possible, while depending on another device, such as the DSP or MIDI drivers, to handle the timing of the actual realization (this is further explained in the descriptions of the MKOrchestra and MKMidi classes). To allow the queues to be processed in this way, you set the MKConductor class to be unclocked by sending it the setClocked:NO message. If you set the MKConductor class to be unclocked, be aware that the startPerformance method doesn't return until the performance is over. (In this situation, sending setFinishWhenEmpty:NO to the MKConductor class is ill-advised since startPerformance would never return.)

Setting a performance's delta time further refines the responsiveness of a performance. Delta time is set through the setDeltaT: class method; the argument defines an imposed time lag, in seconds, between the MKConductor's notion of time and that of the DSP and MIDI device drivers. It acts as a timing cushion that can help to maintain rhythmic integrity by granting your application a sort of computational head start: As you set the delta time to larger values, your application has more time to process MKNotes before they are realized. However, this computational advantage is obtained at the expense of degraded responsiveness. Choosing the proper delta time value depends on how responsive your application needs to be. For example, if you are driving DSP synthesis from MIDI input (in other words, you have a MKMidi object connected to a MKSynthInstrument - this is usually the most demanding scenario in terms of desired real-time response), a delta time of as much as 10 milliseconds (0.01 seconds) is generally acceptable. If you are adjusting MKNote parameters by moving a NSSlider with the mouse, a delta time of 100 milliseconds or more can be tolerated. Finding the right delta time for your application is largely a matter of experimentation.

Every MKConductor instance has a notion of the current time measured in its own tempo, as returned by sending it the time message. The returned value is the number of beats the receiver has spent in performance and doesn't include the receiver's time offset, any time it has spent while paused, nor does it include the performance's delta time. The MKConductor class also responds to the time message; it returns the current duration of the performance in seconds, excluding any time that the entire performance has been paused (and also excluding deltat time). The value returned by the time message, whether sent to the MKConductor class or to an instance, is actually the time at which the last message from any of the MKConductors' queues was sent. This latency is present because the MKConductor class updates its notion of time (from which all the MKConductor instances compute their time) only when a message from one of the request queues is sent. If your application sends a message (or calls a C function) in response to an asynchronous event, it must first update the MKConductors' notions of time by bracketing the code you invoke with [MKConductor lockPerformance] and [MKConductor unlockPerformance]. You should send these messages before performing tasks such as pausing or resuming a MKConductor - you should even send them immediately before sending finishPerformance. If, for yet another example, your application sends MKNotes directly to MKInstruments, you should send lockPerformance immediately before each MKNote is sent and unlockPerformance afterwards. (This API supercedes the older adjustTime, which will still work only if the MusicKit is not run in a separate thread. See +useSeparateThread:.)

MKConductors and MKPerformers have a special relationship: Every MKPerformer object is controlled by an instance of MKConductor, as set through MKPerformer's setConductor: method. While a MKPerformer can be controlled by only one MKConductor, a single MKConductor can control any number of MKPerformers. As a MKPerformer acquires successive MKNotes, it enqueues, with its associated MKConductor, requests for the MKNotes to be sent to its connected MKInstruments. This enqueuing is performed automatically through a mechanism defined by the MKPerformer class. As a convenience, the MusicKit automatically creates an instance of MKConductor called the defaultConductor; if you don't set a MKPerformer's MKConductor directly, it's controlled by the defaultConductor. You can retrieve the defaultConductor (in order to set its tempo or to enqueue message requests, for example) by sending the defaultConductor message to the MKConductor class.

The MusicKit also creates an instance of MKConductor called the clockConductor, which you can retrieve through the clockConductor class method. The clockConductor has an unchangeable tempo of 60.0 beats per minute and can't be paused. While the clockConductor can be used to control MKPerformers, its most important task is to control the timing of MKEnvelope objects during DSP synthesis. All MKEnvelopes are controlled by the clockConductor automatically. The clockConductor also controls the duration of any MKNoteDurs that you send directly to an MKInstrument. In other words, the duration of such a MKNote is always computed using the 60.0 beats-per-minute tempo of the clockConductor.

The clockConductor's queue is treated like any other queue: You can enqueue message requests with the clockConductor just as you would with any other MKConductor. This also means that the clockConductor's queue contributes to a determination of whether all the queues are empty.

MKConductors can synchronize to incoming MIDI time code. This functionality is described in Appendix B. entitled MIDI Time Code in the MusicKit.

See also:
MKPerformer, MKOrchestra, MKMidi

The documentation for this class was generated from the following file: