AU automation recording problems in Digital Performer/Logic

A user of my plugin has found some strange bug:

Why does it only work after the user clicked the knob? Seems strange, because it’s not only a GUI issue, but the user also not hears the changes until he clicks the knobs,…

In Logic Express 7 trial (free download), automation recording does not work either. In Ableton it does. Playback works in Logic and Ableton.

Same problem with Juce AU Demo.

I compiled with 1.44 (wrote 1.45 before, which was not correct, I’m trying with 1.45 now).

I’ll add it to my list of AU issues to look at…

Just tried with Juce 1.45. It’s all the same. I think this is an important issue, because Logic is for sure the most important sequencer on Mac. Do you perhaps know what it could be? I checked the AU code, and the only thing I noticed that is more or less different from the FilterDemo AU code is that there are no listeners registered for notifications, although this should not be a problem?

After several hours of searching, it seems I have found what the problem is.
This code is from Destroy FX’s RMS Buddy:

OSStatus RMSBuddyEditor::SendAUParameterEvent(AudioUnitParameterID inParameterID, AudioUnitEventType inEventType)
{
// we’re not actually prepared to do anything at this point if we don’t yet know which AU we are controlling
if (GetEditAudioUnit() == NULL)
return kAudioUnitErr_Uninitialized;

OSStatus result = noErr;

// do the current way, if it's available on the user's system
AudioUnitEvent paramEvent;
paramEvent.mEventType = inEventType;
paramEvent.mArgument.mParameter.mParameterID = inParameterID;
paramEvent.mArgument.mParameter.mAudioUnit = GetEditAudioUnit();
paramEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
paramEvent.mArgument.mParameter.mElement = 0;
if (AUEventListenerNotify != NULL)
	result = AUEventListenerNotify(NULL, NULL, &paramEvent);

//return result; // <---- IF THIS IS UNCOMMENTED, LOGIC 7 WILL STOP RESPONDING TO AUTOMATION

// as a back-up, also still do the old way, until it's enough obsolete
if (mEventListener != NULL)
{
	AudioUnitCarbonViewEventID carbonViewEventType = -1;
	if (inEventType == kAudioUnitEvent_BeginParameterChangeGesture)
		carbonViewEventType = kAudioUnitCarbonViewEvent_MouseDownInControl;
	else if (inEventType == kAudioUnitEvent_EndParameterChangeGesture)
		carbonViewEventType = kAudioUnitCarbonViewEvent_MouseUpInControl;
	(*mEventListener)(mEventListenerUserData, GetComponentInstance(), &(paramEvent.mArgument.mParameter), carbonViewEventType, NULL);
}

return result;

}

I compiled RMSBuddy once normally and it worked in Logic. Then I put the commented line into the code and then after compiling again automation recording stopped working. I also read on some pages that the “oldfashioned” mEventListener way works, whereas the AUEventListenerNotify way can cause strange bugs.

Also check this out: http://lists.apple.com/archives/coreaudio-api/2005/Nov/msg00174.html

Right… So this kind of thing might work?

[code] void sendAUEvent (const AudioUnitEventType type, const int index) throw()
{
if (AUEventListenerNotify != 0)
{
auEvent.mEventType = type;
auEvent.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index;
AUEventListenerNotify (0, 0, &auEvent);
}

    if ((type == kAudioUnitEvent_BeginParameterChangeGesture 
          || type == kAudioUnitEvent_EndParameterChangeGesture)
         && mEventListener != 0)
    {
        (*mEventListener) (mEventListenerUserData, GetComponentInstance(),
                           &(auEvent.mArgument.mParameter),
                           type == kAudioUnitEvent_BeginParameterChangeGesture
                             ? kAudioUnitCarbonViewEvent_MouseDownInControl
                             : kAudioUnitCarbonViewEvent_MouseUpInControl,
                           0);
    }
}

[/code]

(P.S. I’ve not got my mac with me right now to try this out, so haven’t even checked if this compiles…)

