Make Juce host listen to Audio Units events


#1

Currently a Juce host does not listen to a hosted AU plugin’s events. Parameter changes do not get propagated to the host, which prevents us from tracking the “dirty” status of a hosted plugin (among other useful applications). This feature is nicely implemented for VST already, but is still lacking from AU.

As I need it for my host, I will attempt to implement it. If anyone else is interested in having this feature implemented too, please chime in! It will be easier if we could join forces, share code and hand it over to Jules when we are done.

This is what I found out so far: Currently a call to AUEventListenerCreate (…) is missing from juce_AudioUnitPluginFormat.mm. From what I’ve read in the Apple docs, that function seems to be key to the feature. I found more docs on this here: http://developer.apple.com/library/mac/#technotes/tn2104/_index.html . As far as I understood this at the moment, the listener callback installed with AUEventListenerCreate basically just needs to call AudioProcessor::sendParamChangeMessageToListeners(), the same way it does for VST. That should not be too difficult to accomplish.

I’ll’ start with this over the next weekend. If anyone else has already done this, please let me know.

Thanks!


#2

Couldn’t resist and got the first hack working already. I had to hack class AudioUnitPluginInstance as follows:

  1. Add a new member:
  1. Add three member functions, a global callback and two methods for install/uninstall:

[code]static void EventListenerDispatcher (void *inRefCon,
void *inObject,
const AudioUnitEvent *inEvent,
UInt64 inHostTime,
Float32 inValue)
{
jassert ((inRefCon != 0) && (inEvent != 0));

AudioUnitPluginInstance* receiver = (AudioUnitPluginInstance*)inRefCon;

if (receiver != 0)
{        
    switch (inEvent->mEventType) 
    {
        case kAudioUnitEvent_ParameterValueChange: 
            receiver->sendParamChangeMessageToListeners (inEvent->mArgument.mParameter.mParameterID, inValue);                
            break;             
        // none of these are handled (yet)..   
        case kAudioUnitEvent_BeginParameterChangeGesture:
        case kAudioUnitEvent_EndParameterChangeGesture:
        case kAudioUnitEvent_PropertyChange:
            break;
    }
}

}

OSStatus setParameterChangeCallback ()
{
AudioUnitEvent myParameterEvent;

OSStatus result = noErr;
myParameterEvent.mEventType = kAudioUnitEvent_ParameterValueChange;
myParameterEvent.mArgument.mParameter.mAudioUnit    = audioUnit;
myParameterEvent.mArgument.mParameter.mParameterID  = 0;
myParameterEvent.mArgument.mParameter.mScope        = kAudioUnitScope_Global;
myParameterEvent.mArgument.mParameter.mElement      = 0;

result = AUEventListenerCreate (EventListenerDispatcher,
                               (void*)this,
                               (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetCurrentEventLoop()),
                               kCFRunLoopDefaultMode,
                               0.05,
                               0.05,
                               &mParameterListener);
if (noErr == result) 
{
    for (int i = 0; i < getNumParameters(); i++)
    {
        myParameterEvent.mArgument.mParameter.mParameterID  = i;
        result = AUEventListenerAddEventType (mParameterListener, 0, &myParameterEvent);
    }
}
return result;

}

OSStatus removeParameterChangeCallback ()
{
return AUListenerDispose (mParameterListener);
}[/code]

  1. Finally hack prepareToPlay() and releaseResources():

-> insert setParameterChangeCallback (); at the end of prepareToPlay() -> insert removeParameterChangeCallback (); in the beginning of releaseResources();
Both protected inside the prepared check.

Violá !

Whether the last step is actually hooked into the best places, I am a little unsure, but it works. A hosted AudioUnit now properly notifies its parameter change listeners, as VST did already. When turning knobs on the AU, my host correctly lights the dirty LED :wink:

BTW: It was an incredible pain to work with the amalgamated include. I am really looking forward to the modules branch getting ready for production.


#3

It might even be better to enable this only when an editor is currently open.

So far this works great for me. No issues found yet.