Audio Unit Extensions

Similar documents
What s New in Core Audio

What s New in Audio. Media #WWDC17. Akshatha Nagesh, AudioEngine-eer Béla Balázs, Audio Artisan Torrey Holbrook Walker, Audio/MIDI Black Ops

Using and Extending the Xcode Source Editor

Delivering an Exceptional Audio Experience

What s New in Xcode App Signing

Audio Unit Properties Reference

Creating Audio Apps for watchos

Extending Your Apps with SiriKit

Understanding Undefined Behavior

Mastering Xcode for iphone OS Development Part 1. Todd Fernandez Sr. Manager, IDEs

Introducing the Photos Frameworks

Advanced Debugging and the Address Sanitizer

Implementing UI Designs in Interface Builder

What s New in tvos #WWDC16. App Frameworks. Session 206. Hans Kim tvos Engineer

Creating Great App Previews

Enhancing your apps for the next dimension of touch

Enabling Your App for CarPlay

Improving your Existing Apps with Swift

For Mac and iphone. James McCartney Core Audio Engineer. Eric Allamanche Core Audio Engineer

Building Faster in Xcode

Getting the Most Out of HealthKit

Mastering Xcode for iphone OS Development Part 2. Marc Verstaen Sr. Manager, iphone Tools

Thread Sanitizer and Static Analysis

Media and Gaming Accessibility

Finding Bugs Using Xcode Runtime Tools

Introducing the Contacts Framework

HKUST. CSIT 6910A Report. iband - Musical Instrument App on Mobile Devices. Student: QIAN Li. Supervisor: Prof. David Rossiter

Swift. Introducing swift. Thomas Woodfin

Seamless Linking to Your App

Advances in AVFoundation Playback

What s New in watchos

Building Watch Apps #WWDC15. Featured. Session 108. Neil Desai watchos Engineer

Power, Performance, and Diagnostics

Introducing the Modern WebKit API

Building for Voice with Siri Shortcuts

Working with Metal Overview

Getting the Most out of Playgrounds in Xcode

iphone App Basics iphone and ipod touch Development Fall 2009 Lecture 5

Leveraging Touch Input on ios

Working With Metal Advanced

Swift 5, ABI Stability and

Generalised User Interface for Embedded Applications using an LCD screen and keypad.

Contents. Introduction 7. What Is Core Audio? 10. Core Audio Essentials 19. Organization of This Document 8 See Also 8

Optimizing Swift Performance Session 409

Introduction to Siri Shortcuts

Cross Platform Nearby Networking

Content Protection for HTTP Live Streaming

Cocoa Touch Best Practices

Mastering Drag and Drop

Introducing MusicKit. Media #WWDC17. Tim Parthemore, MusicKit Services Joel Lopes Da Silva, ios Music

Advanced Notifications

Data Delivery with Drag and Drop

What s New in imessage Apps

Introducing Metal 2. Graphics and Games #WWDC17. Michal Valient, GPU Software Engineer Richard Schreyer, GPU Software Engineer

Mastering UIKit on tvos

WatchKit In-Depth, Part 2

App Extension Best Practices

What s New in Testing

ios Application Development Lecture 2: Seminar and Unit 1

The MVC Design Pattern

Kevin van Vechten Core OS

DROP-X. Drag & Drop - Beat Repeat Sampler USER MANUAL. VST / AU / AAX Plugin Mac OSX / WIN 32 / 64 bit

What s New in Notifications

Installing Your Software Important:

Assignment I: Concentration

Mobile App Development. ios Platform

Introducing On Demand Resources

What's New in Core Spotlight

What s New in tvos 12

What s New in Energy Debugging

What s New in Cocoa for macos

Inear Display Lancinantes

the gamedesigninitiative at cornell university Lecture 15 Game Audio

Registering for the Apple Developer Program

Richard Mallion. Swift for Admins #TEAMSWIFT

What s New in Metal, Part 2

CarPlay Audio and Navigation Apps

Your Apps and the Future of macos Security

Accessibility on OS X

ios Memory Deep Dive #WWDC18 Kyle Howarth, Software Engineer James Snee, Software Engineer Kris Markel, Software Engineer

What s New in Core Data?

Xcode 6 and ios 8 What s New for Software Developers

32 Lives 32 Bit to 64 Bit AU Adapter User Manual

Audio Queue Services. Dave

Instruments User Guide Apple Ipad 2 Ios 5.0. Software >>>CLICK HERE<<<

Playing & Recording Audio Audio Queue Services Playback

Managing Documents In Your ios Apps

What s New in LLDB. Debug your way to fame and glory #WWDC15. Developer Tools. Session 402

AVContentKeySession Best Practices

Kony Visualizer. Wearables Developer's Guide

Announcements. Today s Topics

Getting to Know Swift Package Manager

What s New in Foundation for Swift Session 207

Fix Bugs Faster Using Activity Tracing

Intro to Development for ios. Dave Koziol Arbormoon Software, Inc.

