NSSlider correct event handling in Audio Unit with Cocoa GUI
NSSlider correct event handling in Audio Unit with Cocoa GUI
- Subject: NSSlider correct event handling in Audio Unit with Cocoa GUI
- From: Salda Van Mörlingam <email@hidden>
- Date: Fri, 17 Oct 2008 16:28:09 +0000
- Importance: Normal
Hi everyone,
I apologize in advance if this is not the right list to post on, but my question relates both to Audio Units and Cocoa.
I'm currently developing an AU with a Cocoa Custom GUI and I'm having trouble when handling begin and end gestures for NSSliders in my GUI. I'm not sure if I'm incorrectly implementing my AU's event handling code, or my custom NSSlider's notification posting code, or both.
My understanding for the Apple docs is that when the user (through a slider in the GUI) or an AU host (in this test, Logic 8) changes a parameter in the internal state of my AU, it does so through a call to AUParameterSet. This change in the internal state of the AU should also be accompanied by events (through AUEventListenerNotify) in the following order:
1 send a kAudioUnitEvent_BeginParameterChangeGesture event
2 send a stream of KAudioUnitEvent_ParameterValueChange events (while the parameter takes different values)
3 send a kAudioUnitEvent_EndParameterChangeGesture event, upon completion of parameter change.
In the case of my AU, I have a main view and another view (inside the main view) with a graph. The graph in the subview, is only changed through sliders in the main view (8 slider cells in an NSMatrix and 2 separate sliders). my understanding is that event handling should be as follows:
. if the user changes a parameter from the GUI:
1. my view tells the subview to update its graph
2. my view changes the internal state of the AU through a call to AUParameterSet
3. the call to AUParameterSet further issues an AUEventListenerNotify call that (potentially) alerts the host to update its automation data for the parameter.
. if the host changes a parameter (through AUParameterSet, when playing back automation, for instance):
1. my view receives the AudioUnitEvent
2. the code for event handling in my view tells the subview to update its graph.
First, the fact that I have duplicated code to update the graph both in my main view's Target-Action methods (for the case where the user moves the sliders) and in the event handling code (for when the event results from the host's call to AUParameterSet) makes me thing I'm implementing the incorrect way.
Second, I'm experiencing the following erratic behavior:
. if I have a single instance of my AU running, automation seems to work fine. That is, I can record automation into Logic by moving the sliders and I can replay automation from logic and both the AU's internal state and interface respond to it.
. if I have two or more instances of my AU running, automation on the last instantiated AU works just fine (same as above), but instances before it cannot correctly record automation to the host (through movement of the sliders) anymore. Specifically, it results in the host not receiving value updates until the next parameter change, as if events were being queued but not delivered on time.
I suspect the main problem is that I don't understand how I should implement event dispatch in the case of NSSlider's modal-loop on mousedown. I resorted to the NSNotificationCenter and might be doing something wrong.
Following, is the code in my main view that pertains to event handling.
// CocoaView.m
/* an array to hold all possible parameters:
8 corresponding to the NSSliderCell matrix and 2 independent sliders */
AudioUnitParameter parameter[kNumberOfParameters];
void createAUParameterArray()
{
for(int index = 0; index < kNumberOfParameters; index++)
{
parameter[index].mAudioUnit = NULL;
parameter[index].mParameterID = index;
parameter[index].mScope = kAudioUnitScope_Global;
parameter[index].mElement = 0;
}
}
LISTENER CALLBACK DISPATCHER
void eventListenerDispatcher (void *inRefCon, void *inObject, const AudioUnitEvent *inEvent, UInt64 inHostTime, Float32 inValue)
{
myCocoaView *SELF = (myCocoaView *)inRefCon;
[SELF eventListenerHandler:inObject event:inEvent value:inValue];
}
//////////////////
@implementation MyCocoaView
...
-(void) setAU:(AudioUnit)inAU
{
if (mAU) [self removeListeners];
mAU = inAU;
createAUParameterArray();
[self addListeners];
[self synchronizeUIWithParameterValues];
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(beginGesture:) name: kBeginGestureNotification object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(endGesture:) name: kEndGestureNotification object: nil];
}
-(void) beginGesture:(NSNotification *) aNotification
{
id sender = [aNotification object];
parameter[[sender tag]].mAudioUnit = mAU;
AudioUnitEvent event;
event.mArgument.mParameter = parameter[[sender tag]];
event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
AUEventListenerNotify (eventListener, sender, &event);
}
-(void) endGesture:(NSNotification *) aNotification
{
id sender = [aNotification object];
parameter[[sender tag]].mAudioUnit = mAU;
AudioUnitEvent event;
event.mArgument.mParameter = parameter[[sender tag]];
event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
AUEventListenerNotify (eventListener, sender, &event);
}
void addEventsForParameters(AUEventListenerRef listener, void* refCon, AudioUnitEvent *inEvent)
{
inEvent->mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
verify_noerr ( AUEventListenerAddEventType(listener, refCon, inEvent));
inEvent->mEventType = kAudioUnitEvent_EndParameterChangeGesture;
verify_noerr ( AUEventListenerAddEventType(listener, refCon, inEvent));
inEvent->mEventType = kAudioUnitEvent_ParameterValueChange;
verify_noerr ( AUEventListenerAddEventType(listener, refCon, inEvent));
}
-(void) addListeners
{
if (!mAU) return;
verify_noerr
(
AUEventListenerCreate(eventListenerDispatcher, self,CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0.05, 0.05, &eventListener)
);
AudioUnitEvent auEvent;
for(int index = 0; index mArgument.mParameter.mParameterID;
switch(inEvent->mEventType)
{
case kAudioUnitEvent_ParameterValueChange:
switch(parameterID)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
/* update a graph and update slider and texfield values for the slider matrix*/
break;
case kWaveShapingIndex:
/* update a graph and update slider and texfield values for a slider*/
break;
case kOutputLevel:
/* update a graph and update slider and texfield values for another slider*/
break;
}
break;
case kAudioUnitEvent_BeginParameterChangeGesture:
/* do nothing.*/
break;
case kAudioUnitEvent_EndParameterChangeGesture:
/* do nothing*/
break;
}
}
...
@end
And this is my workaround to get NSSlider to post begin, end and change gestures:
#import "NSSliderCustom.h"
extern NSString *kBeginGestureNotification;
extern NSString *kEndGestureNotification;
@implementation NSSliderCustom
-(void) mouseDown:(NSEvent *) theEvent
{
[[NSNotificationCenter defaultCenter] postNotificationName: kBeginGestureNotification object:self];
[super mouseDown:theEvent];
[[NSNotificationCenter defaultCenter] postNotificationName: kEndGestureNotification object:self];
}
@end
Admittedly, the above subclassing of NSSlider doesn't feel right to me. I really think this is the cause of my problem. On the other hand, having to completely re-implement the slider's mouseUp, mouseDragged and mouseDown combo just for it to conform to audio unit events doesn't seem right either.
I'd really appreciate any help.
Thank you in advance for everyone's time (and I apologize for the length of this post).
Regards,
Federico.
_________________________________________________________________
Discover the new Windows Vista
http://search.msn.com/results.aspx?q=windows+vista&mkt=en-US&form=QBRE _______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden