SlideShare a Scribd company logo
1 of 90
Core Audio in iOS 6
         Chris Adamson • @invalidname
                CocoaConf PDX
                October 27, 2012



Slides and sample code will be available later today
Plug!
The Reviews Are In!
The Reviews Are In!
The Reviews Are In!
The Reviews Are In!
Legitimate copies!
• Amazon (paper or Kindle)
• Barnes & Noble (paper or Nook)
• Apple (iBooks)
• Direct from InformIT (paper, eBook [.epub
  + .mobi + .pdf], or Bundle)
 • 35% off with code COREAUDIO3174
What You’ll Learn

• What Core Audio does and doesn’t do
• When to use and not use it
• What’s new in Core Audio for iOS 6
Simple things should be simple,
complex things should be possible.
             –Alan Kay
AV Foundation,
 Media Player

           Simple things should be simple,
         complex things should be possible.
                      –Alan Kay
AV Foundation,
 Media Player

           Simple things should be simple,
         complex things should be possible.
                      –Alan Kay
                                   Core Audio
Core Audio

• Low-level C framework for processing
  audio
 • Capture, play-out, real-time or off-line
    processing
• The “complex things should be possible”
  part of audio on OS X and iOS
Chris’ CA Taxonomy
• Engines: process streams of audio
 • Capture, play-out, mixing, effects
    processing
• Helpers: deal with formats, encodings, etc.
 • File I/O, stream I/O, format conversion,
    iOS “session” management
Helpers: Audio File

• Read from / write to multiple audio file
  types (.aiff, .wav, .caf, .m4a, .mp3) in a
  content-agnostic way
• Get metadata (data format, duration,
  iTunes/ID3 info)
Helpers: Audio File
         Stream

• Read audio from non-random-access
  source like a network stream
• Discover encoding and encapsulation on
  the fly, then deliver audio packets to client
  application
Helpers: Converters

• Convert buffers of audio to and from
  different encodings
• One side must be in an uncompressed
  format (i.e., Linear PCM)
Helpers: ExtAudioFile

• Combine file I/O and format conversion
• Read a compressed file into PCM buffers
• Write PCM buffers into a compressed file
Helpers: Audio Session
• iOS-only API to negotiate use of audio
  resources with the rest of the system
• Deetermine whether your app mixes with
  other apps’ audio, honors ring/silent
  switch, can play in background, etc.
• Gets notified of audio interruptions
• See also AVAudioSession
Engines: Audio Units

• Low-latency (~10ms) processing of
  capture/play-out audio data
• Effects, mixing, etc.
• Connect units manually or via an AUGraph
• Much more on this topic momentarily…
Engines: Audio Queue
• Convenience API for recording or play-out,
  built atop audio units
• Rather than processing on-demand and on
  Core Audio’s thread, your callback provides
  or receives buffers of audio (at whatever size
  is convenient to you)
• Higher latency, naturally
• Supports compressed formats (MP3, AAC)
Engines: Open AL

• API for 3D spatialized audio, implemented
  atop audio units
• Set a source’s properties (x/y/z
  coordinates, orientation, audio buffer, etc.),
  OpenAL renders what it sounds like to the
  listener from that location
Engines and Helpers
•   Audio Units   •   Audio File

•   Audio Queue   •   Audio File Stream

•   Open AL       •   Audio Converter

                  •   ExtAudioFile

                  •   Audio Session
Audio Units
Audio Unit


  AUSomething
Types of Audio Units
• Output (which also do input)
• Generator
• Converter
• Effect
• Mixer
• Music
Pull Model


 AUSomething
Pull Model


 AUSomething
               AudioUnitRender()
Pull Model



AUSomethingElse   AUSomething
Buses (aka, Elements)

   AUSomethingElse




                     AUSomething




   AUSomethingElse
AUGraph

AUSomethingElse




                  AUSomething




AUSomethingElse
Render Callbacks
OSStatus converterInputRenderCallback (void *inRefCon,
                                        AudioUnitRenderActionFlags *ioActionFlags,
                                        const AudioTimeStamp *inTimeStamp,
                                        UInt32 inBusNumber,
                                        UInt32 inNumberFrames,
                                        AudioBufferList * ioData) {
       CCFWebRadioPlayer *player = (__bridge CCFWebRadioPlayer*) inRefCon;

      // read from buffer
      ioData->mBuffers[0].mData = player.preRenderData;

      return noErr;
}



                                                                                     AUSomething




                                                          AUSomethingElse
AURemoteIO
• Output unit used for play-out, capture
• A Core Audio thread repeatedly and
  automatically calls AudioUnitRender()
• Must set EnableIO property to explicitly
  enable capture and/or play-out
  • Capture requires setting appropriate
    AudioSession category
Create AURemoteIO
CheckError(NewAUGraph(&_auGraph),
! !     "couldn't create au graph");
!
CheckError(AUGraphOpen(_auGraph),
! !     "couldn't open au graph");
!
AudioComponentDescription componentDesc;
componentDesc.componentType = kAudioUnitType_Output;
componentDesc.componentSubType = kAudioUnitSubType_RemoteIO;
componentDesc.componentManufacturer =
             kAudioUnitManufacturer_Apple;
!
AUNode remoteIONode;
CheckError(AUGraphAddNode(_auGraph,
! ! ! ! ! !      &componentDesc,
! ! ! ! ! !      &remoteIONode),
! !     "couldn't add remote io node");
Getting an AudioUnit
        from AUNode

!   CheckError(AUGraphNodeInfo(self.auGraph,
!   ! ! ! ! ! !       remoteIONode,
!   ! ! ! ! ! !       NULL,
!   ! ! ! ! ! !       &_remoteIOUnit),
!   ! !     "couldn't get remote io unit from node");
AURemoteIO Buses


     AURemoteIO
AURemoteIO Buses


     AURemoteIO
                       bus 0
                  to output H/W
AURemoteIO Buses


           AURemoteIO
  bus 0                      bus 0
from app                to output H/W
AURemoteIO Buses

     bus 1
from input H/W
                 AURemoteIO
    bus 0                          bus 0
  from app                    to output H/W
AURemoteIO Buses

     bus 1                        bus 1
from input H/W                   to app
                 AURemoteIO
    bus 0                          bus 0
  from app                    to output H/W
EnableIO
!   UInt32 oneFlag = 1;
!   UInt32 busZero = 0;
!   CheckError(AudioUnitSetProperty(self.remoteIOUnit,
!   ! ! ! ! ! ! ! ! kAudioOutputUnitProperty_EnableIO,
!   ! ! ! ! ! ! ! ! kAudioUnitScope_Output,
!   ! ! ! ! ! ! ! ! busZero,
!   ! ! ! ! ! ! ! ! &oneFlag,
!   ! ! ! ! ! ! ! ! sizeof(oneFlag)),
!   ! !     "couldn't enable remote io output");
!   UInt32 busOne = 1;
!   CheckError(AudioUnitSetProperty(self.remoteIOUnit,
!   ! ! ! ! ! ! ! ! kAudioOutputUnitProperty_EnableIO,
!   ! ! ! ! ! ! ! ! kAudioUnitScope_Input,
!   ! ! ! ! ! ! ! ! busOne,
!   ! ! ! ! ! ! ! ! &oneFlag,
!   ! ! ! ! ! ! ! ! sizeof(oneFlag)),
!   ! !     "couldn't enable remote io input");
Pass Through

     bus 1