What s New in the LLVM Compiler. Chris Lattner LLVM Chief Architect

Quick Interaction Techniques for watchos

AVAudioPlayer. avtouch Application

Jython. An introduction by Thinh Le

What s New in Core Image

Transcription:

Media #WWDC15 Audio Unit Extensions Session 508 Doug Wyatt Core Audio Plumber 2015 Apple Inc. All rights reserved. Redistribution or public display not permitted without written permission from Apple.

Audio Units Since OS X 10.0, ios 2.0 OS includes many units I/O, mixers, effects, more Used in higher-level API s (e.g. media playback) 3rd-party plug-ins (OS X)

Audio Unit Extensions Full plug-in model OS X and ios App Extensions -> App Store! New API Modern Compatible

Version 3 Audio Unit API v1: 2001 v2: 2002 v3: AUAudioUnit class In AudioUnit.framework Objective-C / Swift

AVFoundation AVAudioUnitComponentManager Locate Audio Unit components OS X 10.10+ ios 9.0+ AVAudioUnitComponent An Audio Unit component OS X 10.10+ ios 9.0+ AVAudioUnit AVAudioUnitEffect etc. Audio Unit instance, in AVAudioEngine OS X 10.10+ ios 8.0+

Compatibility Existing v2 Hosts v2 API AudioComponentInstanceNew AudioComponentFactoryFunction Existing v2 AUs

Compatibility Existing v2 Hosts New v3 Hosts v2 API AudioComponentInstanceNew AudioComponentFactoryFunction v3 API AUAudioUnit AU extensions Existing v2 AU s New v3 AU s

Compatibility Existing v2 Hosts New v3 Hosts v2 API AudioComponentInstanceNew AudioComponentFactoryFunction Bridges v3 API AUAudioUnit AU extensions Existing v2 AU s New v3 AU s

Compatibility Existing v2 Hosts Compatible New v3 Hosts v2 API AudioComponentInstanceNew AudioComponentFactoryFunction Bridges v3 API AUAudioUnit AU extensions Existing v2 AU s New v3 AU s

Compatibility Existing v2 Hosts Minor Source Changes New v3 Hosts v2 API AudioComponentInstanceNew AudioComponentFactoryFunction Bridges v3 API AUAudioUnit AU extensions Existing v2 AU s New v3 AU s

Demo Audio Unit Extension in Logic Pro Doug Wyatt Core Audio Plumber

Logic Pro with AU Extension Logic Pro (experimental version) Extension Service Process v2 API Bridges AUAudioUnit ViewController IPC AUAudioUnit Subclass Custom ViewController

Hosting Audio Units

Hosting Audio Units Using v3 API s Sample Code: AudioUnitV3Example AUHost shows Finding Opening Connecting Presets Opening AU s custom view

SimplePlayEngine AVAudioEngine AVAudioPlayerNode audio file AVAudioUnitEffect AUAudioUnit mixer output

SimplePlayEngine AVAudioEngine AVAudioUnitComponentManager AVAudioPlayerNode audio file AVAudioUnitEffect AUAudioUnit AVAudioUnitComponent mixer output

Audio Components struct AudioComponentDescription { var componenttype: OSType var componentsubtype: OSType var componentmanufacturer: OSType var componentflags: UInt32 var componentflagsmask: UInt32 }

Finding Audio Unit Components let anyeffect = AudioComponentDescription(componentType: kaudiounittype_effect, componentsubtype: 0, componentmanufacturer: 0, componentflags: 0, componentflagsmask: 0) let availableeffects: [AVAudioUnitComponent] = AVAudioUnitComponentManager.sharedAudioUnitComponentManager(). componentsmatchingdescription(anyeffect)

Finding Audio Unit Components let anyeffect = AudioComponentDescription(componentType: kaudiounittype_effect, componentsubtype: 0, componentmanufacturer: 0, componentflags: 0, componentflagsmask: 0) let availableeffects: [AVAudioUnitComponent] = AVAudioUnitComponentManager.sharedAudioUnitComponentManager(). componentsmatchingdescription(anyeffect)

Finding Audio Unit Components let anyeffect = AudioComponentDescription(componentType: kaudiounittype_effect, componentsubtype: 0, componentmanufacturer: 0, componentflags: 0, componentflagsmask: 0) let availableeffects: [AVAudioUnitComponent] = AVAudioUnitComponentManager.sharedAudioUnitComponentManager(). componentsmatchingdescription(anyeffect)

Finding Audio Unit Components let anyeffect = AudioComponentDescription(componentType: kaudiounittype_effect, componentsubtype: 0, componentmanufacturer: 0, componentflags: 0, componentflagsmask: 0) let availableeffects: [AVAudioUnitComponent] = AVAudioUnitComponentManager.sharedAudioUnitComponentManager(). componentsmatchingdescription(anyeffect)

Selecting an Audio Unit func selecteffectcomponent(comp: AVAudioUnitComponent?, completionhandler: () -> Void) { selecteffectwithcomponentdescription (comp?.audiocomponentdescription, completionhandler: completionhandler) }

