AVAudioPlayerNode crash (no IO cycle)

We're getting the following crash when playing audio through AVAudioPlayerNode (via AudioKit AKPlayer):


Fatal Exception: com.apple.coreaudio.avfaudio
player did not see an IO cycle.

Fatal Exception: com.apple.coreaudio.avfaudio
0  CoreFoundation                 0x1a585c98c __exceptionPreprocess
1  libobjc.A.dylib                0x1a55850a4 objc_exception_throw
2  CoreFoundation                 0x1a575ed1c +[_CFXNotificationTokenRegistration keyCallbacks]
3  AVFAudio                       0x1b21d1e24 AVAE_RaiseException(NSString*, ...)
4  AVFAudio                       0x1b227356c AVAudioPlayerNodeImpl::StartImpl(AVAudioTime*)
5  AVFAudio                       0x1b2270910 -[AVAudioPlayerNode play]
6  OurApp                         0x1049920f0 AKPlayer.play(from:to:at:hostTime:)
7  OurApp                         0x1049af9fc AKPlayer.play(from:to:when:hostTime:)
8  OurApp                         0x1049af840 AKPlayer.play(when:hostTime:)


We're getting these crash logs through Crashlytics.


We play with a 0.2s delay.


I haven't been able to reliably reproduce the crash, nor find any documentation about what the error message means.


We've seen another crash in the same area if the audio engine is not running, but according to our logs, the audio engine is running when this particular crash accurs.


The code is not doing anything fancy, just downloading and playing a small (<1 minute) mp3 audio file. The only complicating factor is that the audio session is also configured for recording (that's the next step after picking and previewing the audio file).


Any suggestions are welcome.

Replies

That error message typically indicates that the audio engine is not running. It may get stopped, after you have started it, if you reconfigure the node graph. It may also be stopped on some route changes, which seems like a possibility if you're changing from playback to play-and-record. Make sure you register for the interruption notification, the route-change notification, and the media-services-were-reset notification, and handle them appropriately.

Thanks! That's very helpful. I'll look into handling those notifications (I believe we handle the interrupt notification, but I'm not sure about the other ones). This probably also explains why we see this error grouped with the engine not running error in Crashlytics.

Are there any other cases where this can occur? We've reduced the number of occurences by handling the different notifications, but we're still seeing a fair amount of crashes with this error. Our logging indicates that the audio engine is running when these crashes occur.

I too have seen the 'player did not see an IO cycle' error. In my case, this error shows up when setVoiceProcessingEnabled is true, and when I am using certain bluetooth devices. If setVoiceProcessingEnabled is changed to false, I do not get the exception.
@mgarabed7 I've long suspected that some bluetooth devices are part of the cause. Do you have any examples of specific devices that cause this for you?

I can easily reproduce this crash when I call AVAudioSession.sharedInstance().setPreferredInput(...) over a wired headset mic.

By calling setPreferredInput over a wired microphone, my AVAudioSession.routeChangeNotification do not notificate when I unplug a wired headset while this headset is both the preferredInput and the active input, as seen in session.currentRoute.inputs.first.

All the notification handlers (OS Media Reset, OS Media Lost, Interruption, etc) do not fire when I unplug a headset in the condition above.

To protect my audio graph, I stop the audio engine and reroute it when I set the headset mic as the preferred input, in a attempt to not be in the mercy of route change notifications in this particular case.

Finally, node.play() crashes when called.

The crash above happens when my engine is active, since I must activate a engine before playing any player node.

This was happening to me as well when I had bluetooth connected to iPhone, but was not preferred device in any way. I received crashes with:

player did not see an IO cycle.

I encountered this because I used setVoiceProcessingEnabled function on input node same as mgarabed7.

This was my solution in case anyone runs into this problem again. I kept bumping into this problem and was rarely connected to bluetooth so it took me a while to figure it out.

In my app I record and playback audio from microphone at the same time, so I use input and output.

// Define this somewhere
let engine = AVAudioEngine()
// ...

// Capture current active device, if you want to specify any other device thats fine too.
let audioSession = AVAudioSession.sharedInstance()
let activeInputDevice = audioSession.currentRoute.inputs.first.map { .fromAVSessionPort(port: $0) }

// -- Generally here you can expect various combinations, such as built-in microphone as input and speaker or even nil as output.
// or perhaps even bluetooth as input and output.

// Enable voice processing to prevent microphone from picking up feedback from speakers.
try engine.inputNode.setVoiceProcessingEnabled(true)

// -- When bluetooth device is connected, active device (first route input) is no longer the same, bluetooth has taken over.
// -- return to original state. No category or mode helped here, tried them all.

// Set previously active device as preferred device. And it no longer crashes.
if let activeInputDevice = activeInputDevice {
  try audioSession.setPreferredInput(activeInputDevice)
}