I haven’t compiled yet, but I think one would have to first get the mEventListener function’s pointer, which is part of JuceAUView (becaused it inherits from AUCarbonView where the pointer is in), and not of JuceAUBaseClass / AUMIDIEffectBase. Is there some simple way to retrieve the AUCarbonView pointer? If yes, one just has to get the pointer and then it should work.

It’s quite hard to find the view from the effect, but you could do it in the other direction, and pass that function pointer to the effect during the view’s constructor. Might need a bit of hackery to get there.

when I copy the mEventListener and mEventListenerUserData in the JuceAUView constructor to the JuceAU, I just get a crash. I do it like this:

((JuceAU*)GetEditAudioUnit())->mEventListener=mEventListener;
((JuceAU*)GetEditAudioUnit())->mEventListenerUserData=mEventListenerUserData;

I also tried to do it in the CreateUI function, but then it seems like the mEventListener will be set to NULL (or not modified?).

Even if I do this copying in the timerCallback() mEventListener still stays NULL.

What am I doing wrong?

Like I said, it requires a certain amount of cunningness… I’ve checked in a version now that you might want to try out.

I’m having a terrible time trying to build it under 10.5 though - all the apple files are failing to build in stupid ways. What config are you using?

I just found out that the function AUCarbonViewBase::ComponentEntryDispatch() is responsibe for setting the mEventListener. I guess this one never gets called and that’s why the mEventListener stays NULL?

I don’t know what this function is for, are you sure it gets called?

I have an IBook G4 with Tiger 10.4.10. And since my XCode is soooo slow on this machine it’s really PITA for me to code or even compile on it… (when source files get too big, I just have to spend some time drinking coffee until the wheel-of-death dissapears)…

Just tried your code, it starts working :slight_smile: But when I move the slider, the automated value jumps to the selected value once, and then goes back immediately to 0 and stays there even if I continue moving the slider. When I release it, and start over again, it again jumps to the actual slider value and then it goes back to 0 immediately.

Sometimes the automated parameter also changes, but it just changes at position 0 (so I get a straight horizontal automation line which varies in height).

Strange issues here… Anyway, you might check this out with Logic Trial yourself. I can’t find the reasons for this strange behaviour.

BTW that was in Juce Demo AU, forgot to mention. Just added the beginParameterChangeGesture and endParameterChangeGesture functions to the sliderDragStarted and sliderDragEnded listener functions.

Anyone else tried automation in Juce Demo AU with the latest SVN in Logic ?

Jules, if you have any hint where I can look at to finally get rid of this bug (I think there’s another post from August 2007 where the bug is already described!), please let me know. So I can check what I can do.

If you need Logic Express 7 Trial (works fully for 1 week, I think), I can upload it somewhere for you.

Thanks.

All I actually need is some free time to spend looking at this, but that’s quite difficult to find at the moment…

I added code to use the old-fashioned way of sending the start/end gestures, but maybe there’s also an old-fashioned way to send the kAudioUnitEvent_ParameterValueChange event?

You can also use AUParameterSet() instead, but it’s just all the same - same weird automation recording behaviour.

Perhaps any other idea where I could look at? Can’t possibly sell my product with this bug…

Just to respond to this: yes, I am having this problem too. The project’s on hold right now but you may be hearing more from me shortly.

Have you already found something?

Ok. Duh.

The control gestures are not being made in the demo AU.

EDIT:
OLD BAD INFO EDITED OUT

You have to override SliderListener:: sliderDragStarted and sliderDragEnded to call beginParameterChangeGesture and endParameterChangeGesture. Otherwise automation does not work, at least in 10.5.2 and Logic 8.

void DemoEditorComponent::sliderDragStarted(Slider *)
{
	getFilter()->beginParameterChangeGesture(0);
}

void DemoEditorComponent::sliderDragEnded(Slider *)
{
	getFilter()->endParameterChangeGesture(0);
}

Yes, first one has to use the gesture functions. But in my case, it was the Timer that was not working correctly, so I decided to rewrite it using Carbon Timer API and now it works. Check this thread:
http://www.rawmaterialsoftware.com/juceforum/viewtopic.php?t=2650&start=15