Sunday, June 28, 2009

-[AVAudioRecorder record] returns NO

The AVAudioPlayer class has been around for a while, but the AVAudioRecorder is new in the iPhone 3.0 SDK. Being so new, there is little information out there, which made it harder for us to debug this problem.

In our application, we use AVAudioRecorder for recording and OpenAL for playback. From some debugging and logging, it appeared that AVAudioRecorder would automatically switch the audio session category whenever it would start and stop recording. This matches what the Audio Session Programming Guide says. However, something about our OpenAL playback was breaking the AVAudioRecorder. We could record as many times as we wanted, but after we would play back once, recording wouldn't work. In particular, -[AVAudioRecorder record] was returning NO, which indicates that something went wrong. Unfortunately, the documentation doesn't (yet) explain what might cause that to be a problem.

It turns out that AVAudioRecorder does automatically switch your audio session category, but only when it feels like it. If you've already explicitly set the category, AVAudioRecorder will assume that you know what you're doing, and will not auto-switch categories anymore. The solution was to use AudioSessionSetProperty() to set the audio category to either kAudioSessionCategory_RecordAudio or kAudioSessionCategory_PlayAndRecord before starting the recording.

If you're only using AVAudioPlayer, AVAudioRecorder, and system sound services for audio playback, you shouldn't need to explicitly set a category. The only reason we did was because we were using OpenAL.

Now to be a little critical: I'm not a fan of this behavior. In order to make AVAudioRecorder "easy to use", Apple had to make its behavior complicated. It needs to know whether the user has explicitly set the audio session category. It sometimes implicitly changes the category. When it changes the category, the change is not observable (by callbacks registered with AudioSessionAddPropertyListener). All of these things are frustrating, but the real aggravation is that there is no way for us to implement a class like AVAudioRecorder. Because the class seems to make use of private APIs, its behavior cannot be duplicated. As far as I can see, there is no good reason for it to be using private APIs. Maybe it was just laziness. Whatever the case, it's disappointing.

On the other hand, things aren't all bad. AVAudioRecorder replaced about 4 source files in our application. That alone makes it worth bearing the annoyances. I just wish it were more... sane.

2 comments:

  1. Thanks for this. I just had the EXACT same problem.

    ReplyDelete
  2. Thank you!! I've wasted a huge amount of time with AVAudioRecorder + AVPlayer before finding your post.

    ReplyDelete