Playing Audio Files using the iPhone SDK

Playing sound is a big part of any rich media application and it's very likely you'll need to do it in your future iPhone apps. Before version 2.2 of the SDK this was somewhat difficult -- you needed to either roll your own player or use AudioQueueServices. To use the latter you have to create a bunch of C-style structures and callbacks to feed the data manually byte by byte. It's a very programming intensive process while all you want to do is just load and play a sound!

To remedy this problem Apple introduced a new Framework: AVFoundation and a new audio player: AVAudioPlayer. It has the simplicity you desire but the features you need in order to play your audio files: play, stop, seek, and pause. You can also choose to register a delegate to get certain events like errors in playback, notification when playback ends, and interruption events for when phone calls occur. Pretty cool stuff.

I've written up an example application which is checked into SVN at http://code.9mmedia.com/svn/public/iphone/audio-example/ but here are the key points:

//Make sure you're building for 2.2 and you are also including AVFoundation.framework
 
#import <AVFoundation/AVFoundation.h>
AVAudioPlayer* player;
...
/* Both these actions are hooked up to buttons in IB */
- (IBAction)startPlayback:(UIButton *)sender {
    if(!player){
        /*
         * Here we grab our path to our resource
         */
        NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
        resourcePath = [resourcePath stringByAppendingString:@"/grabbag.m4a"];
        NSLog(@"Path to play: %@", resourcePath);
        NSError* err;
 
        //Initialize our player pointing to the path to our resource
        player = [[AVAudioPlayer alloc] initWithContentsOfURL:
                            [NSURL fileURLWithPath:resourcePath] error:&err];
 
        if( err ){
            //bail!
            NSLog(@"Failed with reason: %@", [err localizedDescription]);
        }
        else{
            //set our delegate and begin playback
            player.delegate = self;
            [player play];
        }
    }
}
- (IBAction)pausePlayback:(UIButton*)sender {
    NSLog(@"Player paused at time: %f", player.currentTime);
    [player pause];
}

That's all you need to do to play a sound... create a file URL, init, and play! Pausing is done by a simple method call, and you can monitor the location in the song by checking the currentTime property of the object.

Make sure to check out the project to see the delegate methods implemented, and the implementation of the code above! Also make sure to check the iPhone documentation for these classes as well:
  • AVAudioPlayer Documentation (apple.com)
  • AVAudioPlayerDelegate Documentation (apple.com)

24 Comments