Creating an AudioUnit Instance With AVAudioEngine AVAudioUnit.instantiateWithComponentDescription(desc!, options: []) { avaudiounit, error in guard let avaudiounit = avaudiounit else { return } self.effect = avaudiounit self.engine.attachnode(self.effect!)

Creating an AudioUnit Instance With AVAudioEngine AVAudioUnit.instantiateWithComponentDescription(desc!, options: []) { avaudiounit, error in guard let avaudiounit = avaudiounit else { return } self.effect = avaudiounit self.engine.attachnode(self.effect!)

Creating an AudioUnit Instance With AVAudioEngine AVAudioUnit.instantiateWithComponentDescription(desc!, options: []) { avaudiounit, error in guard let avaudiounit = avaudiounit else { return } self.effect = avaudiounit self.engine.attachnode(self.effect!)

Creating an AudioUnit Instance With AVAudioEngine AVAudioUnit.instantiateWithComponentDescription(desc!, options: []) { avaudiounit, error in guard let avaudiounit = avaudiounit else { return } self.effect = avaudiounit self.engine.attachnode(self.effect!)

Creating an AudioUnit Instance With AVAudioEngine AVAudioUnit.instantiateWithComponentDescription(desc!, options: []) { avaudiounit, error in guard let avaudiounit = avaudiounit else { return } self.effect = avaudiounit self.engine.attachnode(self.effect!)

Creating an AudioUnit Instance With AVAudioEngine AVAudioUnit.instantiateWithComponentDescription(desc!, options: []) { avaudiounit, error in guard let avaudiounit = avaudiounit else { return } self.effect = avaudiounit self.engine.attachnode(self.effect!)

Creating an AudioUnit Instance With AVAudioEngine self.engine.disconnectnodeinput(self.engine.mainmixernode) self.engine.connect(self.player, to: self.effect!, format: self.file!.processingformat) self.engine.connect(self.effect!, to: self.engine.mainmixernode, format: self.file!.processingformat)

Creating an AudioUnit Instance With AVAudioEngine self.engine.disconnectnodeinput(self.engine.mainmixernode) self.engine.connect(self.player, to: self.effect!, format: self.file!.processingformat) self.engine.connect(self.effect!, to: self.engine.mainmixernode, format: self.file!.processingformat)

Creating an AudioUnit Instance With AVAudioEngine self.engine.disconnectnodeinput(self.engine.mainmixernode) self.engine.connect(self.player, to: self.effect!, format: self.file!.processingformat) self.engine.connect(self.effect!, to: self.engine.mainmixernode, format: self.file!.processingformat)

Creating an AudioUnit Instance With AVAudioEngine self.engine.disconnectnodeinput(self.engine.mainmixernode) self.engine.connect(self.player, to: self.effect!, format: self.file!.processingformat) self.engine.connect(self.effect!, to: self.engine.mainmixernode, format: self.file!.processingformat)

Creating an AudioUnit Instance With AVAudioEngine self.engine.disconnectnodeinput(self.engine.mainmixernode) self.engine.connect(self.player, to: self.effect!, format: self.file!.processingformat) self.engine.connect(self.effect!, to: self.engine.mainmixernode, format: self.file!.processingformat)

Creating an AudioUnit Instance With AVAudioEngine self.engine.disconnectnodeinput(self.engine.mainmixernode) self.engine.connect(self.player, to: self.effect!, format: self.file!.processingformat) self.engine.connect(self.effect!, to: self.engine.mainmixernode, format: self.file!.processingformat)

Creating an AudioUnit Instance With AVAudioEngine self.audiounit = self.effect!.auaudiounit if let pl = self.audiounit!.factorypresets { self.presetlist = pl } else { self.presetlist = [AUAudioUnitPreset]() }

Creating an AudioUnit Instance With AVAudioEngine self.audiounit = self.effect!.auaudiounit if let pl = self.audiounit!.factorypresets { self.presetlist = pl } else { self.presetlist = [AUAudioUnitPreset]() }

Creating an AudioUnit Instance With AVAudioEngine self.audiounit = self.effect!.auaudiounit if let pl = self.audiounit!.factorypresets { self.presetlist = pl } else { self.presetlist = [AUAudioUnitPreset]() }

Creating an AudioUnit Instance With AVAudioEngine self.audiounit = self.effect!.auaudiounit if let pl = self.audiounit!.factorypresets { self.presetlist = pl } else { self.presetlist = [AUAudioUnitPreset]() }

Accessing an Audio Unit s View Controller playengine.audiounit?.requestviewcontrollerwithcompletionhandler { [weak self] viewcontroller in embed audio unit s view controller s view }

Demo AUHost Michael Hopkins Purveyor of Pixels

Using an AudioUnit Directly, v3 (Swift): AUAudioUnit.instantiateWithComponentDescription(desc, options: []) { audiounit, err in } Directly, v2 (C): AudioComponentInstantiate(desc, options, ^(AudioComponentInstance au, OSStatus err) { })

Hosting: In Versus Out of Process

v2 AU s: Always in Host s Process Host Existing v2 Hosts New v3 Hosts v2 API AudioComponentInstanceNew AudioComponentFactoryFunction Bridges v3 API AUAudioUnit AU extensions Existing v2 AU s

v3 AU s: Default to Separate Process Existing v2 Hosts Host New v3 Hosts Extension Service Process v2 API Bridges AUAudioUnit ViewController IPC AUAudioUnit Subclass Custom ViewController

v3 AU s: Optionally In-Process Host options: kaudiocomponentinstantiation_loadinprocess AU extension plist entry: AudioComponentBundle Host Existing v2 Hosts New v3 Hosts v2 API Bridges AUAudioUnit Subclass Custom ViewController

In-Process: Safety Versus Performance Safety Security risk Misbehaving AU can crash the host Performance IPC overhead ~40 µs per render cycle Significance depends on number of AU s and render interval 0.1% at 1024 frames / 44.1 khz 5.5% at 32 frames / 44.1 khz

Updating a v2 Host If kaudiocomponentflag_requiresasyncinstantiation is set, use: extern void AudioComponentInstantiate( AudioComponent incomponent, AudioComponentInstantiationOptions inoptions, void (^incompletionhandler)( AudioComponentInstance nullable, OSStatus)); Instead of: extern OSStatus AudioComponentInstanceNew(AudioComponent incomponent, AudioComponentInstance *outinstance);

Updating a v2 Host If kaudiocomponentflag_isv3audiounit is set, use: kaudiounitproperty_requestviewcontroller (also asynchronous) Instead of: kaudiounitproperty_cocoaui

Asynchronous Operations Can use new methods with v2 units Must use new methods with v3 units Helps responsiveness Unblocks main thread Don t wait on the main thread! Deadlock

Creating an Audio Unit Extension

About App Extensions.appex App s PlugIns directory Runs in XPC service process See App Extension Programming Guide

Creating an Audio Unit Using v3 API s New Sample Code: AudioUnitV3Example FilterDemo

FilterDemo Anatomy FilterDemoApp Containing app Extension (.appex) FilterDemo App Extension Both link framework FilterDemo Framework

FilterDemo Anatomy FilterDemoApp Containing app Extension FilterDemo App Extension Both link framework FilterDemo Framework AUv3FilterDemo FilterDemoViewController

FilterDemo Anatomy FilterDemoApp Containing app Extension FilterDemo App Extension Both link framework Easy development within app (no XPC) FilterDemo Framework Code size/duplication On OS X, framework can be loaded into host process AUv3FilterDemo FilterDemoViewController

Extension s Info.plist Comments in <AudioUnit/AUAudioUnitImplementation.h> NSExtensionPointIdentifier com.apple.audiounit-ui NSExtensionMainStoryboard MainInterface AudioComponents AudioComponentDescription (type/subtype/manufacturer), name, version, tags

Extension s MainInterface.storyboard Entry point is a View Controller, set its Custom Class:

Extension Code import FilterDemoFramework /* The app extension has to contain *some* code linking against the framework. */ let dummy = FilterDemoViewController.self

Framework: FilterDemoViewController View controller is extension s principal class Creates AUAudioUnit subclass Creates and manages custom view

FilterDemoViewController public class FilterDemoViewController : AUViewController, AUAudioUnitFactory, ControllerDelegate { public var audiounit: AUv3FilterDemo? { } public func createaudiounitwithcomponentdescription(desc: AudioComponentDescription) throws -> AUAudioUnit { audiounit = try AUv3FilterDemo(componentDescription: desc, options: []) return audiounit! } }

FilterDemoViewController public class FilterDemoViewController : AUViewController, AUAudioUnitFactory, ControllerDelegate { public var audiounit: AUv3FilterDemo? { } public func createaudiounitwithcomponentdescription(desc: AudioComponentDescription) throws -> AUAudioUnit { audiounit = try AUv3FilterDemo(componentDescription: desc, options: []) return audiounit! } }

FilterDemoViewController public class FilterDemoViewController : AUViewController, AUAudioUnitFactory, ControllerDelegate { public var audiounit: AUv3FilterDemo? { } public func createaudiounitwithcomponentdescription(desc: AudioComponentDescription) throws -> AUAudioUnit { audiounit = try AUv3FilterDemo(componentDescription: desc, options: []) return audiounit! } }

FilterDemoViewController public class FilterDemoViewController : AUViewController, AUAudioUnitFactory, ControllerDelegate { public var audiounit: AUv3FilterDemo? { } public func createaudiounitwithcomponentdescription(desc: AudioComponentDescription) throws -> AUAudioUnit { audiounit = try AUv3FilterDemo(componentDescription: desc, options: []) return audiounit! } }

FilterDemoViewController public class FilterDemoViewController : AUViewController, AUAudioUnitFactory, ControllerDelegate { public var audiounit: AUv3FilterDemo? { } public func createaudiounitwithcomponentdescription(desc: AudioComponentDescription) throws -> AUAudioUnit { audiounit = try AUv3FilterDemo(componentDescription: desc, options: []) return audiounit! } }

FilterDemoViewController public class FilterDemoViewController : AUViewController, AUAudioUnitFactory, ControllerDelegate { public var audiounit: AUv3FilterDemo? { } public func createaudiounitwithcomponentdescription(desc: AudioComponentDescription) throws -> AUAudioUnit { audiounit = try AUv3FilterDemo(componentDescription: desc, options: []) return audiounit! } }

Framework: AUv3FilterDemo AUAudioUnit subclass @interface AUv3FilterDemo () { FilterDSPKernel kernel; BufferedInputBus inputbus; AUAudioUnitBus *outputbus; AUAudioUnitBusArray *inputbusarray; AUAudioUnitBusArray *outputbusarray; AUParameterTree *parametertree; }

Framework: AUv3FilterDemo AUAudioUnit subclass @interface AUv3FilterDemo () { FilterDSPKernel kernel; BufferedInputBus inputbus; AUAudioUnitBus *outputbus; AUAudioUnitBusArray *inputbusarray; AUAudioUnitBusArray *outputbusarray; AUParameterTree *parametertree; }

Framework: AUv3FilterDemo AUAudioUnit subclass @interface AUv3FilterDemo () { FilterDSPKernel kernel; BufferedInputBus inputbus; AUAudioUnitBus *outputbus; AUAudioUnitBusArray *inputbusarray; AUAudioUnitBusArray *outputbusarray; AUParameterTree *parametertree; }

Framework: AUv3FilterDemo AUAudioUnit subclass @interface AUv3FilterDemo () { FilterDSPKernel kernel; BufferedInputBus inputbus; AUAudioUnitBus *outputbus; AUAudioUnitBusArray *inputbusarray; AUAudioUnitBusArray *outputbusarray; AUParameterTree *parametertree; }

Framework: AUv3FilterDemo AUAudioUnit subclass @interface AUv3FilterDemo () { FilterDSPKernel kernel; BufferedInputBus inputbus; AUAudioUnitBus *outputbus; AUAudioUnitBusArray *inputbusarray; AUAudioUnitBusArray *outputbusarray; AUParameterTree *parametertree; }

Framework: AUv3FilterDemo AUAudioUnit subclass @interface AUv3FilterDemo () { FilterDSPKernel kernel; BufferedInputBus inputbus; AUAudioUnitBus *outputbus; AUAudioUnitBusArray *inputbusarray; AUAudioUnitBusArray *outputbusarray; AUParameterTree *parametertree; }

AUv3FilterDemo Creating bus arrays inputbus.init(defaultformat, 8); outputbus = [[AUAudioUnitBus alloc] initwithformat:defaultformat error:nil]; inputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeinput busses: @[inputbus.bus]]; outputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeoutput busses: @[outputbus]];

AUv3FilterDemo Creating bus arrays inputbus.init(defaultformat, 8); outputbus = [[AUAudioUnitBus alloc] initwithformat:defaultformat error:nil]; inputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeinput busses: @[inputbus.bus]]; outputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeoutput busses: @[outputbus]];

AUv3FilterDemo Creating bus arrays inputbus.init(defaultformat, 8); outputbus = [[AUAudioUnitBus alloc] initwithformat:defaultformat error:nil]; inputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeinput busses: @[inputbus.bus]]; outputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeoutput busses: @[outputbus]];

