Handling Audio Unit Events

This technote describes how to use AudioUnitEvents to send and receive notifications about an Audio Unit's changing state, whether those changes are due to "gestures", or changes in the values of parameters or properties.

Using the AudioUnitEvent API, Audio Units, Audio Unit Hosts, and their UIs now have a unified method of sending and receiving notifications of events. Notifications of events can be sent and received without the need of different implementations for both Carbon and Cocoa UIs. This greatly simplifies the interaction between Audio Units, their hosts, and their UIs.





There are three different types of events that can fall into the AudioUnitEvent category:

  • Changes to an Audio Unit Property value.

  • Signaling begin or end of Audio Unit Parameter change gestures

The AudioUnitEvent API is simply a mechanism for sending notification of these types of changes. This new implementation builds on top of the existing Core Audio API for Audio Unit property and parameter listening. This new implementation allows better cooperation among clients by using AudioUnitEvents to be aware of what the other client is doing. Now, when an Audio Unit host changes an Audio Unit's parameter (or property value), that change can be reflected in the host's GUI, and vice versa.

This new API also provides a universal notification of signaling begin or end parameter change gestures. An example of a gesture would be a user clicking and dragging a slider on an UI. The begin gesture in this case would be the users first click, whereas the end gesture would be when the user releases the mouse button. This is much like Carbon mouse click events, but now a developer can expand the definition of gestures to events other than just mouse clicks (such as pressing a key on the keyboard).

AudioUnitEvents and AUEventListeners

Audio Unit Events are comprised of an AudioUnitEventType and possibly an argument, which can refer to either an AudioUnitParameter or an AudioUnitProperty.

Listing 1: The AudioUnitEvent struct as defined in AudioUnitUtilities.h

typedef struct AudioUnitEvent {
   AudioUnitEventType        mEventType;
    union {
          AudioUnitParameter      mParameter;
          AudioUnitProperty       mProperty;
    }  mArgument;
} AudioUnitEvent;

An AudioUnitEventType will describe an AudioUnitEvent. Event types are restricted to the following four types of events: begin and end gestures, parameter changes, and property changes. If the intended use of an AudioUnitEvent is to send a notification of AudioUnitParameter activity, then the only event types that may be used are kAudioUnitEvent_ParameterValueChange, kAudioUnitEvent_BeginParameterChangeGesture, or kAudioUnitEvent_EndParameterChangeGesture. Similarly, an AudioUnitProperty can only use kAudioUnitEvent_PropertyChange as the event type.

Listing 2: AudioUnitEventType as defined in AudioUnitUtilities.h

typedef UInt32 AudioUnitEventType;

enum {
    kAudioUnitEvent_ParameterValueChange           = 0,
    kAudioUnitEvent_BeginParameterChangeGesture    = 1,
    kAudioUnitEvent_EndParameterChangeGesture      = 2,
    kAudioUnitEvent_PropertyChange                 = 3
};

Listeners can be created to monitor changes in the parameters or properties of an Audio Unit. The AUEventListenerCreate method (defined in <AudioToolbox/AudioUnitUtilities.h>) will create an AUEventListener for your Audio Unit. To correctly create an event listener with the AUEventListenerCreate method, you must include the CFRunLoop and the CFRunLoop mode on which you intend to receive AudioUnitEvent notifications. While these notifications may be issued from real-time priority threads, they may be received on another run loop that can be specified when the event listener is created. When using AUEventListenerCreate, you must also specify the interval between calls to the event listener proc, as well as the granularity with which the parameter value changes will queued by the event listener proc. This will improve performance because it defines the correct frequency for notifications to occur, which enables Audio Units and hosts to react more appropriately to an event. For example, when an event follows a previous one by a smaller time interval than the granularity, then the listener will only be notified for the second parameter change. This can be very helpful depending on the type of Audio Unit or host being created. Please refer to AudioUnitUtilities.h for more information and examples of notification intervals and value change granularity.

