AudioUnitEffectExample/Source/CocoaUI/AppleDemoFilter_UIView.m
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
*/ |
#import "AppleDemoFilter_UIView.h" |
enum |
{ |
kFilterParam_CutoffFrequency = 0, |
kFilterParam_Resonance = 1 |
}; |
extern NSString *kGraphViewDataChangedNotification; // notification broadcast by the view when the user has changed the resonance |
// or cutoff values via directly mousing in the graph view |
extern NSString *kGraphViewBeginGestureNotification;// notification broadcast by the view when the user has started a gesture |
extern NSString *kGraphViewEndGestureNotification; // notification broadcast by the view when the user has finished a gesture |
#pragma mark ____ LISTENER CALLBACK DISPATCHER ____ |
// This listener responds to parameter changes, gestures, and property notifications |
void EventListenerDispatcher (void *inRefCon, void *inObject, const AudioUnitEvent *inEvent, UInt64 inHostTime, Float32 inValue) |
{ |
AppleDemoFilter_UIView *SELF = (AppleDemoFilter_UIView *)inRefCon; |
[SELF priv_eventListener:inObject event: inEvent value: inValue]; |
} |
@implementation AppleDemoFilter_UIView |
-(void) awakeFromNib { |
NSString *path = [[NSBundle bundleForClass: [AppleDemoFilter_UIView class]] pathForImageResource: @"SectionPatternLight"]; |
NSImage *pattern = [[NSImage alloc] initByReferencingFile: path]; |
mBackgroundColor = [[NSColor colorWithPatternImage: [pattern autorelease]] retain]; |
} |
#pragma mark ____ (INIT /) DEALLOC ____ |
- (void)dealloc { |
[self priv_removeListeners]; |
[mBackgroundColor release]; |
[[NSNotificationCenter defaultCenter] removeObserver: self]; |
free (mData); |
[super dealloc]; |
} |
#pragma mark ____ PUBLIC FUNCTIONS ____ |
- (void)setAU:(AudioUnit)inAU { |
// remove previous listeners |
if (mAU) |
[self priv_removeListeners]; |
if (!mData) // only allocate the data once |
mData = malloc(kNumberOfResponseFrequencies * sizeof(FrequencyResponse)); |
mData = [graphView prepareDataForDrawing: mData]; // fill out the initial frequency values for the data displayed by the graph |
// register for resize notification and data changes for the graph view |
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleGraphDataChanged:) name: kGraphViewDataChangedNotification object: graphView]; |
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleGraphSizeChanged:) name: NSViewFrameDidChangeNotification object: graphView]; |
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(beginGesture:) name: kGraphViewBeginGestureNotification object: graphView]; |
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(endGesture:) name: kGraphViewEndGestureNotification object: graphView]; |
mAU = inAU; |
// add new listeners |
[self priv_addListeners]; |
// initial setup |
[self priv_synchronizeUIWithParameterValues]; |
} |
- (void)drawRect:(NSRect)rect |
{ |
[mBackgroundColor set]; |
NSRectFill(rect); // this call is much faster than using NSBezierPath, but it doesn't handle non-opaque colors |
[super drawRect: rect]; // we call super to draw all other controls after we have filled the background |
} |
#pragma mark ____ INTERFACE ACTIONS ____ |
- (IBAction) cutoffFrequencyChanged:(id)sender { |
float floatValue = [sender floatValue]; |
AudioUnitParameter cutoffParameter = {mAU, kFilterParam_CutoffFrequency, kAudioUnitScope_Global, 0 }; |
NSAssert( AUParameterSet(mAUEventListener, sender, &cutoffParameter, (Float32)floatValue, 0) == noErr, |
@"[AppleDemoFilter_UIView cutoffFrequencyChanged:] AUParameterSet()"); |
} |
- (IBAction) resonanceChanged:(id)sender { |
float floatValue = [sender floatValue]; |
AudioUnitParameter resonanceParameter = {mAU, kFilterParam_Resonance, kAudioUnitScope_Global, 0 }; |
NSAssert( AUParameterSet(mAUEventListener, sender, &resonanceParameter, (Float32)floatValue, 0) == noErr, |
@"[AppleDemoFilter_UIView resonanceChanged:] AUParameterSet()"); |
} |
- (void) handleGraphDataChanged:(NSNotification *) aNotification { |
float resonance = [graphView getRes]; |
float cutoff = [graphView getFreq]; |
AudioUnitParameter cutoffParameter = {mAU, kFilterParam_CutoffFrequency, kAudioUnitScope_Global, 0 }; |
AudioUnitParameter resonanceParameter = {mAU, kFilterParam_Resonance, kAudioUnitScope_Global, 0 }; |
NSAssert( AUParameterSet(mAUEventListener, cutoffFrequencyField, &cutoffParameter, (Float32)cutoff, 0) == noErr, |
@"[AppleDemoFilter_UIView cutoffFrequencyChanged:] AUParameterSet()"); |
NSAssert( AUParameterSet(mAUEventListener, resonanceField, &resonanceParameter, (Float32)resonance, 0) == noErr, |
@"[AppleDemoFilter_UIView resonanceChanged:] AUParameterSet()"); |
} |
- (void) handleGraphSizeChanged:(NSNotification *) aNotification { |
mData = [graphView prepareDataForDrawing: mData]; // the size of the graph has changed so we need the graph to reconfigure the data frequencies that it needs to draw |
// get the curve data from the audio unit |
UInt32 dataSize = kNumberOfResponseFrequencies * sizeof(FrequencyResponse); |
ComponentResult result = AudioUnitGetProperty( mAU, |
kAudioUnitCustomProperty_FilterFrequencyResponse, |
kAudioUnitScope_Global, |
0, |
mData, |
&dataSize); |
if (result == noErr) |
[graphView plotData: mData]; // ask the graph view to plot the new data |
else if (result == kAudioUnitErr_Uninitialized) |
[graphView disableGraphCurve]; |
} |
- (void) beginGesture:(NSNotification *) aNotification { |
AudioUnitEvent event; |
AudioUnitParameter parameter = {mAU, kFilterParam_CutoffFrequency, kAudioUnitScope_Global, 0 }; |
event.mArgument.mParameter = parameter; |
event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture; |
AUEventListenerNotify (mAUEventListener, self, &event); |
event.mArgument.mParameter.mParameterID = kFilterParam_Resonance; |
AUEventListenerNotify (mAUEventListener, self, &event); |
} |
- (void) endGesture:(NSNotification *) aNotification { |
AudioUnitEvent event; |
AudioUnitParameter parameter = {mAU, kFilterParam_CutoffFrequency, kAudioUnitScope_Global, 0 }; |
event.mArgument.mParameter = parameter; |
event.mEventType = kAudioUnitEvent_EndParameterChangeGesture; |
AUEventListenerNotify (mAUEventListener, self, &event); |
event.mArgument.mParameter.mParameterID = kFilterParam_Resonance; |
AUEventListenerNotify (mAUEventListener, self, &event); |
} |
void addParamListener (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)); |
} |
#pragma mark ____ PRIVATE FUNCTIONS ____ |
- (void)priv_addListeners |
{ |
if (mAU) { |
verify_noerr( AUEventListenerCreate(EventListenerDispatcher, self, |
CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0.05, 0.05, |
&mAUEventListener)); |
AudioUnitEvent auEvent; |
AudioUnitParameter parameter = {mAU, kFilterParam_CutoffFrequency, kAudioUnitScope_Global, 0 }; |
auEvent.mArgument.mParameter = parameter; |
addParamListener (mAUEventListener, self, &auEvent); |
auEvent.mArgument.mParameter.mParameterID = kFilterParam_Resonance; |
addParamListener (mAUEventListener, self, &auEvent); |
/* Add a listener for the changes in our custom property */ |
/* The Audio unit will send a property change when the unit is intialized */ |
auEvent.mEventType = kAudioUnitEvent_PropertyChange; |
auEvent.mArgument.mProperty.mAudioUnit = mAU; |
auEvent.mArgument.mProperty.mPropertyID = kAudioUnitCustomProperty_FilterFrequencyResponse; |
auEvent.mArgument.mProperty.mScope = kAudioUnitScope_Global; |
auEvent.mArgument.mProperty.mElement = 0; |
verify_noerr (AUEventListenerAddEventType (mAUEventListener, self, &auEvent)); |
} |
} |
- (void)priv_removeListeners |
{ |
if (mAUEventListener) verify_noerr (AUListenerDispose(mAUEventListener)); |
mAUEventListener = NULL; |
mAU = NULL; |
} |
- (void) updateCurve { |
UInt32 dataSize = kNumberOfResponseFrequencies * sizeof(FrequencyResponse); |
ComponentResult result = AudioUnitGetProperty( mAU, |
kAudioUnitCustomProperty_FilterFrequencyResponse, |
kAudioUnitScope_Global, |
0, |
mData, |
&dataSize); |
if (result == noErr) |
[graphView plotData: mData]; // plot the new curve data and redraw the graph |
else if (result == kAudioUnitErr_Uninitialized) |
[graphView disableGraphCurve]; |
} |
- (void)priv_synchronizeUIWithParameterValues { |
Float32 freqValue, resValue; |
AudioUnitParameter parameter = {mAU, kFilterParam_CutoffFrequency, kAudioUnitScope_Global, 0 }; |
NSAssert ( AudioUnitGetParameter(mAU, kFilterParam_CutoffFrequency, kAudioUnitScope_Global, 0, &freqValue) == noErr, |
@"[AppleDemoFilter_UIView priv_synchronizeUIWithParameterValues] (x.1)"); |
parameter.mParameterID = kFilterParam_Resonance; |
NSAssert ( AudioUnitGetParameter(mAU, kFilterParam_Resonance, kAudioUnitScope_Global, 0, &resValue) == noErr, |
@"[AppleDemoFilter_UIView priv_synchronizeUIWithParameterValues] (x.1)"); |
[cutoffFrequencyField setFloatValue: freqValue]; // update the frequency text field |
[graphView setFreq: freqValue]; // update the graph's frequency visual state |
[resonanceField setFloatValue: resValue]; // update the resonance text field |
[graphView setRes: resValue]; // update the graph's gain visual state |
[self updateCurve]; |
} |
#pragma mark ____ LISTENER CALLBACK DISPATCHEE ____ |
// Handle kAudioUnitProperty_PresentPreset event |
- (void)priv_eventListener:(void *) inObject event:(const AudioUnitEvent *)inEvent value:(Float32)inValue { |
switch (inEvent->mEventType) { |
case kAudioUnitEvent_ParameterValueChange: // Parameter Changes |
switch (inEvent->mArgument.mParameter.mParameterID) { |
case kFilterParam_CutoffFrequency: // handle cutoff frequency parameter |
[cutoffFrequencyField setFloatValue: inValue]; // update the frequency text field |
[graphView setFreq: inValue]; // update the graph's frequency visual state |
break; |
case kFilterParam_Resonance: // handle resonance parameter |
[resonanceField setFloatValue: inValue]; // update the resonance text field |
[graphView setRes: inValue]; // update the graph's gain visual state |
break; |
} |
// get the curve data from the audio unit |
[self updateCurve]; |
break; |
case kAudioUnitEvent_BeginParameterChangeGesture: // Begin gesture |
[graphView handleBeginGesture]; // notify graph view to update visual state |
break; |
case kAudioUnitEvent_EndParameterChangeGesture: // End gesture |
[graphView handleEndGesture]; // notify graph view to update visual state |
break; |
case kAudioUnitEvent_PropertyChange: // custom property changed |
if (inEvent->mArgument.mProperty.mPropertyID == kAudioUnitCustomProperty_FilterFrequencyResponse) |
[self updateCurve]; |
break; |
} |
} |
/* If we get a mouseDown, that means it was not in the graph view, or one of the text fields. |
In this case, we should make the window the first responder. This will deselect our text fields if they are active. */ |
- (void) mouseDown: (NSEvent *) theEvent { |
[super mouseDown: theEvent]; |
[[self window] makeFirstResponder: self]; |
} |
- (BOOL) acceptsFirstResponder { |
return YES; |
} |
- (BOOL) becomeFirstResponder { |
return YES; |
} |
- (BOOL) isOpaque { |
return YES; |
} |
@end |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-02-19