5 February 2010

Audio 101 for iPhone Developers: Playing Audio Programmatically

Screenshot from BasicSounds sample project

Screenshot from BasicSounds sample project

So far in this series we’ve talked about the difference between file and data formats and how to convert and record audio on your Mac. Now we’ll get to the fun part – actually playing audio on your phone!

There are many ways to play audio on the Mac – System Sound Services, AVAudioPlayer, Audio Queue Services, and OpenAL. Without outside support libraries, the two easiest ways by far are System Sound Services and AVAudioPlayer – so let’s talk about when you would (and wouldn’t) want to use those, and how you can use them.

System Sound Services

System Sound Services provides an extremely easy way to play audio files. All you have to do is the following:

NSString *pewPewPath = [[NSBundle mainBundle] 
    pathForResource:@"pew-pew-lei" ofType:@"caf"];
NSURL *pewPewURL = [NSURL fileURLWithPath:pewPewPath];
AudioServicesCreateSystemSoundID((CFURLRef)pewPewURL, &_pewPewSound);
AudioServicesPlaySystemSound(_pewPewSound);

Doesn’t get much easier than that. However there are some strong drawbacks to this method:

  • It only supports audio data formats linear PCM or IMA4.
  • It only supports audio file formats CAF, AIF, or WAV.
  • The sounds must be 30 seconds or less in length.
  • And more – see the iPhone Application Programming Guide, page 149.

AVAudioPlayer

So what if you have an audio file encoded with AAC or MP3 that you want to play as background music? Another easy way to play music is via the AVAudioPlayer class. For the most part, it again is quite simple:

NSError *error;
_backgroundMusicPlayer = [[AVAudioPlayer alloc]
    initWithContentsOfURL:backgroundMusicURL error:&error];
[_backgroundMusicPlayer prepareToPlay];
[_backgroundMusicPlayer play];

However, the drawback of AVAudioPlayer is it is extremely slow. If you tap a button and try to trigger a sound with AVAudioPlayer, there will be a noticeable small delay. But if that doesn’t matter to you (like for starting up background music), AVAudioPlayer is a fine choice.

And there are a couple other things to keep in mind:

  1. If you’re playing background music, you should check to see if other audio (like the iPod) is playing first, so you don’t have two layers of music going on at once!
  2. If a phone call arrives and the user chooses “Decline”, by default your AVAudioPlayer will stop. You can start it back up again by registering for the AVAudioPlayerDelegate and starting the music back up again in the audioPlayerEndInterruption method.

Sample Code

I put together some sample code showing how to use System Sound Services and AVAudioPlayer that you might want to check out. Not only does it demonstrate those APIs, it has some mighty funky beats and a cool spaceship to boot. Pew-pew!

OpenAL

If you’re writing a game or another app where you want fine grained control of audio with low latency, you probably don’t want to use the above methods. Instead, you might want to use OpenAL, a cross-platform audio library supported by the iPhone.

OpenAL can be a beast with a steep learning curve. Luckily, Alex Restrepo has extended a great example by gehacktes.net and made a nice Sound Engine library using OpenAL that you can either use in your projects or use as a reference.

Another option is the Cocos2D game library includes an extremely easy to use sound engine that makes playing audio a snap. You can learn how to use it in my tutorial on How To Make a Simple iPhone Game With Cocos2D.

And That’s a Wrap!

That’s about all I’m going to cover about audio topics in iPhone programming for now – but keep in mind I’ve barely scratched the surface. If you’re interested in more, I’d recommend reading Apple’s docs, especially the Core Audio Overview and Audio Session Programming Guide, and possibly digging into OpenAL a bit more.

I hope this series has been useful to other developers who may be new to audio concepts. Feel free to share any additional info you’re aware regarding audio programming that may be useful to others!

Category: iPhone

Tags: , ,