from input H/W
                 AURemoteIO
                                   bus 0
                              to output H/W
Connect In to Out
!   UInt32 busZero = 0;
!   UInt32 busOne = 1;
!   CheckError(AUGraphConnectNodeInput(self.auGraph,
!   ! ! ! ! ! ! ! !        remoteIONode,
!   ! ! ! ! ! ! ! !        busOne,
!   ! ! ! ! ! ! ! !        remoteIONode,
!   ! ! ! ! ! ! ! !        busZero),
!   ! !     "couldn't connect remote io bus 1 to 0");
Pass-Through with Effect

                       AUEffect




          bus 1
     from input H/W
                      AURemoteIO
                                        bus 0
                                   to output H/W
Demo: Delay Effect
      New in iOS 6!
Creating the AUDelay
! componentDesc.componentType = kAudioUnitType_Effect;
! componentDesc.componentSubType = kAudioUnitSubType_Delay;
! componentDesc.componentManufacturer =
           kAudioUnitManufacturer_Apple;
!
! AUNode effectNode;
! CheckError(AUGraphAddNode(self.auGraph,
! ! ! ! ! ! !      &componentDesc,
! ! ! ! ! ! !      &effectNode),
! ! !     "couldn't create effect node");
! AudioUnit effectUnit;
! CheckError(AUGraphNodeInfo(self.auGraph,
! ! ! ! ! ! !        effectNode,
! ! ! ! ! ! !        NULL,
! ! ! ! ! ! !        &effectUnit),
! ! !     "couldn't get effect unit from node");
The problem with effect
         units
• Audio Units available since iPhone OS 2.0
  prefer int formats
• Effect units arrived with iOS 5 (arm7 era)
  and only work with float format
• Have to set the AUEffect unit’s format on
  AURemoteIO
Setting formats
!   AudioStreamBasicDescription effectDataFormat;
!   UInt32 propSize = sizeof (effectDataFormat);
!   CheckError(AudioUnitGetProperty(effectUnit,
!   ! ! ! ! ! ! ! ! kAudioUnitProperty_StreamFormat,
!   ! ! ! ! ! ! ! ! kAudioUnitScope_Output,
!   ! ! ! ! ! ! ! ! busZero,
!   ! ! ! ! ! ! ! ! &effectDataFormat,
!   ! ! ! ! ! ! ! ! &propSize),
!   ! !     "couldn't read effect format");
!   CheckError(AudioUnitSetProperty(self.remoteIOUnit,
!   ! ! ! ! ! ! ! ! kAudioUnitProperty_StreamFormat,
!   ! ! ! ! ! ! ! ! kAudioUnitScope_Output,
!   ! ! ! ! ! ! ! ! busOne,
!   ! ! ! ! ! ! ! ! &effectDataFormat,
!   ! ! ! ! ! ! ! ! propSize),
!   ! !     "couldn't set bus one output format");

    Then repeat AudioUnitSetProperty() for input scope / bus 0
AUNewTimePitch

• New in iOS 6!
• Allows you to change pitch independent of
  time, or time independent of pitch
• How do you use it?
AUTimePitch
!   AudioComponentDescription effectcd = {0};
!   effectcd.componentType = kAudioUnitType_FormatConverter;
!   effectcd.componentSubType = kAudioUnitSubType_NewTimePitch;
!   effectcd.componentManufacturer = kAudioUnitManufacturer_Apple;
!
!   AUNode effectNode;
!   CheckError(AUGraphAddNode(self.auGraph,
!   ! ! ! ! ! !       &effectcd,
!   ! ! ! ! ! !       &effectNode),
!   ! !      "couldn't get effect node [time/pitch]");




    Notice the type is AUFormatConverter, not AUEffect
AudioUnitParameters.h
      // Parameters for AUNewTimePitch
      enum {
      ! ! // Global, rate, 1/32 -> 32.0, 1.0
      ! kNewTimePitchParam_Rate! ! ! ! ! ! =
                               !                  0,
      ! ! // Global, Cents, -2400 -> 2400, 1.0
      ! kNewTimePitchParam_Pitch! ! ! ! ! ! =     1,
      ! ! // Global, generic, 3.0 -> 32.0, 8.0
      ! kNewTimePitchParam_Overlap! ! ! ! ! !     = 4,
      ! ! // Global, Boolean, 0->1, 1
      ! kNewTimePitchParam_EnablePeakLocking! !   ! = 6
      };



This is the entire documentation for the AUNewTimePitch parameters
AUNewTimePitch
      parameters
• Rate: kNewTimePitchParam_Rate takes a
  Float32 rate from 1/32 speed to 32x
  speed.
 • Use powers of 2: 1/32, 1/16, …, 2, 4, 8…
• Pitch: kNewTimePitchParam_Pitch takes
  a Float32 representing cents, meaning
  1/100 of a musical semitone
Pitch shifting


• Pitch can vary, time does not
• Suitable for real-time sources, such as audio
  capture
Demo: Pitch Shift
     New in iOS 6!
Rate shifting
• Rate can vary, pitch does not
 • Think of 1.5x and 2x speed modes in
    Podcasts app
• Not suitable for real-time sources, as data
  will be consumed faster. Files work well.
  • Sources must be able to map time
    systems with
    kAudioUnitProperty_InputSamplesInOutput
Demo: Rate Shift
     New in iOS 6!
AUSplitter

                   AUSomethingElse




AUSplitter



                   AUSomethingElse




         New in iOS 6!
AUMatrixMixer
AUSomethingElse



                                     AUSomethingElse




AUSomethingElse      AUMatrixMixer



                                     AUSomethingElse




AUSomethingElse




                  New in iOS 6!
Audio Queues
(and the APIs that help them)
AudioQueue
• Easier than AURemoteIO - provide data
  when you want to, less time pressure, can
  accept or provide compressed formats
  (MP3, AAC)
• Recording queue - receive buffers of
  captured audio in a callback
• Play-out queue - enqueue buffers of audio
  to play, optionally refill in a callback
AudioQueue


   2   1   0
Common AQ scenarios
• File player - Read from file and “prime”
  queue buffers, start queue, when called
  back with used buffer, refill from next part
  of file
• Synthesis - Maintain state in your own
  code, write raw samples into buffers during
  callbacks
Web Radio

• Thursday class’ third project
• Use Audio File Stream Services to pick out
  audio data from a network stream
• Enqueue these packets as new AQ buffers
• Dispose used buffers in callback
Parsing web radio
Parsing web radio
NSURLConnection delivers
NSData buffers, containing audio
and framing info. We pass it to              NSData                 NSData
Audio File Services.               Packets      Packets   Packets   Packets   Packets
Parsing web radio
NSURLConnection delivers
NSData buffers, containing audio
and framing info. We pass it to                NSData                         NSData
Audio File Services.                 Packets        Packets         Packets   Packets   Packets




                                     Packets   Packets
