1/13/2009

AVFoundation and Audio

There are a couple different ways to play audio on the iPhone and iPod touch. There's OpenAL, Audio Queues, SystemSounds and, introduced in firmware version 2.2, AVAudioPlayer from the AVFoundation framework. AVFoundation will eventually have a whole lot more to it, including video playback and the like, but AVAudioPlayer is a good start.

The nice thing about AVAudioPlayer is that it's a piece of cake to use. The downside is that it isn't instantaneous, so you’ll get a slight pause before the sound plays. So, I’ve found that it’s great for playing alerts and things like that, but not for anything that requires immediate playback, like games or a virtual piano.

Loading a sound and playing it is simple. To load a piece of audio, use something like this:

AVAudioPlayer *myExampleSound;

NSString *myExamplePath = [[NSBundle mainBundle] pathForResource:@"myaudiofile" ofType:@"caf"];

myExampleSound =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:myExamplePath] error:NULL];

myExampleSound.delegate = self;

The second and third lines are fairly common for loading files – point to the file, then assign said file to your variable. The third line, which I believe is optional for simple stuff, basically tells the view (I think) to pay attention to the sound so that you can monitor what it’s doing.

Anyway, once the file is loaded, playing (and stopping) it is simple. Those commands look like:

[myExampleSound play];
[myExampleSound stop];

Before you play the sound, you can use:

[myExampleSound prepareToPlay];

to get it ready. AVAudioPlayer will do this for you automatically when you call the play method, but you then get a tiny bit more lag. If it’s a short sample, you’ll probably be fine without it.

Aside from playing and stopping the sound, you can tweak it in a few ways. Here are two examples:

myExampleSound.volume = 0.5;
myExampleSound.numberOfLoops = 2;

Obviously, the first one sets the volume (from 0 to 1.0) and the second can be used to repeat the sound multiple times in a row.

There are a couple more things you can do with AVAudioPlayer, though I haven’t played around with more myself. For instance, it sounds like you can monitor whether or not a sound is currently playing, which can certainly be helpful.

11 comments:

  1. Hi there,

    Thanks a lot! for this post. This has been very handy for me (iPhone noob).

    Do you know how I could create a button to toggle the looping sound on and off? I tried ...
    - (IBAction) toggleAudio:(id)sender{
    if (myExampleSound.volume = 1.0)
    myExampleSound.volume =0.0
    }else{
    myExampleSound.volume = 1.0
    }

    But I don't know where to put it? and I come up with errors like "myExampleSound is undecleared (first use in this function) I think my heads going to explode!?

    Any ideas?

    Thanks again.
    Jonathan

    ReplyDelete
  2. First, make sure your if statement has two equals instead of just one. So, (myExampleSound.volume == 1.0). Otherwise it's just setting it to 1 every time.

    Otherwise, it looks like your action will work to me. Just declare myExampleSound in your header file (.h) and make sure that you have it properly declared with an @property there and an @synthesize in the .m file.

    ReplyDelete
  3. Hi Chris. Nice blog. Where can I find the other commands for AVAudioPlayer? I am having problems getting one music track to stop before another starts. It works great in the simulator but won't play ball on my Touch. If I can get my app to detect if the last piece of music is playing then turn it off before starting the next one, it may solve the problem. I have tried looking at the API but can't find any info on AVAudioPlayer, unless I am doing something wrong. Thanks.

    ReplyDelete
  4. I highly suggest taking a look at AppKiDo (the iPhone one obviously). You can look up APIs and whatnot and see how they work much more easily than the included documents, IMO.

    Anyway, I haven't tried using it before, but it looks like you want to do something along the lines of:

    if (yourSound.playing == true) blahblahblah;

    From what I can tell, the "playing" property will return true if it's playing, and false if not. If you wanted to have your code figure out exactly which sound is playing, then stop that and move onto the next one, you'll want to do something more complicated (like with an array or some such), but that should get you started.

    ReplyDelete
  5. I used the same code you have listed above. The only problem I am having is that the sound wont play at all. When I remove the stop command everything works just fine. Any ideas? Below is my code


    - (IBAction)myAction1:(id)sender {

    NSString *path = [[NSBundle mainBundle] pathForResource:@"boom" ofType:@"wav"];
    AVAudioPlayer* theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
    theAudio.delegate = self;
    [theAudio play];
    [theAudio stop];

    ReplyDelete
  6. The stop command stops the playback of the audio, so removing it and having it works makes perfect sense. In your code, you're telling it to start and then immediately stop.

    In many cases, you probably don't need to use the stop command at all. It's just if you want to cut the sound off early for whatever reason.

    ReplyDelete
  7. is there anyway to show duration of the music played?

    ReplyDelete
  8. is there a way to increase the volume to the ringer volume after the application has been launched?

    ReplyDelete
  9. I want the same thing as ashy.

    How do I get the duration of the sound (say for example if I want to do an action after the sound has played for 15 seconds).

    ReplyDelete
  10. WOW!!! Thanks! YOUR #1 in my book!

    ReplyDelete
  11. hi all,
    got struct in stop... sound that runs in loop is not at all stoping .... pls if any one could mail me the details to adhavan.j@gmail.com....
    thanks in advance

    ReplyDelete