Re: NSSlider correct event handling in Audio Unit with Cocoa GUI
Re: NSSlider correct event handling in Audio Unit with Cocoa GUI
- Subject: Re: NSSlider correct event handling in Audio Unit with Cocoa GUI
- From: Michael Hopkins <email@hidden>
- Date: Fri, 17 Oct 2008 14:48:31 -0700
Salda,
It just so happens that I am currently working on improvements to the
view templates, so your post is very timely.
The workaround you have for handling gestures with the slider is
perfectly fine. NSSlider will not call mouseUp, so you can't override
that. The NSSlider does all it's mouseTracking in mouseDown, and does
not return from that method until the mouse is released, so your
override is fine. I cannot think of an easier way to do what you want
to do.
One thing that I see is incorrect, however, is that you are adding an
observer for the notifications posted by your slider and passing nil
as the last argument. That means that you will get a notification from
other instances of your view. Not just your own slider! I recommend
that you either add a notification for each of your sliders, and pass
the slider as the object, or if you are like me and lazy, check the
object [aNotification object] to make sure that the slider that is
broadcasting the notification is one that is in your instance (and not
some other instance).
It should not be necessary to duplicate code in your action routine.
All you should need to do is call AUParameterSet() with the correct
parameter id and value, and your listener should get called. Thus you
should be able to handle the user changing the parameter via the
slider and via automation the same exact way with a single piece of
code.
Hope this helps!
-michael
On Oct 17, 2008, at 9:28 AM, Salda Van Mörlingam wrote:
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
_______________________________________________
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