Audio File Services calls us back
with parsed packets of audio data.   Packets   Packets    Packets
Parsing web radio
NSURLConnection delivers
NSData buffers, containing audio
and framing info. We pass it to                NSData                                  NSData
Audio File Services.                 Packets        Packets              Packets       Packets   Packets




                                     Packets   Packets
Audio File Services calls us back
with parsed packets of audio data.   Packets   Packets       Packets




We create an AudioQueueBuffer
                                                Packets                      Packets
with those packets and enqueue it                 Packets
                                                    2          Packets
                                                                 1              0
for play-out.
                                                   Packets                   Packets
A complex thing!

• What if we want to see that data after it’s
  been decoded to PCM and is about to be
  played?
  • e.g., spectrum analysis, effects, visualizers
• AudioQueue design is “fire-and-forget”
AudioQueue Tap!




http://www.last.fm/music/Spinal+Tap
AudioQueueProcessingTap

• Set as a property on the Audio Queue
• Calls back to your function with decoded
  (PCM) audio data
• Three types: pre- or post- effects (that the
  AQ performs), or siphon. First two can
  modify the data.
• Only documentation is in AudioQueue.h
Creating an AQ Tap
!   !     // create the tap
!   !     UInt32 maxFrames = 0;
!   !     AudioStreamBasicDescription tapFormat = {0};
!   !     AudioQueueProcessingTapRef tapRef;
!   !     CheckError(AudioQueueProcessingTapNew(audioQueue,
!   !     ! ! ! ! ! ! ! ! !       tapProc,
!   !     ! ! ! ! ! ! ! ! !       (__bridge void *)(player),
!   !     ! ! ! ! ! ! ! ! !       kAudioQueueProcessingTap_PreEffects,
!   !     ! ! ! ! ! ! ! ! !       &maxFrames,
!   !     ! ! ! ! ! ! ! ! !       &tapFormat,
!   !     ! ! ! ! ! ! ! ! !       &tapRef),
!   !     ! !     "couldn't create AQ tap");



        Notice that you receive maxFrames and tapFormat. These do not appear to be settable.
AQ Tap Proc
void tapProc (void *                            inClientData,
! ! !     AudioQueueProcessingTapRef       inAQTap,
! ! !     UInt32                           inNumberFrames,
! ! !     AudioTimeStamp *                 ioTimeStamp,
! ! !     UInt32 *                         ioFlags,
! ! !     UInt32 *                         outNumberFrames,
! ! !     AudioBufferList *                ioData) {
! CCFWebRadioPlayer *player =
        (__bridge CCFWebRadioPlayer*) inClientData;
! UInt32 getSourceFlags = 0;
! UInt32 getSourceFrames = 0;
! AudioQueueProcessingTapGetSourceAudio(inAQTap,
! ! ! ! ! ! ! ! ! !         inNumberFrames,
! ! ! ! ! ! ! ! ! !         ioTimeStamp,
! ! ! ! ! ! ! ! ! !         &getSourceFlags,
! ! ! ! ! ! ! ! ! !         &getSourceFrames,
! ! ! ! ! ! ! ! ! !         ioData);
  // then do something with ioData
  // ...
So what should we do
   with the audio?
So what should we do
   with the audio?


  Let’s apply our pitch-shift effect
Shouldn’t this work?


       AUEffect
Shouldn’t this work?


       AUEffect
                  AudioUnitRender()
AudioUnitRender()
• Last argument is an AudioBufferList, whose
  AudioBuffer members have mData pointers
  • If mData != NULL, audio unit does its
    thing with those samples
  • If mData == NULL, audio data pulls from
    whatever it’s connected to
• So we just call with AudioBufferList ioData
  we got from tap callback, right?
Psych!

• AQ tap provides data as signed ints
• Effect units only work with floating point
• We need to do an on-the-spot format
  conversion
invalidname’s convert-
      and-effect recipe
      OSStatus converterInputRenderCallback (void *inRefCon,
                                              AudioUnitRenderActionFlags *ioActionFlags,
                                              const AudioTimeStamp *inTimeStamp,
                                              UInt32 inBusNumber,
                                              UInt32 inNumberFrames,
                                              AudioBufferList * ioData) {
             CCFWebRadioPlayer *player = (__bridge CCFWebRadioPlayer*) inRefCon;

            // read from buffer
            ioData->mBuffers[0].mData = player.preRenderData;

            return noErr;
      }




               AUConverter                               AUEffect                          AUConverter   AUGenericOutput




Note: red arrows are float format, yellow arrows are int
How it works

• AUGraph: AUConverter → AUEffect →
  AUConverter → AUGenericOutput
• Top AUConverter is connected to a render
  callback function
The trick!
• Copy mData pointer to a state variable and
  NULL it in ioData
• Call AudioQueueRender() on output unit.
  The NULL makes it pull from the graph.
• Top of the graph pulls on render callback,
  which gives it back the mData we copied
  off.
Yes, really
          This is the rest of tapProc()
! // copy off the ioData so the graph can read from it
  // in render callback
! player.preRenderData = ioData->mBuffers[0].mData;
! ioData->mBuffers[0].mData = NULL;
!
! OSStatus renderErr = noErr;
! AudioUnitRenderActionFlags actionFlags = 0;
! renderErr = AudioUnitRender(player.genericOutputUnit,
! ! ! ! ! ! ! ! &actionFlags,
! ! ! ! ! ! ! ! player.renderTimeStamp,
! ! ! ! ! ! ! ! 0,
! ! ! ! ! ! ! ! inNumberFrames,
! ! ! ! ! ! ! ! ioData);
! NSLog (@"AudioUnitRender, renderErr = %ld",renderErr);
}
Yes, really
  This is the render callback that supplies data to the int→float converter

OSStatus converterInputRenderCallback (void *inRefCon,
! ! ! ! ! ! ! ! !        AudioUnitRenderActionFlags *ioActionFlags,
! ! ! ! ! ! ! ! !        const AudioTimeStamp *inTimeStamp,
! ! ! ! ! ! ! ! !        UInt32 inBusNumber,
! ! ! ! ! ! ! ! !        UInt32 inNumberFrames,
! ! ! ! ! ! ! ! !        AudioBufferList * ioData) {
! CCFWebRadioPlayer *player =
               (__bridge CCFWebRadioPlayer*) inRefCon;
!
! // read from buffer
! ioData->mBuffers[0].mData = player.preRenderData;

! return noErr;
}
Demo: AQ Tap +
AUNewTimePitch
     New in iOS 6!
Other new stuff
Multi-Route
• Ordinarily, one input or output is active:
  earpiece, speaker, headphones, dock-
  connected device
  • “Last in wins”
• With AV Session “multi-route” category,
  you can use several at once
• WWDC 2012 session 505
Utility classes moved
         again
• C++ utilities, including the CARingBuffer
 • < Xcode 4.3, installed into /Developer
 • Xcode 4.3-4.4, optional download from
    developer.apple.com
  • ≧ Xcode 4.5, sample code project “Core
    Audio Utility Classes”
Takeaways
• Core Audio fundamentals never change
• New stuff is added as properties, typedefs,
  enums, etc.