25 Comments

  1. awmyhr (1 comments) says:

    This is some timely information, as I just got to the point in my app where I’m needing this. Want to download a single MP3 file, then play it in the background (w/pause control). Hopefully the next version of the OS will allow us to hand this off to the system so it keeps playing when the app is exited…

  2. Mike (8 comments) says:

    But what about looping audio in the background? Or using cross-fades between sections of BGM?

  3. Ray Wenderlich (492 comments) says:

    AVAudioPlayer can loop music in the background, just call something like this:

    [_backgroundMusicPlayer setNumberOfLoops:-1]; // Negative number means loop forever

    As for cross-fades, that sounds like a case for using OpenAL. You can either use it directly, or look into one of the OpenAL-based sound engine libraries referenced above.

  4. Mohit (1 comments) says:

    How to play the file using AVAudioPlayer which is on the web server

  5. Joe (6 comments) says:

    Thanks, this was great to help me understand some concepts.

  6. Pete (9 comments) says:

    I’m experiencing a lag in the very first sound load (using SimpleAudioEngine cocos2d method for loading an effect). I’ve experimented with LEI16 and also IM4A data formats and both give the same issue. Strange thing is it only occurs when I initiate the first call to playEffect:, subsequent calls play fine, even when I replace a new game scene. However, when I exit and restart my app, the same loading lag appears. Both the sound and rendering lags (skips maybe 60 frames) and then everything resumes.
    Is there some initializing method for SimpleAudioEngine I should be doing first?

  7. Pete (9 comments) says:

    >> Follow up on last post
    I can actually flush out that lag by making call with playBackgroundMusic (as this becomes the very first load). Since it is background music, and it starts on a MenuScreen, the lag goes unnoticed.
    However, there will be times when, I won’t need background music on start up… so the question still stands.

  8. Ray Wenderlich (492 comments) says:

    @Pete: There are some calls you can use to preload sound effects/music:

    [audioEngine preloadBackgroundMusic:@"xxx"];
    [audioEngine preloadEffect:@"xxx"];
    

    Hope this helps!

  9. Pete (9 comments) says:

    Thanks Ray, that solved it. So as observed it seems the SimpleAudioEngine needs a bit of a rev to get it working right.

  10. Seth (1 comments) says:

    I love your tutorials Ray. Thanks soooooo much for posting them. Any chance you’ll be posting a tutorial on recording audio on the iPhone? (AVAudioRecorder??)

  11. Ray Wenderlich (492 comments) says:

    @Seth: Thanks! I haven’t had the need to record audio in any of my apps yet, but if I ever do that sounds like a good idea for a tutorial!

  12. Forrest (5 comments) says:

    Is there any most simplest way to call just one system API, then make some sound ?

    The funny thing is that I tried implementation in TTPhotoViewController, when some sample used it, it can not make any sound.

    But for another cases, implemented in my sample, it make sound.

    Strange thing !

  13. Ray Wenderlich (492 comments) says:

    @Forrest: The simplest way is the System Sound Services, if the drawbacks of that method are acceptable for your app.

    If you’re seing weirdness with playing sounds, be sure to try it on an actual device because I’ve seen some strange behavior in the simulator in the past.

  14. Ashwin Jumani (1 comments) says:

    Is there any way to play 2 background sound simultaneously using SimpleAudioEngine? I need to play 2 background sounds in my game. Please help me.

    Thanks in advance.

  15. Tom (2 comments) says:

    Hey ray thanks a lot for this awesome tutorial!I have a question though, im creating a drum app and when a user taps the drum it plays a sound. I Want to add a record feature that records the drums he played and then he can replay it through a play button. How do i go about this, can u link some code?I have been searching the whole web for an answer and i will thank you a million times if you could reply to me :D!

  16. Ray Wenderlich (492 comments) says:

    @Ashwin: Yes you can use different “channels” for each sound effect in Cocos2D’s CocosDenshion library so they play simultaneously. See the DrumPad example that comes with Cocos2D for an example.

    @Tom: Well, one way you could implement this is by keeping track of the time delta between the start of the song and when the drum was tapped each time the user taps a drum. Then when you want to replay it, just loop through your list and play each effect at the appropriate time. Don’t have any code examples of this though!

  17. Satheesan.op (4 comments) says:

    Thanks Ray..your tutorials are really helpful… Suppose i load videos to an iPhone using iTunes. I want to write an app that will allow the selection of videos, and the playing of them.
    I am familiar with the MPMoviePlayerController class. but i only played video that i loaded onside my app..
    is it possible to access i-phone videos (var/mobile/library/downloads.)programmatically…?
    Thanks again

  18. Fly (1 comments) says:

    How to add backgroundmusic in a game

  19. Tom (2 comments) says:

    Thank you for your reply. So far i have been trying to make an NSMutableArray and work with NSDate to keep track of when the user taps the drum. I am failing miserably at it. Also this is a topic requested by many and there is not a single post on this topic on the internet. I found a sample code written for the mac osx http://pastie.org/376474 . I haven’t managed to transcribe it to the iphone yet. Anyways have a look at it. It might be worth making a tutorial. You would be the first one to cover this topic. And im sure many would praise you for this tutorial including me. Btw im sorry if tutorial requests are rude. I know that you are doing all of this for free and its really your choice. So thanks a lot anyways.

  20. Ray Wenderlich (492 comments) says:

    @Santheesan: You can actually use the UIImagePickerController to allow the user to browse for movies as well, check this out for more info:

    http://www.drdobbs.com/article/printableArticle.jhtml;jsessionid=LFMF4GVO3BQUHQE1GHPSKHWATMY32JVN?articleId=225800144&dept_url=/mobility/

    @Fly: Check out the AVAudioPlayer section in this tutorial, or if you’re using Cocos2D you can use the SimpleAudioEngine to play background music.

    @Tom: Thanks for the tutorial idea, added to the idea list!

  21. Ramkumar (6 comments) says:

    Ray i have seen all your tutorial. It was amazing..Now i am struck up in a problem… can we edit audio and video, synchronize it and store it in photo library exactly like IMovie app …. is there any tutorial for it..please help me

  22. Ray Wenderlich (492 comments) says:

    @Ramkumar: I haven’t looked into this at all, so am not sure!

  23. Ramkumar (6 comments) says:

    @Ray: ok Ray thanks and another question can we play mp3 from resource path in background IOS 4.0. I am playing it but problem is once it enters background NSTimer stops updating progress bar so the next song is not played wat can i do ….how can i activate NSTimer in background..

  24. Ray Wenderlich (492 comments) says:

    @Ramkumar: You want to use the new background audio support available in iOS4. Check out this FAQ:

    http://developer.apple.com/iphone/library/qa/qa2010/qa1668.html

  25. Ramkumar (6 comments) says:

    Thanks Ray i have Done It

Leave a Comment

I'd love to hear your thoughts!

Tip: Want a cool picture next to your comment? Create a Gravatar!