NEW! Master Swift design patterns with my latest book! >>

How to control the pitch and speed of audio using AVAudioEngine

Paul Hudson       @twostraws

Although it’s easy enough to play sound effects and music using AVKit, it’s actually one of the most powerful frameworks in iOS and can easily add some fun and interesting effects to your apps and games.

For example, one of the powerful classes in AVKit is called AVEngine. Its job is to connect audio processing objects in a chain so that the output of one object is the input for another. You can feed audio into the start, apply processing in the middle, then play the audio as the output, giving you real-time audio manipulation without much effort.

To try this out, we’ll create a simple test that loads an MP3 file and starts playing it, but adjusts the playback speed and pitch of the audio every time the user taps the screen.

First you need a property to store your AVAudioEngine object, along with properties that store an AVAudioUnitTimePitch and an AVAudioUnitVarispeed – the processors that transform the speed and pitch of audio:

let engine = AVAudioEngine()
let speedControl = AVAudioUnitVarispeed()    
let pitchControl = AVAudioUnitTimePitch()

Next you need a method that will play a URL. This takes six steps:

  1. Create an AVAudioFile that reads from whatever file URL gets passed into the method.
  2. Create an AVAudioPlayerNode that will read in your AVAudioFile. This is a like a more advanced AVAudioPlayer, and we can use it as part of our engine connections.
  3. Connect the audio player, the pitch control, and the speed control to our playback engine.
  4. Arrange the parts so that the audio player feeds into the speed control, the speed control feeds into the pitch control, and the pitch control feeds to the main mixer output – gets played aloud.
  5. Prepare the audio player node to start reading its file.
  6. Start the engine and the player.

Here’s the code for that, with comments matching the numbers above:

func play(_ url: URL) throws {
    // 1: load the file
    let file = try AVAudioFile(forReading: url)

    // 2: create the audio player
    let audioPlayer = AVAudioPlayerNode()

    // 3: connect the components to our playback engine
    engine.attach(audioPlayer)
    engine.attach(pitchControl)
    engine.attach(speedControl)

    // 4: arrange the parts so that output from one is input to another
    engine.connect(audioPlayer, to: speedControl, format: nil)
    engine.connect(speedControl, to: pitchControl, format: nil)
    engine.connect(pitchControl, to: engine.mainMixerNode, format: nil)

    // 5: prepare the player to play its file from the beginning
    audioPlayer.scheduleFile(file, at: nil)

    // 6: start the engine and player
    try engine.start()
    audioPlayer.play()
}

Now you can call that method using a path to any audio file in your app bundle.

As for changing the pitch and rate, we made pitchControl and speedControl properties so we can adjust them at will. For example:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    pitchControl.pitch += 50
    speedControl.rate += 0.1
}

As you’ll see, this processing happens incredibly quickly – it’s all realtime, so you can create fun effects for apps and games in just a few minutes of work!

Available from iOS 8.0

Did this solution work for you? Please pass it on!

Other people are reading…

About the Swift Knowledge Base

This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.

Learn Swift faster!

Take your Swift learning to the next level: buy the Hacking with Swift e-book and get bonus material to help you learn faster!

MASTER SWIFT NOW
Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Practical iOS 11 Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Click here to visit the Hacking with Swift store >>