After the event listener is created, you may add events it will listen for by using AUEventListenerAddEventType. The AUEventListenerAddEventType method requires an AUEventListenerRef and an AudioUnitEvent.

Listing 3: The AUEventListenerProc callback as defined in AudioUnitUtilities.h

typedef void (*AUEventListenerProc) (void *        inCallbackRefCon,
                                     void *        inObject,
                                     const AudioUnitEvent *        inEvent,
                                     UInt64            inEventHostTime,
                                     Float32           inParameterValue);

Listing 4: AUListenerCreate declaration as defined in AudioUnitUtilities.h

extern OSStatus
     AUListenerCreate(AUParameterListenerProc    inProc,
                         void *inRefCon,
                         CFRunLoopRef inRunLoop,
                         CFStringRef inRunLoopMode,
                         Float32 inNotificationInterval,
                         AUParameterListenerRef *outListener);

Listing 5: Creating an Audio Unit Event Listener

OSStatus createListener(){

    OSStatus result = noErr;
    AudioUnitProperty  myProperty;
    AudioUnitEvent  myPropertyEvent;
    AUEventListenerRef MyListener;

    //this may be a run loop of your choosing
    CFRunLoopRef runLoop =
           (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetCurrentEventLoop());
    CFStringRef loopMode = kCFRunLoopDefaultMode;

    myPropertyEvent.mEventType = kAudioUnitEvent_PropertyChange;
    myPropertyEvent.mArgument.mProperty = myProperty;

    AUEventListenerCreate(MyPropertyListener,
                                NULL,
                                runLoop,
                                loopMode,
                                100,
                                100,
                                &MyListener);
    AUEventListenerAddEventType(MyListener, NULL, &myPropertyEvent );

    return result;
}

Back to Top 

Handling Parameter Changes

Audio Unit parameters are used to modify the behavior of the rendering process of an Audio Unit. Parameters can be used to modify volume, gain, delay and many other behaviors that would have an effect on an Audio Unit. These changes are often applied in real time and notifications of these changes can be sent and received through an AudioUnitEvent.

Parameter change notifications can be sent by an Audio Unit or host using AUEventListenerNotify. The AudioUnitEvent must have kAudioUnitEvent_ParameterValueChange as the type and an AudioUnitParameter for the argument. The events are delivered serially to the listener interspersed with parameter changes, preserving the time order of the events and parameter changes. This is much like what AUParameterListener already does, but in a more general case. An AUParameterListener can generate theses notifications by using AUParameterSet and AUParameterListenerNotify.

The AUEventListener API's extend the above AUParameterListener API's by adding a semantic of events other than parameter changes.

Listing 6: Short example of creating an AudioUnitEvent (Parameter Change) and notifying AUEventListener

    AudioUnitEvent myEvent;
    myEvent.mEventType = kAudioUnitEvent_ParameterValueChange;
    myEvent.mArgument.mParameter.mAudioUnit = myAudioUnit;
    myEvent.mArgument.mParameter.mParameterID = parameterID;
    myEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
    myEvent.mArgument.mParameter.mElement = 0;
    AUEventListenerNotify(NULL, NULL, &myEvent);

Back to Top 

Handling Property Changes

Properties represent a general and extensible mechanism for passing information to and from Audio Units. The type of information passed by properties can be managed by listening for AudioUnitEvents. Actions both internal and external to an Audio Unit can change a property's value.

For instance, an output audio unit that tracks the device that a user chooses in the Sound Preferences pane is the Default Output Unit. The device can be changed at any time through user interaction. If a program has an instance of the DefaultOutputUnit open, a property listener for the kAudioOutputUnitProperty_CurrentDevice property can be established to allow the program to detect that change. Alternatively, the program may not care about the particular device that the DefaultOutputUnit is connected to, but may care about the format of that device, e.g. the sample rate of the device. To detect that property has changed, a listener can also be established beforehand to notify that a property value has changed. Then the host or Audio Unit can take any appropriate actions that it needs to.