24 Responses to “Playing Audio Files using the iPhone SDK”


  1. 1 John

    Hi Jeff,

    Thank you for putting this up – perfect timing for me!

    Question: I am having problems downloading the project to see the delegate methods.

    I click on the link which brings me to a page with what appears to be the program, but when I control click to download, I only get a 4kb HTML file.

    Thanks

  2. 2 John

    Sorry My bad

    Any chance of seeing the controller files

  3. 3 John

    Implemented what I could- got the following error:

    Projects/audiotest/Classes/audiotestViewController.m:21: fatal error: method definition not in @implementation context

    - (IBAction)startPlayback:(UIButton *)sender {

    Thanks

  4. 4 jeff

    To get the whole project, try running this command:

    svn co http://svn.9mmedia.com/public/iphone/audio-example/

    Username/Password is left blank (as in nothing)… you should be able to see the entire project that way. You can also check the whole ApplicationDelegate class by looking here if you want to copy/paste code:

    http://svn.9mmedia.com/public/iphone/audio-example/AudioPlayerExample/Classes/AudioPlayerExampleAppDelegate.m

    Finally, that error is most likely caused by not wrapping up your methods in an object @implementation context. For example say you have this header:

    header:

    @interface ANewObject : NSObject{
    UIWindow *window;
    }
    - (IBAction)anEvent:(UIButton *)sender;

    @property (nonatomic, retain) IBOutlet UIWindow *window;
    @end

    you will want your .m to look like this:

    #import “ANewObject.h”
    @implementation ANewObject
    @synthesize window
    - (IBAction) anEvent:(UIButton *)sender{
    //code here
    }
    @end

    Hope this helps.

    Jeff

  5. 5 John

    yes Thank You – I had left out the @implementation (obviously new to this)

    One more – File runs great – hit start button and my mp3 plays. Once it’s done, however, the button won’t cause playback to begin again. Should it play again?

    Thanks!

  6. 6 jeff

    John,

    Most likely if you are accessing the same object you would have to return the player to beginning by doing something like this:

    playerInstance.currentTime = 0;
    if( !playerInstance.isPlaying ) [playerInstance play];

    Otherwise, you can reallocate an instance of AVAudioPlayer with the same URL and just start playing it again. Make sure you release the old one though!

    Jeff

  7. 7 John M

    Very nice example! Is there a simple means to loop a sound?

    Thanks again, very well done.

  8. 8 chicken

    John M: set your AVAudioPlayer object to obj.numberOfLoops = -1;

  9. 9 Rashi

    HI,
    M also doin the same stuff…. but pause in my code is not working..
    not able to figure out y..????
    can u put some light onto it…

  10. 10 anonymous

    This doesn’t work on 2.2.1. I downloaded your code and it ran in the simulator in 2.2 but not in 2.2.1. Another problem is that the sound doesn’t play at all, even in 2.2. When I try it in 2.2.1 it crashes on start up and it gives me the warning:passing argument 2 of “initWithContentsOfURL:error:” from distinct Objective-C type. I don’t know what this means but it may be why it is crashing. Also I tried to implement this into my own code and it had problems linking the audio player framework with the code.

    thanks!

  11. 11 jeff

    anonymous — I just tried building for 2.2.1 (Debug/Simulator and Debug/Phone) and it worked perfectly for both. Are you using the code snippet in your own code? It seems like you might be missing a Framework include: you have to include AVFoundation.framework whenever you use AVAudioPlayer.

    Please let me know if you are still having problems!

    Jeff

  12. 12 anonymous

    jeff – My problem is that the simulator won’t play the file for some reason. I’m almost positive its not my code. I’m fairly confident that if I tried it on a device that it would play. I’ve been looking on the internet and it seems like the simulator has problems playing sounds. The code compiles fine but it freezes up when it gets to the play method call. Its a little odd but unless someone has any ideas on how to fix this, I guess I’ll just have to wait until I can test it on a device.
    Anyone with any ideas please help. Thanks!

  13. 13 jeff

    anonymous — do you have any third party quicktime components? Move everything that doesn’t say “Apple” on it out of /Library/Quicktime and $HOME/Library/Quicktime to a temporary location, logout and log back in and try running the sample again.

    A coworker has had some problems with the DIVX Player QT component locking up their simulator and that seems to be similar to your problem.

    Let me know if you get any results!

    Jeff

  14. 14 anonymous

    jeff – I checked there and there weren’t any non-apple components there even though I do have DivX installed but I think its just the player and not the Quicktime component because its definitely not there. If you know of anywhere else this might be installed that might be affecting it please tell me. Thanks again.

  15. 15 k00k

    Jeff,

    Thanks for this. I built and tested successfully on 2.2.1. I did receive one warning though:

    warning: passing argument 2 of ‘initWithContentsOfURL:error:’ from distinct Objective-C type

    This occurs at around line 39 of AudioPlayerExampleAppDelegate.m which is:

    //Initialize our player pointing to the path to our resource
    player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:resourcePath] error:&err];

  16. 16 jeff

    k00k -

    The warning is due to the passing of an error pointer as the second argument. Funny thing is, the declaration of NSError* err, and passing the pointer of the pointer results in that error despite this being common practice in ObjC programming results in a warning when using that method.

    Here’s the declaration in AVAudioPlayer.h:
    /* all data must be in the form of an audio file understood by CoreAudio */
    - (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError;

    In other words, you can safely ignore that warning.

    Jeff

  17. 17 MK

    Hi,

    I can’t seem to find the .xcodeproj project file?

  18. 18 jeff

    MK, the project file is located on SVN here:

    http://svn.9mmedia.com/public/iphone/audio-example/AudioPlayerExample/

    You should be able to run ’svn co http://svn.9mmedia.com/public/iphone/audio-example/AudioPlayerExample/

    username/password is blank (as in nothing). Let me know if you still have problems!

    Jeff

  19. 19 TC

    Hi jeff,

    I am unable to log in to the svn links above when leaving both the username and password fields empty. I’m using Safari – does that matter?

  20. 20 Mark

    How do you just use one button to toggle between play/stop?
    I have just tried an if in the IBACtion button to see if (myAudio isPlaying =! YES) then Play, Else myAudio stop.
    Is there a way to do this just off one button?
    Thank,s
    Mark

  21. 21 Umaid

    I have implemented my sound stop and play through other method, it is working fine, on same view, but when I navigate to different view and return to same view to stop the sound so it won’t stop and continuosly playing. I am enclosing my code as below. Please help me where I am lacking.

    -(IBAction)Play:(UIButton *)sender
    {

    NSUserDefaults *def = [NSUserDefaults standardUserDefaults];

    // NSString *myLastPlay = [def objectForKey:@"myLastPlay"];
    if([lastindexbtn tag] == [sender tag])
    {
    if([self.playstop isEqualToString:@"play"])
    {
    //Stop

    [sender setImage:[UIImage imageNamed:@"playbtn.png"] forState:UIControlStateNormal];

    NSLog(@”Player stop at time %f”, myExampleSound.currentTime);
    [myExampleSound pause];
    //[myExampleSound stop];
    self.playstop = @”stop”;
    }
    else if([self.playstop isEqualToString:@"stop"])
    {
    //play
    //[myExampleSound stop];
    [sender setImage:[UIImage imageNamed:@"stop.png"] forState:UIControlStateNormal];

    NSString *playme = [NSString stringWithFormat:@"%d",[sender tag]];
    //this variable can be named differently

    NSString *myExamplePath = [[NSBundle mainBundle] pathForResource:playme ofType:@”mp3″]; // *Music filename* is the name of the file that you want to play. BE SURE that you type the correct characters as the system is case-sensitive. It caused a crash for me… Very painful.

    myExampleSound =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:myExamplePath] error:nil];
    // NSLog(@”%f”,myExampleSound.duration);
    myExampleSound.delegate = self;

    //[myExampleSound prepareToPlay];
    [myExampleSound play];
    self.playstop = @”play”;

    [def setObject:[NSString stringWithFormat:@"%d",[sender tag]-1] forKey:@”myLastPlay”];
    }
    }
    else
    {
    //[myExampleSound stop];
    [lastindexbtn setImage:[UIImage imageNamed:@"playbtn.png"] forState:UIControlStateNormal];
    [sender setImage:[UIImage imageNamed:@"stop.png"] forState:UIControlStateNormal];

    NSString *playme = [NSString stringWithFormat:@"%d",[sender tag]];
    //this variable can be named differently

    NSString *myExamplePath = [[NSBundle mainBundle] pathForResource:playme ofType:@”mp3″]; // *Music filename* is the name of the file that you want to play. BE SURE that you type the correct characters as the system is case-sensitive. It caused a crash for me… Very painful.

    myExampleSound =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:myExamplePath] error:nil];
    // NSLog(@”%f”,myExampleSound.duration);
    myExampleSound.delegate = self;

    //[myExampleSound prepareToPlay];

    [myExampleSound play];
    self.playstop = @”play”;
    [def setObject:[NSString stringWithFormat:@"%d",[sender tag]-1] forKey:@”myLastPlay”];
    }
    //[myExampleSound stop];

    // myExampleSound.numberOfLoops = 10; /* can be as many times as needed by the application – myExampleSound.numberOfLoops = -1; >> this will allow the file to play an infinite number of times */

    lastindexbtn = sender;
    //[def setObject:lastindexbtn forKey:@"LastObj"];

    }

  22. 22 Ankush

    Hi,This is fer playing the video thats already in the iphone. but please help me out if i wan to play it from any link.i.e from server.

  1. 1 16 Resources for developing apps for the iPhone | blarnee.com
  2. 2 DevTutorial #19 – AVPlayer: come riprodurre semplicemente file audio! - Bubi Devs

Leave a Reply