• Watch the SDK API diffs document to find
  the new stuff
• Hope you like header files and
  experimentation
Q&A
• Slides will be posted to slideshare.net/
  invalidname
• Code will be linked from there and my blog
• Watch CocoaConf PDX glassboard,
  @invalidname on Twitter/ADN, or [Time
  code]; blog for announcement
• Thanks!

More Related Content

What's hot

Integrating Voice Through Adhearsion
Integrating Voice Through AdhearsionIntegrating Voice Through Adhearsion
Integrating Voice Through AdhearsionMojo Lingo
 
Deep dive into Android’s audio latency problem
Deep dive into Android’s audio latency problemDeep dive into Android’s audio latency problem
Deep dive into Android’s audio latency problemSirawat Pitaksarit
 
Android Audio & OpenSL
Android Audio & OpenSLAndroid Audio & OpenSL
Android Audio & OpenSLYoss Cohen
 
Html5 game, websocket e arduino
Html5 game, websocket e arduino Html5 game, websocket e arduino
Html5 game, websocket e arduino Giuseppe Modarelli
 
Squence Annotation
Squence AnnotationSquence Annotation
Squence AnnotationLuke Finlay
 
How you think the sound in your chosen example has been produced zelda
How you think the sound in your chosen example has been produced zeldaHow you think the sound in your chosen example has been produced zelda
How you think the sound in your chosen example has been produced zeldaconor0994
 

What's hot (6)

Integrating Voice Through Adhearsion
Integrating Voice Through AdhearsionIntegrating Voice Through Adhearsion
Integrating Voice Through Adhearsion
 
Deep dive into Android’s audio latency problem
Deep dive into Android’s audio latency problemDeep dive into Android’s audio latency problem
Deep dive into Android’s audio latency problem
 
Android Audio & OpenSL
Android Audio & OpenSLAndroid Audio & OpenSL
Android Audio & OpenSL
 
Html5 game, websocket e arduino
Html5 game, websocket e arduino Html5 game, websocket e arduino
Html5 game, websocket e arduino
 
Squence Annotation
Squence AnnotationSquence Annotation
Squence Annotation
 
How you think the sound in your chosen example has been produced zelda
How you think the sound in your chosen example has been produced zeldaHow you think the sound in your chosen example has been produced zelda
How you think the sound in your chosen example has been produced zelda
 

Similar to Core Audio in iOS 6: An In-Depth Look at the Low-Level Audio Framework

Get On The Audiobus (CocoaConf Boston, October 2013)
Get On The Audiobus (CocoaConf Boston, October 2013)Get On The Audiobus (CocoaConf Boston, October 2013)
Get On The Audiobus (CocoaConf Boston, October 2013)Chris Adamson
 
Get On The Audiobus (CocoaConf Atlanta, November 2013)
Get On The Audiobus (CocoaConf Atlanta, November 2013)Get On The Audiobus (CocoaConf Atlanta, November 2013)
Get On The Audiobus (CocoaConf Atlanta, November 2013)Chris Adamson
 
Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]
Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]
Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]Chris Adamson
 
Openframworks x Mobile
Openframworks x MobileOpenframworks x Mobile
Openframworks x MobileJanet Huang
 
Core MIDI and Friends
Core MIDI and FriendsCore MIDI and Friends
Core MIDI and FriendsChris Adamson
 
Guitar Effects with the HTML5 Audio API
Guitar Effects with the HTML5 Audio APIGuitar Effects with the HTML5 Audio API
Guitar Effects with the HTML5 Audio APICathy Lill
 
Arduino Workshop @ MSA University
Arduino Workshop @ MSA UniversityArduino Workshop @ MSA University
Arduino Workshop @ MSA UniversityAhmed Magdy Farid
 
Building Modern Audio Apps with AVAudioEngine
Building Modern Audio Apps with AVAudioEngineBuilding Modern Audio Apps with AVAudioEngine
Building Modern Audio Apps with AVAudioEngineBob McCune
 
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)Chris Adamson
 
Core audio
Core audioCore audio
Core audioscussen
 
[COSCUP 2013] Audio Competing
[COSCUP 2013] Audio Competing[COSCUP 2013] Audio Competing
[COSCUP 2013] Audio CompetingAlive Kuo
 
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)Chris Adamson
 
An Introduction to Go
An Introduction to GoAn Introduction to Go
An Introduction to GoCloudflare
 
Modulator p5: Make your Processing Sketches More Dynamic with Modulators
Modulator p5: Make your Processing Sketches More Dynamic with ModulatorsModulator p5: Make your Processing Sketches More Dynamic with Modulators
Modulator p5: Make your Processing Sketches More Dynamic with ModulatorsNathan Koch
 
iPlayground: CarPlay and MFI Hearing Aids
iPlayground: CarPlay and MFI Hearing AidsiPlayground: CarPlay and MFI Hearing Aids
iPlayground: CarPlay and MFI Hearing AidsWeizhong Yang
 
Setting Up An Interactive AudioInspector Demo
Setting Up An Interactive AudioInspector DemoSetting Up An Interactive AudioInspector Demo
Setting Up An Interactive AudioInspector DemoMediaservices
 
Moving to modules
Moving to modulesMoving to modules
Moving to modulesSean Mize
 
Timeshift Everything, Miss Nothing - Mashup your PVR with Kamaelia
Timeshift Everything, Miss Nothing - Mashup your PVR with KamaeliaTimeshift Everything, Miss Nothing - Mashup your PVR with Kamaelia
Timeshift Everything, Miss Nothing - Mashup your PVR with Kamaeliakamaelian
 

Similar to Core Audio in iOS 6: An In-Depth Look at the Low-Level Audio Framework (20)

Get On The Audiobus (CocoaConf Boston, October 2013)
Get On The Audiobus (CocoaConf Boston, October 2013)Get On The Audiobus (CocoaConf Boston, October 2013)
Get On The Audiobus (CocoaConf Boston, October 2013)
 
Get On The Audiobus (CocoaConf Atlanta, November 2013)
Get On The Audiobus (CocoaConf Atlanta, November 2013)Get On The Audiobus (CocoaConf Atlanta, November 2013)
Get On The Audiobus (CocoaConf Atlanta, November 2013)
 
Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]
Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]
Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]
 
Openframworks x Mobile
Openframworks x MobileOpenframworks x Mobile
Openframworks x Mobile
 
Core MIDI and Friends
Core MIDI and FriendsCore MIDI and Friends
Core MIDI and Friends
 
Guitar Effects with the HTML5 Audio API
Guitar Effects with the HTML5 Audio APIGuitar Effects with the HTML5 Audio API
Guitar Effects with the HTML5 Audio API
 
Arduino Workshop @ MSA University
Arduino Workshop @ MSA UniversityArduino Workshop @ MSA University
Arduino Workshop @ MSA University
 
Building Modern Audio Apps with AVAudioEngine
Building Modern Audio Apps with AVAudioEngineBuilding Modern Audio Apps with AVAudioEngine
Building Modern Audio Apps with AVAudioEngine
 
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
 
Core audio
Core audioCore audio
Core audio
 
