While investigating some performance issues in my game app I found that AVAudioPlayerNode.play() is taking a long time to complete, sometimes close to 20 milliseconds (more than the time allotted for a single frame at 60 FPS).
Currently I'm disconnecting and reconnecting player nodes with each play event to ensure they're connected with the proper format for the sound being played. Below is some example code (it uses Xcode's iOS game template and replaces the code in GameViewController.swift):
import AVFoundation
import GLKit
import OpenGLES
import QuartzCore
final class GameViewController: GLKViewController {
private let engine = AVAudioEngine()
private let player = AVAudioPlayerNode()
private var context: EAGLContext?
private var buffer: AVAudioPCMBuffer?
override func viewDidLoad() {
// General set up.
super.viewDidLoad()
context = EAGLContext(api: .openGLES2)
let view = self.view as! GLKView
view.context = context!
// Load audio buffer.
let path = Bundle.main.path(forResource: "test.wav", ofType: nil)!
let url = URL(fileURLWithPath: path)
do {
let file = try AVAudioFile(forReading: url)
buffer = AVAudioPCMBuffer(
pcmFormat: file.processingFormat,
frameCapacity: AVAudioFrameCount(file.length)
)
try file.read(into: buffer!)
print(buffer!.format)
} catch {
}
// Set up and start audio engine.
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: buffer!.format)
do {
try engine.start()
} catch {
}
}
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Play sound and time call to play().
engine.disconnectNodeOutput(player)
engine.connect(player, to: engine.mainMixerNode, format: buffer!.format)
let startTime = CACurrentMediaTime()
player.play()
print(CACurrentMediaTime() - startTime)
player.scheduleBuffer(buffer!)
}
}
I'm testing this on an iPad Mini running iOS 9.3.5. The format of the test audio file is mono, 44.1, Float32.
I can think of three possibilities here:
- I'm doing something wrong that's causing play() to execute slowly.
- play() isn't meant to be used in a real-time context, but rather only during setup.
- There's some issue with the AV audio framework that's causing the behavior.
Can anyone help clarify this? Are these long execution times (e.g. ~20 ms) expected behavior for the play() function?