### Clone the AudioUnit-Examples repository Source: https://github.com/apple/audiounit-examples/blob/main/ReadMe.md Use this command to clone the repository including all necessary submodules. ```bash git clone --recursive https://github.com/apple/AudioUnit-Examples.git ``` -------------------------------- ### Configure Filter Parameters Source: https://context7.com/apple/audiounit-examples/llms.txt Defines parameter IDs and ranges, and implements the constructor and GetParameterInfo to expose controls to the host. ```cpp // Parameter IDs enum { kFilterParam_CutoffFrequency = 0, kFilterParam_Resonance = 1 }; // Parameter ranges const float kMinCutoffHz = 12.0; const float kDefaultCutoff = 1000.0; const float kMinResonance = -20.0; const float kMaxResonance = 20.0; const float kDefaultResonance = 0; // Filter constructor - initialize parameters Filter::Filter(AudioUnit component) : AUEffectBase(component) { SetParameter(kFilterParam_CutoffFrequency, kDefaultCutoff); SetParameter(kFilterParam_Resonance, kDefaultResonance); SetParamHasSampleRateDependency(true); // Max cutoff depends on sample rate } // GetParameterInfo implementation OSStatus Filter::GetParameterInfo(AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo& outParameterInfo) { outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable | kAudioUnitParameterFlag_IsReadable; if (inScope == kAudioUnitScope_Global) { switch (inParameterID) { case kFilterParam_CutoffFrequency: AUBase::FillInParameterName(outParameterInfo, CFSTR("cutoff frequency"), false); outParameterInfo.unit = kAudioUnitParameterUnit_Hertz; outParameterInfo.minValue = kMinCutoffHz; outParameterInfo.maxValue = GetSampleRate() * 0.5; // Nyquist outParameterInfo.defaultValue = kDefaultCutoff; outParameterInfo.flags |= kAudioUnitParameterFlag_IsHighResolution; outParameterInfo.flags |= kAudioUnitParameterFlag_DisplayLogarithmic; break; case kFilterParam_Resonance: AUBase::FillInParameterName(outParameterInfo, CFSTR("resonance"), false); outParameterInfo.unit = kAudioUnitParameterUnit_Decibels; outParameterInfo.minValue = kMinResonance; outParameterInfo.maxValue = kMaxResonance; outParameterInfo.defaultValue = kDefaultResonance; outParameterInfo.flags |= kAudioUnitParameterFlag_IsHighResolution; break; default: return kAudioUnitErr_InvalidParameter; } } return noErr; } ``` -------------------------------- ### Implement AUViewController for Audio Unit v3 Source: https://context7.com/apple/audiounit-examples/llms.txt Subclass AUViewController and implement AUAudioUnitFactory to manage audio unit lifecycle and parameter observation. Ensure the view controller connects to the parameter tree once the audio unit is initialized. ```swift // AudioUnitViewController.swift import AudioToolbox import CoreAudioKit public class AudioUnitViewController: AUViewController, AUAudioUnitFactory { // View configurations for compact and expanded modes let compact = AUAudioUnitViewConfiguration(width: 400, height: 100, hostHasController: false) let expanded = AUAudioUnitViewConfiguration(width: 800, height: 500, hostHasController: false) private var cutoffParameter: AUParameter! private var resonanceParameter: AUParameter! private var parameterObserverToken: AUParameterObserverToken? @IBOutlet weak var filterView: FilterView! @IBOutlet weak var frequencyTextField: TextField! @IBOutlet weak var resonanceTextField: TextField! var needsConnection = true private var audioUnit: FilterAudioUnit? { didSet { performOnMain { if self.isViewLoaded { self.connectViewToAU() } } } } // Factory method to create the audio unit public func createAudioUnit(with componentDescription: AudioComponentDescription) throws -> AUAudioUnit { audioUnit = try FilterAudioUnit(componentDescription: componentDescription) return audioUnit! } // Connect UI elements to AU parameters private func connectViewToAU() { guard needsConnection, let paramTree = audioUnit?.parameterTree else { return } // Find parameters by ID guard let cutoff = paramTree.parameter(withID: 0, scope: 0, element: 0), let resonance = paramTree.parameter(withID: 1, scope: 0, element: 0) else { fatalError("Required AU parameters not found.") } cutoffParameter = cutoff resonanceParameter = resonance // Observe parameter changes from the AU parameterObserverToken = paramTree.token(byAddingParameterObserver: { [weak self] address, value in if [cutoff.address, resonance.address].contains(address) { DispatchQueue.main.async { self?.updateUI() } } }) needsConnection = false updateUI() } private func updateUI() { filterView.frequency = cutoffParameter.value filterView.resonance = resonanceParameter.value frequencyTextField.text = cutoffParameter.string(fromValue: nil) resonanceTextField.text = resonanceParameter.string(fromValue: nil) updateFilterViewFrequencyAndMagnitudes() } } ``` -------------------------------- ### Implement Custom Frequency Response Property in Audio Unit v2 Source: https://context7.com/apple/audiounit-examples/llms.txt Defines and implements the GetPropertyInfo and GetProperty callbacks for a custom Audio Unit property (kAudioUnitProperty_FrequencyResponse) to report and retrieve frequency response data. ```cpp // Custom property ID (defined in header) const AudioUnitPropertyID kAudioUnitProperty_FrequencyResponse = 64000; const int kNumberOfResponseFrequencies = 512; // Report property info OSStatus Filter::GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable) { if (inScope == kAudioUnitScope_Global) { switch (inID) { case kAudioUnitProperty_FrequencyResponse: outDataSize = kNumberOfResponseFrequencies * sizeof(AudioUnitFrequencyResponseBin); outWritable = false; return noErr; } } return AUEffectBase::GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable); } // Return frequency response data OSStatus Filter::GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData) { if (inID == kAudioUnitProperty_FrequencyResponse && inScope == kAudioUnitScope_Global) { if (!IsInitialized()) return kAudioUnitErr_Uninitialized; AudioUnitFrequencyResponseBin* freqResponseTable = (AudioUnitFrequencyResponseBin*)outData; FilterKernel* filterKernel = dynamic_cast(GetKernel(0)); double cutoff = GetParameter(kFilterParam_CutoffFrequency); double resonance = GetParameter(kFilterParam_Resonance); // Normalize and calculate coefficients cutoff = 2.0 * cutoff / GetSampleRate(); filterKernel->CalculateLopassParams(std::min(cutoff, 0.99), resonance); // Calculate magnitude response for each frequency bin for (int i = 0; i < kNumberOfResponseFrequencies; i++) { freqResponseTable[i].mMagnitude = filterKernel->GetFrequencyResponse(freqResponseTable[i].mFrequency); } return noErr; } return AUEffectBase::GetProperty(inID, inScope, inElement, outData); } ``` -------------------------------- ### Implement Cocoa View Factory for Audio Unit v2 Source: https://context7.com/apple/audiounit-examples/llms.txt Implement the AUCocoaUIBase protocol for Audio Unit v2 on macOS to provide custom UI. This involves loading a NIB file and configuring the custom view with the audio unit reference. ```objc // AppleDemoFilter_ViewFactory.h #import #import @class AppleDemoFilter_UIView; @interface AppleDemoFilter_ViewFactory : NSObject @property (nonatomic) IBOutlet AppleDemoFilter_UIView* uiFreshlyLoadedView; - (NSString*)description; @end ``` ```objc // AppleDemoFilter_ViewFactory.m #import "AppleDemoFilter_ViewFactory.h" #import "AppleDemoFilter_UIView.h" @implementation AppleDemoFilter_ViewFactory - (unsigned)interfaceVersion { return 0; } - (NSString*)description { return @"Apple Demo: Filter"; } // Factory method called by host to create the view - (NSView*)uiViewForAudioUnit:(AudioUnit)inAU withSize:(NSSize)inPreferredSize { // Load the nib containing the custom view if (![[NSBundle bundleForClass:[self class]] loadNibNamed:@"CocoaView" owner:self topLevelObjects:nil]) { NSLog(@"Unable to load nib for view."); return nil; } // Configure the view with the audio unit reference [uiFreshlyLoadedView setAU:inAU]; NSView* returnView = uiFreshlyLoadedView; uiFreshlyLoadedView = nil; // Factory keeps no reference return returnView; } @end ``` -------------------------------- ### Create Audio Unit v3 Bridge Factory Source: https://context7.com/apple/audiounit-examples/llms.txt Provides a factory class for creating an Audio Unit v3 extension that wraps an existing v2 implementation using AUAudioUnitV2Bridge. ```swift // AudioUnitFactory.swift - Factory for creating the bridged AU import AudioToolbox public class AudioUnitFactory: NSObject, AUAudioUnitFactory { public func beginRequest(with context: NSExtensionContext) {} @objc public func createAudioUnit(with componentDescription: AudioComponentDescription) throws -> AUAudioUnit { // Create bridged audio unit that wraps the v2 Filter implementation let audioUnit = try FilterAudioUnit(componentDescription: componentDescription) return audioUnit } } // FilterAudioUnit.swift - Generic bridged unit (no custom properties) import AudioToolbox public class FilterAudioUnit: AUAudioUnitV2Bridge {} ``` -------------------------------- ### Filter Factory Presets Source: https://context7.com/apple/audiounit-examples/llms.txt Manages and applies factory presets for the audio filter. Presets allow users to quickly recall predefined parameter states for different filter configurations. ```cpp // Define preset indices static const int kPreset_One = 0; static const int kPreset_Two = 1; static const int kNumberPresets = 2; static AUPreset kPresets[kNumberPresets] = { { kPreset_One, CFSTR("Preset One") }, { kPreset_Two, CFSTR("Preset Two") } }; // Return available presets OSStatus Filter::GetPresets(CFArrayRef* outData) const { if (outData == NULL) return noErr; // Query for preset support CFMutableArrayRef theArray = CFArrayCreateMutable(NULL, kNumberPresets, NULL); for (int i = 0; i < kNumberPresets; ++i) { CFArrayAppendValue(theArray, &kPresets[i]); } *outData = (CFArrayRef)theArray; return noErr; } ``` ```cpp // Apply a factory preset OSStatus Filter::NewFactoryPresetSet(const AUPreset& inNewFactoryPreset) { SInt32 chosenPreset = inNewFactoryPreset.presetNumber; switch (chosenPreset) { case kPreset_One: SetParameter(kFilterParam_CutoffFrequency, 200.0); SetParameter(kFilterParam_Resonance, -5.0); break; case kPreset_Two: SetParameter(kFilterParam_CutoffFrequency, 1000.0); SetParameter(kFilterParam_Resonance, 10.0); break; default: return kAudioUnitErr_InvalidPropertyValue; } SetAFactoryPresetAsCurrent(kPresets[chosenPreset]); return noErr; } ``` -------------------------------- ### Access v2 Custom Properties from Audio Unit v3 Source: https://context7.com/apple/audiounit-examples/llms.txt Extends the AUAudioUnitV2Bridge to query underlying v2 custom properties, such as frequency response, by using AudioUnitGetProperty. ```swift // FilterAudioUnit.swift - With custom property access import AudioToolbox public class FilterAudioUnit: AUAudioUnitV2Bridge { /// Gets the filter magnitude response for the specified frequencies. /// - Parameter frequencies: Array of frequencies in Hz to query /// - Returns: Array of magnitude values (linear scale), or nil on error func magnitudes(forFrequencies frequencies: [Double]) -> [Double]? { // Prepare frequency bins with input frequencies var bins: [AudioUnitFrequencyResponseBin] = frequencies.map { AudioUnitFrequencyResponseBin(mFrequency: $0, mMagnitude: 1) } // Query the v2 custom property let err: OSStatus = bins.withUnsafeMutableBytes { bufferPtr in let dataPtr = bufferPtr.baseAddress! var dataSize = UInt32(bufferPtr.count) return AudioUnitGetProperty( self.audioUnit, AudioUnitPropertyID(kAudioUnitProperty_FrequencyResponse), AudioUnitScope(kAudioUnitScope_Global), AudioUnitElement(0), dataPtr, &dataSize ) } return err == noErr ? bins.map { $0.mMagnitude } : nil } } ``` -------------------------------- ### Implement FilterViewDelegate in Swift Source: https://context7.com/apple/audiounit-examples/llms.txt Implement the FilterViewDelegate protocol to respond to user interactions with the frequency response graph and update parameters. This includes fetching magnitude data, handling touch events, and updating resonance and frequency parameters. ```swift extension AudioUnitViewController: FilterViewDelegate { // Request magnitude data from the AU and update the view func updateFilterViewFrequencyAndMagnitudes() { guard let audioUnit = audioUnit else { return } let frequencies = filterView.frequencyDataForDrawing() guard let magnitudes = audioUnit.magnitudes(forFrequencies: frequencies) else { return } filterView.setMagnitudes(magnitudes) } // User started touching the filter graph func filterViewTouchBegan(_ filterView: FilterView) { resonanceParameter.setValue(filterView.resonance, originator: parameterObserverToken, atHostTime: 0, eventType: .touch) cutoffParameter.setValue(filterView.frequency, originator: parameterObserverToken, atHostTime: 0, eventType: .touch) } // User changed resonance by dragging func filterView(_ filterView: FilterView, didChangeResonance resonance: Float) { resonanceParameter.setValue(resonance, originator: parameterObserverToken, atHostTime: 0, eventType: .value) updateFilterViewFrequencyAndMagnitudes() } // User changed frequency by dragging func filterView(_ filterView: FilterView, didChangeFrequency frequency: Float) { cutoffParameter.setValue(frequency, originator: parameterObserverToken, atHostTime: 0, eventType: .value) updateFilterViewFrequencyAndMagnitudes() } // User finished touching func filterViewTouchEnded(_ filterView: FilterView) { resonanceParameter.setValue(filterView.resonance, originator: nil, atHostTime: 0, eventType: .release) cutoffParameter.setValue(filterView.frequency, originator: nil, atHostTime: 0, eventType: .release) } } ``` -------------------------------- ### FilterKernel DSP Processing Source: https://context7.com/apple/audiounit-examples/llms.txt Implements the biquad filter processing for audio samples. Recalculates filter coefficients only when parameters change to optimize performance. ```cpp class FilterKernel : public ausdk::AUKernelBase { public: FilterKernel(ausdk::AUEffectBase& inAudioUnit); void Process(const Float32* inSourceP, Float32* inDestP, UInt32 inFramesToProcess, bool& ioSilence) override; void Reset() override; void CalculateLopassParams(double inFreq, double inResonance); double GetFrequencyResponse(double inFreq); private: // Biquad coefficients double mA0, mA1, mA2, mB1, mB2; // Filter state (delay line) double mX1, mX2, mY1, mY2; // Cached parameter values double mLastCutoff, mLastResonance; }; // Process audio samples through the biquad filter void FilterKernel::Process(const Float32* inSourceP, Float32* inDestP, UInt32 inFramesToProcess, bool& ioSilence) { double cutoff = GetParameter(kFilterParam_CutoffFrequency); double resonance = GetParameter(kFilterParam_Resonance); // Bounds checking cutoff = std::clamp(cutoff, (double)kMinCutoffHz, GetSampleRate() * 0.495); resonance = std::clamp(resonance, (double)kMinResonance, (double)kMaxResonance); // Normalize frequency to 0-1 range float srate = GetSampleRate(); cutoff = 2.0 * cutoff / srate; // Recalculate coefficients only when parameters change if (cutoff != mLastCutoff || resonance != mLastResonance) { CalculateLopassParams(cutoff, resonance); mLastCutoff = cutoff; mLastResonance = resonance; } // Apply biquad filter: y[n] = a0*x[n] + a1*x[n-1] + a2*x[n-2] - b1*y[n-1] - b2*y[n-2] while (inFramesToProcess--) { float input = *inSourceP++; float output = mA0 * input + mA1 * mX1 + mA2 * mX2 - mB1 * mY1 - mB2 * mY2; mX2 = mX1; mX1 = input; mY2 = mY1; mY1 = output; *inDestP++ = output; } } ``` ```cpp // Calculate biquad coefficients for resonant lowpass filter void FilterKernel::CalculateLopassParams(double inFreq, double inResonance) { double r = pow(10.0, 0.05 * -inResonance); // dB to linear double k = 0.5 * r * sin(M_PI * inFreq); double c1 = 0.5 * (1.0 - k) / (1.0 + k); double c2 = (0.5 + c1) * cos(M_PI * inFreq); double c3 = (0.5 + c1 - c2) * 0.25; mA0 = 2.0 * c3; mA1 = 4.0 * c3; mA2 = 2.0 * c3; mB1 = -2.0 * c2; mB2 = 2.0 * c1; } ``` -------------------------------- ### FilterView Class Implementation Source: https://context7.com/apple/audiounit-examples/llms.txt Handles frequency and magnitude data for a filter view. It manages frequency and resonance values, clamping them within defined ranges, and provides methods to calculate frequency data for drawing and update the frequency response curve with magnitude data. ```swift // FilterView frequency and magnitude handling class FilterView: View { let defaultMinHertz: Float = 12.0 let defaultMaxHertz: Float = 20_000.0 weak var delegate: FilterViewDelegate? var frequency: Float = defaultMinHertz { didSet { frequency = frequency.clamp(to: defaultMinHertz...defaultMaxHertz) editPoint.x = floor(locationForFrequencyValue(frequency)) } } var resonance: Float = 0.0 { didSet { let gain = Float(defaultGain) resonance = resonance.clamp(to: -gain...gain) editPoint.y = floor(locationForDBValue(resonance)) } } /// Returns array of frequencies for the AU to compute magnitudes func frequencyDataForDrawing() -> [Double] { // Returns cached frequencies or calculates new array based on view width guard frequencies == nil else { return frequencies! } let width = graphLayer.bounds.width frequencies = (0.. class Filter : public ausdk::AUEffectBase { public: Filter(AudioUnit component); OSStatus Initialize() override; std::unique_ptr NewKernel() override; // Parameter handling OSStatus GetParameterInfo(AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo& outParameterInfo) override; // Custom property for frequency response curve OSStatus GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable) override; OSStatus GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData) override; // Preset support OSStatus GetPresets(CFArrayRef* outData) const override; OSStatus NewFactoryPresetSet(const AUPreset& inNewFactoryPreset) override; // Tail time for effect (1ms for filter, longer for reverbs) bool SupportsTail() override { return true; } Float64 GetTailTime() override { return 0.001; } Float64 GetLatency() override { return 0.0; } }; ``` -------------------------------- ### FilterView Class Methods Source: https://context7.com/apple/audiounit-examples/llms.txt Methods for managing frequency data and updating the visual representation of the frequency response curve. ```APIDOC ## FilterView Class Methods ### frequencyDataForDrawing() - **Description**: Returns an array of frequencies for the Audio Unit to compute magnitudes. - **Returns**: [Double] - Array of frequency values. ### setMagnitudes(magnitudeData: [Double]) - **Description**: Updates the frequency response curve with magnitude data received from the Audio Unit. - **Parameters**: - **magnitudeData** ([Double]) - Required - Array of magnitude values to render. ``` -------------------------------- ### FilterViewDelegate Protocol Source: https://context7.com/apple/audiounit-examples/llms.txt Defines the interface for responding to user interactions within the FilterView component. ```APIDOC ## FilterViewDelegate Protocol ### Description The FilterViewDelegate protocol defines callbacks for UI interactions, enabling the view controller to respond to user manipulation of the frequency response graph. ### Methods - **filterViewTouchBegan(filterView: FilterView)** - Called when user begins touch interaction. - **filterView(filterView: FilterView, didChangeResonance: Float)** - Called when resonance changes via user interaction. - **filterView(filterView: FilterView, didChangeFrequency: Float)** - Called when frequency changes via user interaction. - **filterView(filterView: FilterView, didChangeFrequency: Float, andResonance: Float)** - Called when both frequency and resonance change simultaneously. - **filterViewTouchEnded(filterView: FilterView)** - Called when user ends touch interaction. - **filterViewDataDidChange(filterView: FilterView)** - Called when view layout changes and magnitude data should be refreshed. ``` -------------------------------- ### FilterView Delegate Protocol Definition Source: https://context7.com/apple/audiounit-examples/llms.txt Defines callbacks for UI interactions with the FilterView, allowing the view controller to respond to user manipulation of the frequency response graph. Implement this protocol to handle touch events and data changes. ```swift // FilterView.swift - Delegate protocol protocol FilterViewDelegate: AnyObject { /// Called when user begins touch interaction func filterViewTouchBegan(_ filterView: FilterView) /// Called when resonance changes via user interaction func filterView(_ filterView: FilterView, didChangeResonance resonance: Float) /// Called when frequency changes via user interaction func filterView(_ filterView: FilterView, didChangeFrequency frequency: Float) /// Called when both frequency and resonance change simultaneously func filterView(_ filterView: FilterView, didChangeFrequency frequency: Float, andResonance resonance: Float) /// Called when user ends touch interaction func filterViewTouchEnded(_ filterView: FilterView) /// Called when view layout changes and magnitude data should be refreshed func filterViewDataDidChange(_ filterView: FilterView) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.