Creating a MKUnitGenerator Subclass

Each MKUnitGenerator subclass is an object-oriented interface to a DSP macro, called a unit generator, that's written in MC56001 assembly language. The Objective-C code in the subclass is generated automatically from a DSP macro by the dspwrap program. To build your own MKUnitGenerator subclass that you can use in MKSynthPatch design, you run dspwrap on a MC56001 assembly language macro that you've created. In addition, you can modify and wrap the DSP macros that NeXT provides as source code in the directory /usr/local/lib/dsp/ugsrc.

The design of DSP assembly language macros is outside the scope of the present discussion. The following sections show how to use dspwrap and how to further modify the MKUnitGenerators that it creates.

Using dspwrap

The dspwrap program is used to create array processing C functions as well as MKUnitGenerator subclasses. To indicate that you want to create the latter, you call the program with the -ug switch followed by the name of the file that contains the assembly code macro (you must include the “.asm” extension when specifying the file). For example, the invocation

dspwrap -ug unoisehp.asm

creates a master MKUnitGenerator class called UnoisehpUG as well as the appropriate leaf classes. These are embodied in the following files, which are automatically generated by dspwrap:

The number of leaf classes that are created depends on the number of address-valued memory arguments, described below, in the macro: A different leaf class is created for each combination of x and y DSP memory spaces. The unoisehp macro, which implements a high-pass random number generator, has only one such argument―its output―so two leaf classes are generated, one for either memory space.

Some other files, such as documentation and DSP assembler and linker files, are also created. These can be moved, deleted, or disregarded as you see fit. For the present purposes, only the files listed above are important.

Modifying the Class

There are two basic reasons to modify a MKUnitGenerator class that's generated by dspwrap:

Of the files generated by dspwrap, you should modify only those that define the master class. In other words, continuing with the Unoisehp example, only UnoisehpUG.m and UnoisehpUG.h should be edited. The entire implementation file of the UnoisehpUG master class as generated by dspwrap is shown below:

#import <MusicKit/MusicKit.h>
#import "UnoisehpUG.h"
@implementation UnoisehpUG:MKUnitGenerator

/* DSP memory arguments. */
enum args { aout, seed};
#import "unoisehpUGInclude.m"

Setting the Arguments

The two enum variables shown above, aout and seed, are DSP memory arguments. A memory argument represents a location on the DSP from which the unit generator that's executing can read information sent to it by the MusicKit. There are two types of memory arguments:

  • Address-valued arguments administer the location in DSP memory from which, or to which, the executing unit generator reads or writes data.

  • Datum-valued arguments take a value that's used as part of the unit generator's computation.

In the example, aout variable is an address-valued argument that represents the MKUnitGenerator's output patchpoint. We create a Unoisehp method named setOutput: that sets this argument:

-setOutput:outputPatchPoint
{
    return [self setAddressArg:aout to:outputPatchPoint];
}

The setAddressArg:to: method is defined by MKUnitGenerator to set an address-valued DSP memory argument.

The other enum variable, seed, is a datum-valued memory argument. It's set through MKUnitGenerator's setDatumArg:to: method, as demonstrated in our implementation of Unoisehp's setSeed:method:

-setSeed:(int)aSeed
{
    return [self setDatumArg:seed to:aSeed];
}

Defining the Class's Response

During a performance, a MKUnitGenerator can expect to receive the following messages:

  • run tells the receiver to begin doing whatever it does.

  • finish winds down the receiver before coming to a halt.

  • idle provides instructions for halting the receiver's activity.

Invocations of these methods were shown in the implementations of the Simplicity and Envy MKSynthPatches. Remember that finish returns the amount of time the MKUnitGenerator needs to complete its mission―the amount of time to wait before idle should be sent. The default implementations of these methods can be sufficient. For further tuning, you should implement, in your master class, the methods runSelf, finishSelf, and idleSelf―methods that are automatically invoked when the corresponding performance messages are received.

You almost always provide an implementation of idleSelf to ensure that your MKUnitGenerator is brought to a halt in a manner befitting its activity. The Unoisehp implementation of this method sets its output to the DSP's sink, a location that, by convention, is never read:

-idleSelf
{
    /* Set the output (aout) to sink. */
    [self setAddressArgToSink:aout];
    return self;
}