AUv3FilterDemo Creating bus arrays inputbus.init(defaultformat, 8); outputbus = [[AUAudioUnitBus alloc] initwithformat:defaultformat error:nil]; inputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeinput busses: @[inputbus.bus]]; outputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeoutput busses: @[outputbus]];

AUv3FilterDemo Creating bus arrays inputbus.init(defaultformat, 8); outputbus = [[AUAudioUnitBus alloc] initwithformat:defaultformat error:nil]; inputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeinput busses: @[inputbus.bus]]; outputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeoutput busses: @[outputbus]];

AUv3FilterDemo Creating bus arrays inputbus.init(defaultformat, 8); outputbus = [[AUAudioUnitBus alloc] initwithformat:defaultformat error:nil]; inputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeinput busses: @[inputbus.bus]]; outputbusarray = [[AUAudioUnitBusArray alloc] initwithaudiounit:self bustype:auaudiounitbustypeoutput busses: @[outputbus]];

AUv3FilterDemo Creating parameters AUParameter *cutoffparam = [AUParameterTree createparameterwithidentifier:@"cutoff" name:@ Cutoff" address:filterparamcutoff min:12.0 max:20000.0 unit:kaudiounitparameterunit_hertz unitname:nil flags:0 valuestrings:nil dependentparameters:nil]; AUParameter *resonanceparam = [AUParameterTree createparameterwithidentifier:@"resonance" name:@ Resonance" address:filterparamresonance

AUv3FilterDemo Creating parameters AUParameter *cutoffparam = [AUParameterTree createparameterwithidentifier:@"cutoff" name:@ Cutoff" address:filterparamcutoff min:12.0 max:20000.0 unit:kaudiounitparameterunit_hertz unitname:nil flags:0 valuestrings:nil dependentparameters:nil]; AUParameter *resonanceparam = [AUParameterTree createparameterwithidentifier:@"resonance" name:@ Resonance" address:filterparamresonance

AUv3FilterDemo Creating parameters AUParameter *cutoffparam = [AUParameterTree createparameterwithidentifier:@"cutoff" name:@ Cutoff" address:filterparamcutoff min:12.0 max:20000.0 unit:kaudiounitparameterunit_hertz unitname:nil flags:0 valuestrings:nil dependentparameters:nil]; AUParameter *resonanceparam = [AUParameterTree createparameterwithidentifier:@"resonance" name:@ Resonance" address:filterparamresonance

AUv3FilterDemo Creating parameters AUParameter *cutoffparam = [AUParameterTree createparameterwithidentifier:@"cutoff" name:@ Cutoff" address:filterparamcutoff min:12.0 max:20000.0 unit:kaudiounitparameterunit_hertz unitname:nil flags:0 valuestrings:nil dependentparameters:nil]; AUParameter *resonanceparam = [AUParameterTree createparameterwithidentifier:@"resonance" name:@ Resonance" address:filterparamresonance

AUv3FilterDemo Creating the parameter tree parametertree = [AUParameterTree createtreewithchildren: @[cutoffparam, resonanceparam]]; parametertree.implementorvalueobserver = ^(AUParameter *param, AUValue value) { filterdspkernel->setparameter(param.address, value); }; parametertree.implementorvalueprovider = ^(AUParameter *param) { return filterdspkernel->getparameter(param.address); };

AUv3FilterDemo Creating the parameter tree parametertree = [AUParameterTree createtreewithchildren: @[cutoffparam, resonanceparam]]; parametertree.implementorvalueobserver = ^(AUParameter *param, AUValue value) { filterdspkernel->setparameter(param.address, value); }; parametertree.implementorvalueprovider = ^(AUParameter *param) { return filterdspkernel->getparameter(param.address); };

AUv3FilterDemo Creating the parameter tree parametertree = [AUParameterTree createtreewithchildren: @[cutoffparam, resonanceparam]]; parametertree.implementorvalueobserver = ^(AUParameter *param, AUValue value) { filterdspkernel->setparameter(param.address, value); }; parametertree.implementorvalueprovider = ^(AUParameter *param) { return filterdspkernel->getparameter(param.address); };

AUv3FilterDemo Creating the parameter tree parametertree = [AUParameterTree createtreewithchildren: @[cutoffparam, resonanceparam]]; parametertree.implementorvalueobserver = ^(AUParameter *param, AUValue value) { filterdspkernel->setparameter(param.address, value); }; parametertree.implementorvalueprovider = ^(AUParameter *param) { return filterdspkernel->getparameter(param.address); };

AUv3FilterDemo Creating the parameter tree parametertree = [AUParameterTree createtreewithchildren: @[cutoffparam, resonanceparam]]; parametertree.implementorvalueobserver = ^(AUParameter *param, AUValue value) { filterdspkernel->setparameter(param.address, value); }; parametertree.implementorvalueprovider = ^(AUParameter *param) { return filterdspkernel->getparameter(param.address); };

AUv3FilterDemo Creating the parameter tree parametertree = [AUParameterTree createtreewithchildren: @[cutoffparam, resonanceparam]]; parametertree.implementorvalueobserver = ^(AUParameter *param, AUValue value) { filterdspkernel->setparameter(param.address, value); }; parametertree.implementorvalueprovider = ^(AUParameter *param) { return filterdspkernel->getparameter(param.address); };

AUv3FilterDemo Preparing to render - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outerror { if (![super allocaterenderresourcesandreturnerror: outerror]) { return NO; } inputbus.allocaterenderresources(self.maximumframestorender); kernel.init(outputbus.format.channelcount, outputbus.format.samplerate); kernel.reset();

AUv3FilterDemo Preparing to render - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outerror { if (![super allocaterenderresourcesandreturnerror: outerror]) { return NO; } inputbus.allocaterenderresources(self.maximumframestorender); kernel.init(outputbus.format.channelcount, outputbus.format.samplerate); kernel.reset();

AUv3FilterDemo Preparing to render - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outerror { if (![super allocaterenderresourcesandreturnerror: outerror]) { return NO; } inputbus.allocaterenderresources(self.maximumframestorender); kernel.init(outputbus.format.channelcount, outputbus.format.samplerate); kernel.reset();

AUv3FilterDemo Preparing to render - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outerror { if (![super allocaterenderresourcesandreturnerror: outerror]) { return NO; } inputbus.allocaterenderresources(self.maximumframestorender); kernel.init(outputbus.format.channelcount, outputbus.format.samplerate); kernel.reset();

AUv3FilterDemo Preparing to render - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outerror { if (![super allocaterenderresourcesandreturnerror: outerror]) { return NO; } inputbus.allocaterenderresources(self.maximumframestorender); kernel.init(outputbus.format.channelcount, outputbus.format.samplerate); kernel.reset();

AUv3FilterDemo Preparing to render - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outerror { if (![super allocaterenderresourcesandreturnerror: outerror]) { return NO; } inputbus.allocaterenderresources(self.maximumframestorender); kernel.init(outputbus.format.channelcount, outputbus.format.samplerate); kernel.reset();

AUv3FilterDemo Preparing to render - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outerror { if (![super allocaterenderresourcesandreturnerror: outerror]) { return NO; } inputbus.allocaterenderresources(self.maximumframestorender); kernel.init(outputbus.format.channelcount, outputbus.format.samplerate); kernel.reset();

AUv3FilterDemo Preparing to render - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outerror { if (![super allocaterenderresourcesandreturnerror: outerror]) { return NO; } inputbus.allocaterenderresources(self.maximumframestorender); kernel.init(outputbus.format.channelcount, outputbus.format.samplerate); kernel.reset();

AUv3FilterDemo Cleaning up - (void)deallocaterenderresources { [super deallocaterenderresources]; inputbus.deallocaterenderresources();

AUv3FilterDemo Rendering - (AUInternalRenderBlock)internalRenderBlock { // Capture properties into locals. block FilterDSPKernel *state = &kernel; block BufferedInputBus *input = &inputbus;

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering return ^AUAudioUnitStatus( AudioUnitRenderActionFlags *actionflags, const AudioTimeStamp AVAudioFrameCount NSInteger AudioBufferList const AURenderEvent AURenderPullInputBlock *timestamp, framecount, outputbusnumber, *outputdata, *realtimeeventlisthead, pullinputblock) {

AUv3FilterDemo Rendering input->pullinput(&pullflags, timestamp, framecount, 0, pullinputblock); AudioBufferList *inaudiobufferlist = input->mutableaudiobufferlist; state->setbuffers(inaudiobufferlist, outaudiobufferlist); state->processwithevents(timestamp, framecount, realtimeeventlisthead);

AUv3FilterDemo Rendering input->pullinput(&pullflags, timestamp, framecount, 0, pullinputblock); AudioBufferList *inaudiobufferlist = input->mutableaudiobufferlist; state->setbuffers(inaudiobufferlist, outaudiobufferlist); state->processwithevents(timestamp, framecount, realtimeeventlisthead);

AUv3FilterDemo Rendering input->pullinput(&pullflags, timestamp, framecount, 0, pullinputblock); AudioBufferList *inaudiobufferlist = input->mutableaudiobufferlist; state->setbuffers(inaudiobufferlist, outaudiobufferlist); state->processwithevents(timestamp, framecount, realtimeeventlisthead);

AUv3FilterDemo Rendering input->pullinput(&pullflags, timestamp, framecount, 0, pullinputblock); AudioBufferList *inaudiobufferlist = input->mutableaudiobufferlist; state->setbuffers(inaudiobufferlist, outaudiobufferlist); state->processwithevents(timestamp, framecount, realtimeeventlisthead);

AUv3FilterDemo Rendering input->pullinput(&pullflags, timestamp, framecount, 0, pullinputblock); AudioBufferList *inaudiobufferlist = input->mutableaudiobufferlist; state->setbuffers(inaudiobufferlist, outaudiobufferlist); state->processwithevents(timestamp, framecount, realtimeeventlisthead);

AUv3FilterDemo Rendering input->pullinput(&pullflags, timestamp, framecount, 0, pullinputblock); AudioBufferList *inaudiobufferlist = input->mutableaudiobufferlist; state->setbuffers(inaudiobufferlist, outaudiobufferlist); state->processwithevents(timestamp, framecount, realtimeeventlisthead);

Demo AUv3FilterDemo Michael Hopkins Purveyor of Pixels

Containing App FilterDemoApp Plug-in vehicle Rapid iteration in development FilterDemo App Extension Extra functionality, e.g. Playback engine FilterDemo Framework Touch controller Documentation/help AUv3FilterDemo FilterDemoViewController

Creating an Extension OS X in-process framework Caution: Swift ABI subject to change Xcode template planned For now, copy from FilterDemo

Modernized API AUAudioUnit

Properties: v2 Scope/element based properties, void * access methods AudioUnitGetProperty(audioUnit, propertyid, scope, element, data, datasize) AudioUnitSetProperty Awkward from Swift

Properties: v3 Dot syntax in ObjC and Swift, KVC/KVO: au.maximumframestorender = 4096 let maxframes = au.valueforkey( maximumframestorender ) as? Int mixerau.addobserver(self, forkeypath: "inputbusses", options: nil, context: nil) mixerau.inputbusses.addobservertoallbusses(self, forkeypath: "format", options: nil, context: nil)

Busses Objects AUAudioUnitBusArray AUAudioUnitBus let fmt: AVAudioFormat = au.inputbusses[0].format let samplerate: Double = fmt.samplerate au.outputbusses[0].name = Reverb send

Parameters: v2 Scope/element/ID Unweildy tuple Not enough bits for complex AU s AudioUnitGetParameter(audioUnit, paramid, scope, element, value) AudioUnitSetParameter(audioUnit, paramid, scope, element, value) Complex AUEventListener API

AUAudioUnit.parameterTree Nodes (root) Groups oscillator filter amplifier Parameters Permanent, wave octave cutoff resonance envelope envelope unique string ID s Key path (KVC) A S R A S R oscillator.wave filter.envelope.attack Parameters also have numeric addresses (transient)

Parameter Wiring Host / View AUParameter Implementation

Parameter Wiring param.value = x param.setvalue( x, originator: token) Host / View AUParameter Implementation (^AUParameterObserver) (AUParameterAddress address, AUValue value)

Parameter Wiring implementorvalueobserver Host / View AUParameter Implementation implementorvalueprovider

Parameter Scheduling Host AUAudioUnit Implementation

Parameter Scheduling doschedule = au.scheduleparameterblock Host AUAudioUnit Implementation doschedule(when, rampdurationframes, paramaddress, value)

Parameter Scheduling internalrender = au.internalrenderblock Host AUAudioUnit Implementation internalrender( AURenderEvent realtimeeventlisthead )

MIDI Events Host AUAudioUnit Implementation

MIDI Events schedulemidi = au.schedulemidieventblock Host AUAudioUnit Implementation schedulemidi(when, cable, numbytes, byteptr)

MIDI Events internalrender = au.internalrenderblock Host AUAudioUnit Implementation internalrender( AURenderEvent realtimeeventlisthead )

Rendering Pull model v2: AU has connection or input callback v3: Input always from callback Otherwise, functionally identical Efficient bridging

Calling the Render Block For hosts Fetch the block when allocating render resources: au.allocaterenderresources() renderblock = au.renderblock Call it to render: renderblock( )

Render Block: Output Buffers Host provides AudioBufferList for the Audio Unit s output bufferlist.mbuffers[i].mdata can be null AU must replace this with an internally-owned buffer Buffer must remain valid until next render cycle

Render Block: Input Buffers Host provides AURenderPullInputBlock AU calls block for input AU must supply valid AudioBufferList (non-null mdata pointers) Host may replace mdata pointers Must guarantee that memory remains valid until next render cycle Goal Avoid copying

Rendering Realtime thread context Cannot allocate memory Cannot make blocking calls Using/implementing render blocks Never capture self: ObjC runtime unsafe Swift unsafe too To capture state: use pointer to C struct, C++ object

Apple Music Creation Apps Audio Unit Extensions Alec Little Product Designer

Upcoming Support for Audio Unit Extensions GarageBand ios GarageBand Mac Logic Pro X Mainstage

GarageBand ios Preliminary Audio Unit Extension UI Preliminary Audio Unit Extension UI

AU Instruments

AU Instruments UI MIDI Events Audio Output Bus

Custom View

No redundant MIDI control surfaces here.

GarageBand ios Custom View ipad Air iphone 6+ iphone 6 iphone 5s 2048 x 670 2208 x 726 1334 x 404 1136 x 350

Apple Music Creation Apps Audio Unit Extensions

FAQ

What About Inter-App Audio? Inter-app audio uses a small subset of v2 API Existing v2 Hosts No parameter support Switching apps can be clunky Not deprecated v2 API AudioComponentInstanceNew AudioComponentFactoryFunction Audio Unit Extensions Inter-App Audio++ Inter-app audio nodes

What About My v2 Host or AU? Bridges maintain compatibility Update to v3 when you can v2 AU porting shortcut Subclass AUAudioUnitV2Bridge

Are v3 Audio Units Cross-Platform? AUAudioUnit Fully portable AUViewController Derives from UIViewController or NSViewController UI is platform-specific

Reference Headers in AudioUnit framework but link AudioToolbox AUAudioUnit.h AUAudioUnitImplementation.h AUParameters.h CoreAudioKit framework AUViewController.h AVFoundation AVAudioUnitComponent.h HeaderDoc

Reference License to use Audio Units logo https://developer.apple.com/softwarelicensing/ agreements/audio.php

Summary Full plug-in model on ios Audio Units in the ios and OS X App Stores AVAudioEngine simple host Sample code: AudioUnitV3Example Write bugs

More Information Technical Support Swift Language Documentation http://developer.apple.com/swift Apple Developer Forums http://developer.apple.com/forums

Related Sessions What s New in Core Audio Nob Hill Wednesday 4:30PM

Related Labs Audio Lab Graphics, Games, and Media Lab A Thursday 1:30PM