Property change notifications can be implemented by using an AudioUnitEvent. The AudioUnitEvent must have an AudioUnitProperty and kAudioUnitEvent_PropertyChange as the type. After a listener is created using AUEventListenerCreate, the Audio Unit or host can receive these notifications safely on the thread where it will be most applicable instead of the thread where the change actually occurred. This is particularly useful for notifications which are generated on an audio rendering thread (such threads typically run with realtime priority).

Listing 7: Short example of creating an AudioUnitEvent (Property Change) and notifying AUEventListener

    AudioUnitEvent myEvent;
    myEvent.mEventType = kAudioUnitEvent_PropertyChange;
    myEvent.mArgument.mProperty.mAudioUnit = myAudioUnit;
    myEvent.mArgument.mProperty.mPropertyID = PropertyID;
    myEvent.mArgument.mProperty.mScope = kAudioUnitScope_Global;
    myEvent.mArgument.mProperty.mElement = 0;
    AUEventListenerNotify(NULL, NULL, &myEvent);

Back to Top 

Notifications of begin/end gestures

Audio Units have a general method of sending begin and end gesture notifications. Begin and end gesture notifications send a signal to say that a parameter is about to be changed or has finished changing. This enables both Carbon and Cocoa UIs to send and receive these types of event notifications from user interactions, then decide what to do with that information. This new mechanism generalizes the definition of gestures so that any interaction with the GUI can be interpreted as a gesture, therefore removing the limitation of mouse clicks (mouse up, mouse down) as the only valid gesture that can be interpreted. This will eventually deprecate the Carbon view events kAudioUnitCarbonViewEvent_MouseDownInControl and kAudioUnitCarbonViewEvent_MouseUpInControl.

For example, a user may want to click and drag a parameter button on an Audio Unit's GUI to change its value. The UI can be made to react to the user's selection by changing the button color to provide feedback to the user that a parameter value is about to be changed. After the parameter button is released, an end parameter change gesture notification can be sent out to revert the button color.

Begin and end gesture notifications have no data associated with them. Using AUEventListenerNotify with the event types kAudioUnitEvent_BeginParameterChangeGesture, or kAudioUnitEvent_EndParameterChangeGesture will ignore parameter argument value if one exists. AudioUnitParameter data will only be passed through an AudioUnitEvent if the event is of the kAudioUnitEvent_ParameterValueChange type.

Listing 8: Short example of managing parameter value changes using begin & end gestures

void CreateAudioUnitEventForParameterID(AudioUnitEvent *myEvent,
                                        AudioUnitParameterID parameterID){

    myEvent->mArgument.mParameter.mAudioUnit = myAudioUnit;
    myEvent->mArgument.mParameter.mParameterID = parameterID;
    myEvent->mArgument.mParameter.mScope = kAudioUnitScope_Global;
    myEvent->mArgument.mParameter.mElement = 0;

}

void HandleMouseDown(AudioUnitEvent *myEvent){
    myEvent->mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
    AUEventListenerNotify(NULL, NULL, myEvent);
}

void HandleParameterChange(AudioUnitEvent *myEvent){
    myEvent->mEventType =kAudioUnitEvent_ParameterValueChange;
    AUEventListenerNotify(NULL, NULL, myEvent);
}

void HandleMouseUp(AudioUnitEvent *myEvent){
    myEvent->mEventType = kAudioUnitEvent_EndParameterChangeGesture;
    AUEventListenerNotify(NULL, NULL, myEvent);
}

Back to Top 

Summary

The AudioUnitEvent API enables Audio Units, Audio Unit hosts, and their GUIs to communicate more effectively. As a result, this will greatly simplify the interaction between these objects.

Back to Top 

References

Audio and MIDI on Mac OS X (May 2001)

Back to Top 

Document Revision History

DateNotes
2005-04-29AUEvent notifications clarification.
2004-02-16This technote explains how to handle AudioUnit Events

Posted: 2005-04-29


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.