Due to the fact that the iPhone and iTouch are all about playing music, one would think that playing audio in iOS would be an easy task. Well, I hate to break it to you, but playing sound effects in iOS is not as easy as it might seem. There are several ways to play audio in iOS, and in this tutorial, I will show you 2 of them.
System Sound Services
This is the easiest way to play sounds on iOS devices, but it has some limitations. System Sound Services provide a C API for playing short sounds in iOS. You can only play one sound at a time, the maximum duration of the sound is 30 seconds, and the API does not provide level, positioning, timing control, or looping.
Furthermore, the sound is played in a different thread and you can’t know exactly when it will be played. It can only be used to play .aif, .wav and .caf files.
You need to add the AudioToolbox framework to your project to use System Sound Services and import it wherever you want to use it.
#import <AudioToolbox/AudioToolbox.h>
Now you need a SystemSoundID variable, and get the URL of the file you wish to play. Once you have done these two things, you need to call AudioServicesCreateSystemSoundID passing those two arguments. This function is written in C, and you need to send the memory address of the SystemSoundID variable you defined, so use the ampersand character. This way, the function does not need to return the value, since it is stored directly in the SSID variable.
Finally, play the sound by calling AudioServicesPlaySystemSound:
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"file" withExtension:@"wav"]; SystemSoundID sound = 0; AudioServicesCreateSystemSoundID((CFURLRef) fileURL,&sound); AudioServicesPlaySystemSound(sound);
Playing sounds with System Sound Services is quite easy, but as I said, it has some limitations. If you only need it to play short sounds, then use this one.
AV Foundation
This framework provides an Objective-C API to work with audiovisual data. With AVAudioPlayer, you can reproduce any audio file. You can control the volume, the number of loops, play multiple sounds simultaneously… A very useful feature is that you can set a delegate for an AVAudioPlayer to handle interruptions or to update the UI once the sound has finished playing.
In this tutorial we will make a very simple MP3 player. This player will have a UISlider to control the volume, and a button to play the sound or to pause it.
Step 1: Creating the project
First of all, create a new Single-View Application project, and you will need to add two frameworks to your project: AudioToolbox and AVFoundation.
Step 2: Setting up the UI
In the storyboard, you will need to add to the view controller a UISlider and a UIButton. Add also a UILabel above the slider to make it look a little bit better.
Step 3: PlayerViewController header file
You are going to need one instance variable for the AVAudioPlayer in your view controller, and two methods to control when the user presses the button and when he changes the value of the slider. Also, import the frameworks you added in step 1.
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface PlayerViewController : UIViewController
{
AVAudioPlayer *player;
}
- (IBAction)playFile:(id)sender;
- (IBAction)changeVolume:(id)sender;
@end
This view controller will be the delegate for the AVAudioPlayer, so it needs to adopt the AVAudioPlayerDelegate protocol. You don’t really need to set a delegate in this tutorial, since we will not use it, but I am doing it to show you how to do it. One of the most useful methods is
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
// Do whatever you need to do
}
since it will be called once the sound has finished playing, so that you can update the UI or play a different sound.
Step 4: Connecting the code to the UI
Again, go to the storyboard to connect the two methods you have just created to the UISlider and the UIButton. To do this, Control-drag the UISlider onto Player View Controller and select changeVolume. Repeat the same operation with the UIButton, but choose playFile instead.
Step 5: Configuring the Audio Session
In the viewDidLoad method, after the call to [super viewDidLoad], the first thing you need to do is to configure the Audio Session. First, set PlayerViewController as its delegate, and set the category to AVAudioSessionCategoryPlayback. You can see a list with all the possible categories here.
Sometimes the sound will come out of the ear speaker, so you also need to add two lines of code to redirect the sound to the loudspeaker.
[[AVAudioSession sharedInstance] setDelegate: self];
[[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil];
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
Step 6: Creating the AVAudioPlayer instance
Once the Audio Session is configured, you need to get the URL of the file you wish to play. Then, create the AVAudioPlayer and set its initial attributes. Since the UISlider we created has a default value of 0.5, we will set the player’s volume to 0.5 as well.
The numberOfLoops property controls how many times the file is played. A value of 0 will make the sound play once, and if you set it to a negative value it will loop indefinitely.
NSString *fileName = [NSString stringWithFormat:@"monalisa"];
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"mp3"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
player.volume = 0.5;
player.numberOfLoops = 0;
player.delegate = self;
Step 7: Final Touch
The last thing you need to do is to implement the two methods you declared in the header file. Its implementation is very simple and you should have no problem understanding it:
- (IBAction)playFile:(id)sender
{
UIButton *button = (UIButton *)sender;
if (([button.titleLabel.text isEqualToString:@"Play"]) {
[button setTitle:@"Pause" forState:UIControlStateNormal];
[player play];
} else if ([button.titleLabel.text isEqualToString:@"Pause"]) {
[button setTitle:@"Play" forState:UIControlStateNormal];
[player pause];
}
}
- (IBAction)changeVolume:(id)sender
{
UISlider *slider = (UISlider *)sender;
[player setVolume:slider.value];
}
IMPORTANT NOTE
The first time I used AVAudioPlayer and tested it with my device instead of the simulator, no sound was emitted by my iPhone. After lots of research, I couldn’t find any answers, until I discovered that the volume of the file being played depends on the volume you have on the Music app of the iPhone. Since I always set its volume to zero when I disconnect my headphones, I couldn’t hear a sound and did not know the reason.
Hence, check that the volume is not set to zero in the Music app before testing your app. If you don’t want to change the volume from the Music app, you can always change the volume programmatically. Add the MediaPlayer framework to your project, and import it in AppDelegate.h:
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
@interface AppDelegate : UIResponder
@property (strong, nonatomic) UIWindow *window;
@end
Then, go to the implementation file and write this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
MPMusicPlayerController *musicPlayer = [MPMusicPlayerController applicationMusicPlayer];
[musicPlayer setVolume:1.0f];
return YES;
}
Now your app will set the Music app’s volume to 1.0 every time you launch your app!
Conclusion
This tutorial has been quite a long one, but playing sounds in iOS can be really cumbersome, so I wanted to explain everything to the last detail. I hope you enjoyed it, and thank you for reading it!