[COSCUP 2013] Audio Competing
[COSCUP 2013] Audio Competing[COSCUP 2013] Audio Competing
[COSCUP 2013] Audio Competing
 
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
 
An Introduction to Go
An Introduction to GoAn Introduction to Go
An Introduction to Go
 
L18 applets
L18 appletsL18 applets
L18 applets
 
Modulator p5: Make your Processing Sketches More Dynamic with Modulators
Modulator p5: Make your Processing Sketches More Dynamic with ModulatorsModulator p5: Make your Processing Sketches More Dynamic with Modulators
Modulator p5: Make your Processing Sketches More Dynamic with Modulators
 
Audio Mixing Console
Audio Mixing ConsoleAudio Mixing Console
Audio Mixing Console
 
iPlayground: CarPlay and MFI Hearing Aids
iPlayground: CarPlay and MFI Hearing AidsiPlayground: CarPlay and MFI Hearing Aids
iPlayground: CarPlay and MFI Hearing Aids
 
Setting Up An Interactive AudioInspector Demo
Setting Up An Interactive AudioInspector DemoSetting Up An Interactive AudioInspector Demo
Setting Up An Interactive AudioInspector Demo
 
Moving to modules
Moving to modulesMoving to modules
Moving to modules
 
Timeshift Everything, Miss Nothing - Mashup your PVR with Kamaelia
Timeshift Everything, Miss Nothing - Mashup your PVR with KamaeliaTimeshift Everything, Miss Nothing - Mashup your PVR with Kamaelia
Timeshift Everything, Miss Nothing - Mashup your PVR with Kamaelia
 

More from Chris Adamson

Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)Chris Adamson
 
Whatever Happened to Visual Novel Anime? (JAFAX 2018)
Whatever Happened to Visual Novel Anime? (JAFAX 2018)Whatever Happened to Visual Novel Anime? (JAFAX 2018)
Whatever Happened to Visual Novel Anime? (JAFAX 2018)Chris Adamson
 
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)Chris Adamson
 
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...Chris Adamson
 
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is FineCocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is FineChris Adamson
 
Forward Swift 2017: Media Frameworks and Swift: This Is Fine
Forward Swift 2017: Media Frameworks and Swift: This Is FineForward Swift 2017: Media Frameworks and Swift: This Is Fine
Forward Swift 2017: Media Frameworks and Swift: This Is FineChris Adamson
 
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...Chris Adamson
 
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)Chris Adamson
 
Firebase: Totally Not Parse All Over Again (Unless It Is)
Firebase: Totally Not Parse All Over Again (Unless It Is)Firebase: Totally Not Parse All Over Again (Unless It Is)
Firebase: Totally Not Parse All Over Again (Unless It Is)Chris Adamson
 
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)Chris Adamson
 
Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...
Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...
Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...Chris Adamson
 
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014Chris Adamson
 
Stupid Video Tricks, CocoaConf Seattle 2014
Stupid Video Tricks, CocoaConf Seattle 2014Stupid Video Tricks, CocoaConf Seattle 2014
Stupid Video Tricks, CocoaConf Seattle 2014Chris Adamson
 
Stupid Video Tricks, CocoaConf Las Vegas
Stupid Video Tricks, CocoaConf Las VegasStupid Video Tricks, CocoaConf Las Vegas
Stupid Video Tricks, CocoaConf Las VegasChris Adamson
 
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)Chris Adamson
 
Stupid Video Tricks (CocoaConf DC, March 2014)
Stupid Video Tricks (CocoaConf DC, March 2014)Stupid Video Tricks (CocoaConf DC, March 2014)
Stupid Video Tricks (CocoaConf DC, March 2014)Chris Adamson
 
Introduction to the Roku SDK
Introduction to the Roku SDKIntroduction to the Roku SDK
Introduction to the Roku SDKChris Adamson
 
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)Chris Adamson
 
iOS Media APIs (MobiDevDay Detroit, May 2013)
iOS Media APIs (MobiDevDay Detroit, May 2013)iOS Media APIs (MobiDevDay Detroit, May 2013)
iOS Media APIs (MobiDevDay Detroit, May 2013)Chris Adamson
 

More from Chris Adamson (20)

Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
 
Whatever Happened to Visual Novel Anime? (JAFAX 2018)
Whatever Happened to Visual Novel Anime? (JAFAX 2018)Whatever Happened to Visual Novel Anime? (JAFAX 2018)
Whatever Happened to Visual Novel Anime? (JAFAX 2018)
 
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
 
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
 
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is FineCocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
 
Forward Swift 2017: Media Frameworks and Swift: This Is Fine
Forward Swift 2017: Media Frameworks and Swift: This Is FineForward Swift 2017: Media Frameworks and Swift: This Is Fine
Forward Swift 2017: Media Frameworks and Swift: This Is Fine
 
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
 
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
 
Firebase: Totally Not Parse All Over Again (Unless It Is)
Firebase: Totally Not Parse All Over Again (Unless It Is)Firebase: Totally Not Parse All Over Again (Unless It Is)
Firebase: Totally Not Parse All Over Again (Unless It Is)
 
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
 
Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...
Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...
Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...
 
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
 
Stupid Video Tricks, CocoaConf Seattle 2014
Stupid Video Tricks, CocoaConf Seattle 2014Stupid Video Tricks, CocoaConf Seattle 2014
Stupid Video Tricks, CocoaConf Seattle 2014
 
Stupid Video Tricks, CocoaConf Las Vegas
Stupid Video Tricks, CocoaConf Las VegasStupid Video Tricks, CocoaConf Las Vegas
Stupid Video Tricks, CocoaConf Las Vegas
 
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
 
Stupid Video Tricks (CocoaConf DC, March 2014)
Stupid Video Tricks (CocoaConf DC, March 2014)Stupid Video Tricks (CocoaConf DC, March 2014)
Stupid Video Tricks (CocoaConf DC, March 2014)
 
Stupid Video Tricks
Stupid Video TricksStupid Video Tricks
Stupid Video Tricks
 
Introduction to the Roku SDK
Introduction to the Roku SDKIntroduction to the Roku SDK
Introduction to the Roku SDK
 
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
 
iOS Media APIs (MobiDevDay Detroit, May 2013)
iOS Media APIs (MobiDevDay Detroit, May 2013)iOS Media APIs (MobiDevDay Detroit, May 2013)
iOS Media APIs (MobiDevDay Detroit, May 2013)
 

Recently uploaded

Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsHyundai Motor Group
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAndikSusilo4
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxnull - The Open Security Community
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?XfilesPro
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetHyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetEnjoy Anytime
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 

Recently uploaded (20)

Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & Application
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetHyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 

Core Audio in iOS 6: An In-Depth Look at the Low-Level Audio Framework

  • 1. Core Audio in iOS 6 Chris Adamson • @invalidname CocoaConf PDX October 27, 2012 Slides and sample code will be available later today
  • 7. Legitimate copies! • Amazon (paper or Kindle) • Barnes & Noble (paper or Nook) • Apple (iBooks) • Direct from InformIT (paper, eBook [.epub + .mobi + .pdf], or Bundle) • 35% off with code COREAUDIO3174
  • 8. What You’ll Learn • What Core Audio does and doesn’t do • When to use and not use it • What’s new in Core Audio for iOS 6
  • 9.
  • 10. Simple things should be simple, complex things should be possible. –Alan Kay
  • 11. AV Foundation, Media Player Simple things should be simple, complex things should be possible. –Alan Kay
  • 12. AV Foundation, Media Player Simple things should be simple, complex things should be possible. –Alan Kay Core Audio
  • 13. Core Audio • Low-level C framework for processing audio • Capture, play-out, real-time or off-line processing • The “complex things should be possible” part of audio on OS X and iOS
  • 14. Chris’ CA Taxonomy • Engines: process streams of audio • Capture, play-out, mixing, effects processing • Helpers: deal with formats, encodings, etc. • File I/O, stream I/O, format conversion, iOS “session” management
  • 15. Helpers: Audio File • Read from / write to multiple audio file types (.aiff, .wav, .caf, .m4a, .mp3) in a content-agnostic way • Get metadata (data format, duration, iTunes/ID3 info)
  • 16. Helpers: Audio File Stream • Read audio from non-random-access source like a network stream • Discover encoding and encapsulation on the fly, then deliver audio packets to client application
  • 17. Helpers: Converters • Convert buffers of audio to and from different encodings • One side must be in an uncompressed format (i.e., Linear PCM)
  • 18. Helpers: ExtAudioFile • Combine file I/O and format conversion • Read a compressed file into PCM buffers • Write PCM buffers into a compressed file
  • 19. Helpers: Audio Session • iOS-only API to negotiate use of audio resources with the rest of the system • Deetermine whether your app mixes with other apps’ audio, honors ring/silent switch, can play in background, etc. • Gets notified of audio interruptions • See also AVAudioSession
  • 20. Engines: Audio Units • Low-latency (~10ms) processing of capture/play-out audio data • Effects, mixing, etc. • Connect units manually or via an AUGraph • Much more on this topic momentarily…
  • 21. Engines: Audio Queue • Convenience API for recording or play-out, built atop audio units • Rather than processing on-demand and on Core Audio’s thread, your callback provides or receives buffers of audio (at whatever size is convenient to you) • Higher latency, naturally • Supports compressed formats (MP3, AAC)
  • 22. Engines: Open AL • API for 3D spatialized audio, implemented atop audio units • Set a source’s properties (x/y/z coordinates, orientation, audio buffer, etc.), OpenAL renders what it sounds like to the listener from that location
  • 23. Engines and Helpers • Audio Units • Audio File • Audio Queue • Audio File Stream • Open AL • Audio Converter • ExtAudioFile • Audio Session
  • 25. Audio Unit AUSomething
  • 26. Types of Audio Units • Output (which also do input) • Generator • Converter • Effect • Mixer • Music
  • 28. Pull Model AUSomething AudioUnitRender()
  • 30. Buses (aka, Elements) AUSomethingElse AUSomething AUSomethingElse
  • 31. AUGraph AUSomethingElse AUSomething AUSomethingElse
  • 32. Render Callbacks OSStatus converterInputRenderCallback (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData) { CCFWebRadioPlayer *player = (__bridge CCFWebRadioPlayer*) inRefCon; // read from buffer ioData->mBuffers[0].mData = player.preRenderData; return noErr; } AUSomething AUSomethingElse
  • 33. AURemoteIO • Output unit used for play-out, capture • A Core Audio thread repeatedly and automatically calls AudioUnitRender() • Must set EnableIO property to explicitly enable capture and/or play-out • Capture requires setting appropriate AudioSession category
  • 34. Create AURemoteIO CheckError(NewAUGraph(&_auGraph), ! ! "couldn't create au graph"); ! CheckError(AUGraphOpen(_auGraph), ! ! "couldn't open au graph"); ! AudioComponentDescription componentDesc; componentDesc.componentType = kAudioUnitType_Output; componentDesc.componentSubType = kAudioUnitSubType_RemoteIO; componentDesc.componentManufacturer = kAudioUnitManufacturer_Apple; ! AUNode remoteIONode; CheckError(AUGraphAddNode(_auGraph, ! ! ! ! ! ! &componentDesc, ! ! ! ! ! ! &remoteIONode), ! ! "couldn't add remote io node");
  • 35. Getting an AudioUnit from AUNode ! CheckError(AUGraphNodeInfo(self.auGraph, ! ! ! ! ! ! ! remoteIONode, ! ! ! ! ! ! ! NULL, ! ! ! ! ! ! ! &_remoteIOUnit), ! ! ! "couldn't get remote io unit from node");
  • 36. AURemoteIO Buses AURemoteIO
  • 37. AURemoteIO Buses AURemoteIO bus 0 to output H/W
  • 38. AURemoteIO Buses AURemoteIO bus 0 bus 0 from app to output H/W
  • 39. AURemoteIO Buses bus 1 from input H/W AURemoteIO bus 0 bus 0 from app to output H/W
  • 40. AURemoteIO Buses bus 1 bus 1 from input H/W to app AURemoteIO bus 0 bus 0 from app to output H/W
  • 41. EnableIO ! UInt32 oneFlag = 1; ! UInt32 busZero = 0; ! CheckError(AudioUnitSetProperty(self.remoteIOUnit, ! ! ! ! ! ! ! ! ! kAudioOutputUnitProperty_EnableIO, ! ! ! ! ! ! ! ! ! kAudioUnitScope_Output, ! ! ! ! ! ! ! ! ! busZero, ! ! ! ! ! ! ! ! ! &oneFlag, ! ! ! ! ! ! ! ! ! sizeof(oneFlag)), ! ! ! "couldn't enable remote io output"); ! UInt32 busOne = 1; ! CheckError(AudioUnitSetProperty(self.remoteIOUnit, ! ! ! ! ! ! ! ! ! kAudioOutputUnitProperty_EnableIO, ! ! ! ! ! ! ! ! ! kAudioUnitScope_Input, ! ! ! ! ! ! ! ! ! busOne, ! ! ! ! ! ! ! ! ! &oneFlag, ! ! ! ! ! ! ! ! ! sizeof(oneFlag)), ! ! ! "couldn't enable remote io input");
  • 42. Pass Through bus 1 from input H/W AURemoteIO bus 0 to output H/W
  • 43. Connect In to Out ! UInt32 busZero = 0; ! UInt32 busOne = 1; ! CheckError(AUGraphConnectNodeInput(self.auGraph, ! ! ! ! ! ! ! ! ! remoteIONode, ! ! ! ! ! ! ! ! ! busOne, ! ! ! ! ! ! ! ! ! remoteIONode, ! ! ! ! ! ! ! ! ! busZero), ! ! ! "couldn't connect remote io bus 1 to 0");
  • 44. Pass-Through with Effect AUEffect bus 1 from input H/W AURemoteIO bus 0 to output H/W
  • 45. Demo: Delay Effect New in iOS 6!
  • 46. Creating the AUDelay ! componentDesc.componentType = kAudioUnitType_Effect; ! componentDesc.componentSubType = kAudioUnitSubType_Delay; ! componentDesc.componentManufacturer = kAudioUnitManufacturer_Apple; ! ! AUNode effectNode; ! CheckError(AUGraphAddNode(self.auGraph, ! ! ! ! ! ! ! &componentDesc, ! ! ! ! ! ! ! &effectNode), ! ! ! "couldn't create effect node"); ! AudioUnit effectUnit; ! CheckError(AUGraphNodeInfo(self.auGraph, ! ! ! ! ! ! ! effectNode, ! ! ! ! ! ! ! NULL, ! ! ! ! ! ! ! &effectUnit), ! ! ! "couldn't get effect unit from node");
  • 47. The problem with effect units • Audio Units available since iPhone OS 2.0 prefer int formats • Effect units arrived with iOS 5 (arm7 era) and only work with float format • Have to set the AUEffect unit’s format on AURemoteIO
  • 48. Setting formats ! AudioStreamBasicDescription effectDataFormat; ! UInt32 propSize = sizeof (effectDataFormat); ! CheckError(AudioUnitGetProperty(effectUnit, ! ! ! ! ! ! ! ! ! kAudioUnitProperty_StreamFormat, ! ! ! ! ! ! ! ! ! kAudioUnitScope_Output, ! ! ! ! ! ! ! ! ! busZero, ! ! ! ! ! ! ! ! ! &effectDataFormat, ! ! ! ! ! ! ! ! ! &propSize), ! ! ! "couldn't read effect format"); ! CheckError(AudioUnitSetProperty(self.remoteIOUnit, ! ! ! ! ! ! ! ! ! kAudioUnitProperty_StreamFormat, ! ! ! ! ! ! ! ! ! kAudioUnitScope_Output, ! ! ! ! ! ! ! ! ! busOne, ! ! ! ! ! ! ! ! ! &effectDataFormat, ! ! ! ! ! ! ! ! ! propSize), ! ! ! "couldn't set bus one output format"); Then repeat AudioUnitSetProperty() for input scope / bus 0
  • 49. AUNewTimePitch • New in iOS 6! • Allows you to change pitch independent of time, or time independent of pitch • How do you use it?
  • 50. AUTimePitch ! AudioComponentDescription effectcd = {0}; ! effectcd.componentType = kAudioUnitType_FormatConverter; ! effectcd.componentSubType = kAudioUnitSubType_NewTimePitch; ! effectcd.componentManufacturer = kAudioUnitManufacturer_Apple; ! ! AUNode effectNode; ! CheckError(AUGraphAddNode(self.auGraph, ! ! ! ! ! ! ! &effectcd, ! ! ! ! ! ! ! &effectNode), ! ! ! "couldn't get effect node [time/pitch]"); Notice the type is AUFormatConverter, not AUEffect
  • 51. AudioUnitParameters.h // Parameters for AUNewTimePitch enum { ! ! // Global, rate, 1/32 -> 32.0, 1.0 ! kNewTimePitchParam_Rate! ! ! ! ! ! = ! 0, ! ! // Global, Cents, -2400 -> 2400, 1.0 ! kNewTimePitchParam_Pitch! ! ! ! ! ! = 1, ! ! // Global, generic, 3.0 -> 32.0, 8.0 ! kNewTimePitchParam_Overlap! ! ! ! ! ! = 4, ! ! // Global, Boolean, 0->1, 1 ! kNewTimePitchParam_EnablePeakLocking! ! ! = 6 }; This is the entire documentation for the AUNewTimePitch parameters
  • 52. AUNewTimePitch parameters • Rate: kNewTimePitchParam_Rate takes a Float32 rate from 1/32 speed to 32x speed. • Use powers of 2: 1/32, 1/16, …, 2, 4, 8… • Pitch: kNewTimePitchParam_Pitch takes a Float32 representing cents, meaning 1/100 of a musical semitone
  • 53. Pitch shifting • Pitch can vary, time does not • Suitable for real-time sources, such as audio capture
  • 54. Demo: Pitch Shift New in iOS 6!
  • 55. Rate shifting • Rate can vary, pitch does not • Think of 1.5x and 2x speed modes in Podcasts app • Not suitable for real-time sources, as data will be consumed faster. Files work well. • Sources must be able to map time systems with kAudioUnitProperty_InputSamplesInOutput
  • 56. Demo: Rate Shift New in iOS 6!
  • 57. AUSplitter AUSomethingElse AUSplitter AUSomethingElse New in iOS 6!
  • 58. AUMatrixMixer AUSomethingElse AUSomethingElse AUSomethingElse AUMatrixMixer AUSomethingElse AUSomethingElse New in iOS 6!
  • 59. Audio Queues (and the APIs that help them)
  • 60. AudioQueue • Easier than AURemoteIO - provide data when you want to, less time pressure, can accept or provide compressed formats (MP3, AAC) • Recording queue - receive buffers of captured audio in a callback • Play-out queue - enqueue buffers of audio to play, optionally refill in a callback
  • 61. AudioQueue 2 1 0
  • 62. Common AQ scenarios • File player - Read from file and “prime” queue buffers, start queue, when called back with used buffer, refill from next part of file • Synthesis - Maintain state in your own code, write raw samples into buffers during callbacks
  • 63. Web Radio • Thursday class’ third project • Use Audio File Stream Services to pick out audio data from a network stream • Enqueue these packets as new AQ buffers • Dispose used buffers in callback
  • 65. Parsing web radio NSURLConnection delivers NSData buffers, containing audio and framing info. We pass it to NSData NSData Audio File Services. Packets Packets Packets Packets Packets
  • 66. Parsing web radio NSURLConnection delivers NSData buffers, containing audio and framing info. We pass it to NSData NSData Audio File Services. Packets Packets Packets Packets Packets Packets Packets Audio File Services calls us back with parsed packets of audio data. Packets Packets Packets
  • 67. Parsing web radio NSURLConnection delivers NSData buffers, containing audio and framing info. We pass it to NSData NSData Audio File Services. Packets Packets Packets Packets Packets Packets Packets Audio File Services calls us back with parsed packets of audio data. Packets Packets Packets We create an AudioQueueBuffer Packets Packets with those packets and enqueue it Packets 2 Packets 1 0 for play-out. Packets Packets
  • 68. A complex thing! • What if we want to see that data after it’s been decoded to PCM and is about to be played? • e.g., spectrum analysis, effects, visualizers • AudioQueue design is “fire-and-forget”
  • 70. AudioQueueProcessingTap • Set as a property on the Audio Queue • Calls back to your function with decoded (PCM) audio data • Three types: pre- or post- effects (that the AQ performs), or siphon. First two can modify the data. • Only documentation is in AudioQueue.h
  • 71. Creating an AQ Tap ! ! // create the tap ! ! UInt32 maxFrames = 0; ! ! AudioStreamBasicDescription tapFormat = {0}; ! ! AudioQueueProcessingTapRef tapRef; ! ! CheckError(AudioQueueProcessingTapNew(audioQueue, ! ! ! ! ! ! ! ! ! ! ! tapProc, ! ! ! ! ! ! ! ! ! ! ! (__bridge void *)(player), ! ! ! ! ! ! ! ! ! ! ! kAudioQueueProcessingTap_PreEffects, ! ! ! ! ! ! ! ! ! ! ! &maxFrames, ! ! ! ! ! ! ! ! ! ! ! &tapFormat, ! ! ! ! ! ! ! ! ! ! ! &tapRef), ! ! ! ! "couldn't create AQ tap"); Notice that you receive maxFrames and tapFormat. These do not appear to be settable.
  • 72. AQ Tap Proc void tapProc (void * inClientData, ! ! ! AudioQueueProcessingTapRef inAQTap, ! ! ! UInt32 inNumberFrames, ! ! ! AudioTimeStamp * ioTimeStamp, ! ! ! UInt32 * ioFlags, ! ! ! UInt32 * outNumberFrames, ! ! ! AudioBufferList * ioData) { ! CCFWebRadioPlayer *player = (__bridge CCFWebRadioPlayer*) inClientData; ! UInt32 getSourceFlags = 0; ! UInt32 getSourceFrames = 0; ! AudioQueueProcessingTapGetSourceAudio(inAQTap, ! ! ! ! ! ! ! ! ! ! inNumberFrames, ! ! ! ! ! ! ! ! ! ! ioTimeStamp, ! ! ! ! ! ! ! ! ! ! &getSourceFlags, ! ! ! ! ! ! ! ! ! ! &getSourceFrames, ! ! ! ! ! ! ! ! ! ! ioData); // then do something with ioData // ...
  • 73. So what should we do with the audio?
  • 74. So what should we do with the audio? Let’s apply our pitch-shift effect
  • 76. Shouldn’t this work? AUEffect AudioUnitRender()
  • 77. AudioUnitRender() • Last argument is an AudioBufferList, whose AudioBuffer members have mData pointers • If mData != NULL, audio unit does its thing with those samples • If mData == NULL, audio data pulls from whatever it’s connected to • So we just call with AudioBufferList ioData we got from tap callback, right?
  • 78. Psych! • AQ tap provides data as signed ints • Effect units only work with floating point • We need to do an on-the-spot format conversion
  • 79. invalidname’s convert- and-effect recipe OSStatus converterInputRenderCallback (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData) { CCFWebRadioPlayer *player = (__bridge CCFWebRadioPlayer*) inRefCon; // read from buffer ioData->mBuffers[0].mData = player.preRenderData; return noErr; } AUConverter AUEffect AUConverter AUGenericOutput Note: red arrows are float format, yellow arrows are int
  • 80. How it works • AUGraph: AUConverter → AUEffect → AUConverter → AUGenericOutput • Top AUConverter is connected to a render callback function
  • 81. The trick! • Copy mData pointer to a state variable and NULL it in ioData • Call AudioQueueRender() on output unit. The NULL makes it pull from the graph. • Top of the graph pulls on render callback, which gives it back the mData we copied off.
  • 82. Yes, really This is the rest of tapProc() ! // copy off the ioData so the graph can read from it // in render callback ! player.preRenderData = ioData->mBuffers[0].mData; ! ioData->mBuffers[0].mData = NULL; ! ! OSStatus renderErr = noErr; ! AudioUnitRenderActionFlags actionFlags = 0; ! renderErr = AudioUnitRender(player.genericOutputUnit, ! ! ! ! ! ! ! ! &actionFlags, ! ! ! ! ! ! ! ! player.renderTimeStamp, ! ! ! ! ! ! ! ! 0, ! ! ! ! ! ! ! ! inNumberFrames, ! ! ! ! ! ! ! ! ioData); ! NSLog (@"AudioUnitRender, renderErr = %ld",renderErr); }
  • 83. Yes, really This is the render callback that supplies data to the int→float converter OSStatus converterInputRenderCallback (void *inRefCon, ! ! ! ! ! ! ! ! ! AudioUnitRenderActionFlags *ioActionFlags, ! ! ! ! ! ! ! ! ! const AudioTimeStamp *inTimeStamp, ! ! ! ! ! ! ! ! ! UInt32 inBusNumber, ! ! ! ! ! ! ! ! ! UInt32 inNumberFrames, ! ! ! ! ! ! ! ! ! AudioBufferList * ioData) { ! CCFWebRadioPlayer *player = (__bridge CCFWebRadioPlayer*) inRefCon; ! ! // read from buffer ! ioData->mBuffers[0].mData = player.preRenderData; ! return noErr; }
  • 84. Demo: AQ Tap + AUNewTimePitch New in iOS 6!
  • 85.
  • 87. Multi-Route • Ordinarily, one input or output is active: earpiece, speaker, headphones, dock- connected device • “Last in wins” • With AV Session “multi-route” category, you can use several at once • WWDC 2012 session 505
  • 88. Utility classes moved again • C++ utilities, including the CARingBuffer • < Xcode 4.3, installed into /Developer • Xcode 4.3-4.4, optional download from developer.apple.com • ≧ Xcode 4.5, sample code project “Core Audio Utility Classes”
  • 89. Takeaways • Core Audio fundamentals never change • New stuff is added as properties, typedefs, enums, etc. • Watch the SDK API diffs document to find the new stuff • Hope you like header files and experimentation
  • 90. Q&A • Slides will be posted to slideshare.net/ invalidname • Code will be linked from there and my blog • Watch CocoaConf PDX glassboard, @invalidname on Twitter/ADN, or [Time code]; blog for announcement • Thanks!

Editor's Notes

  1. \n\n
  2. \n\n
  3. \n\n
  4. \n\n
  5. \n\n
  6. \n\n
  7. \n\n
  8. \n\n
  9. \n\n
  10. \n\n
  11. \n\n
  12. \n\n
  13. \n\n
  14. \n\n
  15. \n\n
  16. \n\n
  17. \n\n
  18. \n\n
  19. \n\n
  20. \n\n
  21. \n\n
  22. \n\n
  23. \n\n
  24. \n\n
  25. \n\n
  26. \n\n
  27. \n\n
  28. \n\n
  29. \n\n
  30. \n\n
  31. \n\n
  32. \n\n
  33. \n\n
  34. \n\n
  35. \n\n
  36. \n\n
  37. \n\n
  38. \n\n
  39. \n\n
  40. \n\n
  41. \n\n
  42. \n\n
  43. \n\n
  44. \n\n
  45. \n\n
  46. \n\n
  47. \n\n
  48. \n\n
  49. \n\n
  50. \n\n
  51. \n\n
  52. \n\n
  53. \n\n
  54. \n\n
  55. \n\n
  56. \n\n
  57. \n\n
  58. \n\n
  59. \n\n
  60. \n\n
  61. \n\n
  62. \n\n
  63. \n\n
  64. \n\n
  65. \n\n
  66. \n\n
  67. \n\n
  68. \n\n
  69. \n\n
  70. \n\n
  71. \n\n
  72. \n\n
  73. \n\n
  74. \n\n
  75. \n\n
  76. \n\n
  77. \n\n
  78. \n\n
  79. \n\n
  80. \n\n
  81. \n\n
  82. \n\n
  83. \n\n
  84. \n\n
  85. \n\n
  86. \n\n
  87. \n\n
  88. \n\n
  89. \n\n