Playing system sounds Playing songs Using the Media Player to interact with a device’s multimedia Playing video Up until the release of the iPhone OS 3.0, the iPhone was a difficult platform for developing multimedia applications. The capabilities were there, as you could always resort to using low-level C APIs to program audio and video, but the higher-level APIs were strictly undocumented and off limits. And as for the multimedia on a device that was placed there by iTunes? Forget it, off limits. Any media you wished playing in your application had to either be packaged as part of your application or streamed from a server. That restriction changed with iPhone OS 3.0; now you can access and play a user’s audio iTunes multimedia, making the iPhone and iPod touch the most programmable portable music players ever released.In this chapter, you explore the basic multimedia capabilities of the iPhone and iPod touch. You first learn how to play system sounds and longer sounds. You then move to the Media Player framework, where you use the framework to select and play a user’s iTunes audio multimedia. After learning to play iTunes media, you then learn how to play a video using the Media Player framework’s video player.
Playing short sounds on an iPhone or iPod touch is easy. Simply load the song as a system sound, obtain the sound’s id, and use the AudioServicesPlaySystemSound method to play the sound. Playing a longer sound using the AVAudioPlayer is not difficult, but a little more involved. However, there is one important limitation you must realize when using sound on your device using the AudioServicesPlaySystemSound function or AVAudioPlayer: Any media you play must be packaged as part of your application or must be streamed from a server. So, although these two classes are good for adding sound to your application or for developing an interface to a server that streams multimedia, they are not good classes for developing a music player. Instead, you should use the Media Player Framework, covered later in this chapter.
The AudioServicesPlaySystemSound function plays a short system sound. Although security restrictions prevent your application from playing a device’s OS system sounds, you can load and play your own short (30 seconds or less) sounds and play them using this function. The AudioServicesPlaySystemSound function can only play a sound with the following format: .caf, if, or .wav. The sound plays at whatever audio level the device is set to, and the sound plays immediately upon its id being passed to the There is no pausing, rewinding, fast-forwarding, or other sound manipulation functionality. You load a sound, and the function plays it. void AudioServicesPlaySystemSound (SystemSoundID inSystemSoundID); The function takes a SystemSoundID as a parameter. A SystemSoundID is an unsigned integer that uniquely identifies the sound. You obtain a SystemSoundID by loading a sound into the AudioServicesCreateSystemSoundID function. OSStatus AudioServicesCreateSystemSoundID (CFURLRef inFileURL, SystemSoundID * outSystemSoundID); The AudioServicesCreateSystemSoundID function takes a reference to the file’s URL and the SystemSoundID to assign the value to. A CFURLRef is simply a lower-level pointer to a URL. You can ignore creating a CFURL (what the CFURLRef points to) and instead cast an NSURL as a CFURLRef. After obtaining a sound’s URL, you pass it to the create system sound function. It assigns the value to the system sound ID variable you defined; you pass that ID to the system sound player function; and it plays the sound.
Q: What’s a CFURLRef? What’s an NSURL? A: A CFURLRef is a reference to a CFURL object. A CFURL is part of the Core Foundation framework, meaning it is C, not Objective-C, and provides functions to create and parse URLs. An NSURL is a higher-level, Cocoa Objective-C class for working with URLs. It encapsulates a URL and provides many functions for manipulating URLs. Refer to the NSURL Class Reference for more information. You can cast an NSURL * as a CFURLRef because of Apple’s ―toll-free bridging‖ functionality. The term ―toll-free bridging‖ refers to certain Core Foundation types being interchangeable with their higher-level Cocoa counterparts. Remember, a pointer to an NSURL is equivalent to a CFURL reference. TIP You can use the AudioServicesPlaySystemSound to vibrate a user’s iPhone. Pass the kSystemSoundID_Vibrate identifier constant to the function. Currently, only the iPhone can vibrate; this code does not do anything on an iPod touch. AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
The AVAudioPlayer plays sounds. The audio player does not have the limitations of the AudioSer vicesPlaySystemSound function. It can play any length sound, loop a sound, play multiple sounds at the same time, and allows control over a sound’s volume. Methods you might use include prepareToPlay, play, pause, and stop. Each method’s functionality should be intuitive. Notice that prepareToPlay and play return a BOOL, so you can evaluate if the call was successful.
You can initialize an AVAudioPlayer with data or a URL. The initWithData:error: function initializes an audio player using data encapsulated in an NSData object. The initWithContentsOfURL:error: initializes an audio player using the sound file referenced by the URL. That sound file can be in your application’s bundle, or it can be a resource on a server and streamed. If streamed, note that the prepareToPlay method discussed previously takes more importance, as it buffers the content and helps lessen a user’s wait when playing an external resource.
Properties you might use include the currentTime, data, delegate, duration, playing, volume, and numberOfLoops. The currentTime property returns the playback in seconds as an NSTimeInterval. The duration property returns a sound’s duration in seconds as an NSTimeInterval. The volume returns the player’s playback gain as a float between 0.0 and 1.0. The playing property returns a BOOL, while the numberOfLoops property returns an unsigned int. There are also more advanced properties, such as numberOfChannels and peakPowerForChannel. For a more complete listing of AVAudioPlayer’s properties and methods, refer to the AVAudioPlayer Class Reference. An AVAudioPlayer’s delegate property refers to an audio player’s AVAudioPlayerDelegate protocol. As with all protocols, you implement a custom class that adopts the protocol. Protocol methods you might implement are listed in Table 18-1.
This task is straightforward; first, you loaded the sound and obtained its id. As system sounds are 30 seconds or less, loading it into memory and keeping it there should not tax your device’s memory. Notice you do not load the longer song into memory until you actually play it in the playSong method, as it takes more memory.You initialize the system sound in the viewDidLoad method. Don’t let the one line of code be intimidating; it’s actually doing something quite simple. It gets the path to the file, creates an NSURL using the path, casts it as a CFURLRef, and creates a system sound from the resource.The playSong method creates a new AVAudioPlayer instance every time the application calls it. If an error occurs, the method logs the error; otherwise, it plays the song. When the song is finished playing, it calls the audioPlayerDidFinishPlaying:successfully: method and releases the player. Figure 18-1 The finished application in the iPhone Simulator Listing 18-1 AvplayerViewController.h #import <UIKit/UIKit.h> #import <AudioToolbox/AudioToolbox.h> #import <AVFoundation/AVFoundation.h> @interface AvplayerViewController : UIViewController <AVAudioPlayerDelegate> { SystemSoundID burpSoundID; AVAudioPlayer * player; }
@end Listing 18-2 AvplayerViewController.m #import "AvplayerViewController.h" @implementation AvplayerViewController
&burpSoundID); }
}
player = [[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource: @"charleston1925_64kb" ofType:@"mp3"]] error:&error]; player.delegate = self; if(error != NULL) { NSLog([error description]); [error release]; } [player play]; }
[theplayer release]; }
} @end NOTE The mp3 song is entitled The Charleston, and is a digital copy of the 1925 recording. I obtained the mp3 from the Internet Archive’s 78RPMS & Cylinder Recordings Collection. The mp3 is licensed under the Creative Commons Commercial license. The burp_2.aif is also public domain. Hopefully, I am not violating any software patents by playing burp sounds on a mobile device...
Before iPhone OS 3.0, a device’s multimedia loaded by iTunes was off limits for application developers. The Media Player framework released with OS 3.0 removes that restriction by providing several classes that work with a device’s iTunes-loaded multimedia. NOTE Running the Media Player audio applications in this chapter require installing the application on an iPod touch or iPhone running iPhone OS 3.0 or later.
An MPMediaLibrary class represents a device’s multimedia library loaded from iTunes. An MPMediaItem object represents every multimedia item in the media library. The MPMediaItem class contains metadata such as title, artist, and length about a media item.When working with the MPMediaLibrary, you usually execute an MPMediaQuery that returns an MPMediaItemCollection. Although the Media Player framework offers several methods for accessing a device’s media programmatically, another way is by using an MPMediaPickerController. An MPMediaPickerController is a class that presents a view much like the current iPod application. A user can then select one or more media items, and the picker returns an MPMediaItemCollection.After selecting the media items to play, you pass them to an MPMusicController to play. The MPMusicController class is responsible for playing music, rewinding, forwarding, and other playback functionality.
An MPMediaItem encapsulates a single audio multimedia element in a device’s iTunes multimedia collection. The MPMediaItem contains one method for obtaining a media item’s properties, the valueForProperty: method. - (id)valueForProperty:(NSString *)property The valueForProperty: method takes a constant representing the property for which to obtain a value. Notice that the method returns an id; this means the function’s return value is tied to the property constant passed to the method. Listing 18-3 lists the properties you might pass to the valueForProperty: method. Listing 18-3 Media Item properties NSString *const MPMediaItemPropertyPersistentID; NSString *const MPMediaItemPropertyAlbumTrackNumber; NSString *const MPMediaItemPropertyAlbumTrackCount; NSString *const MPMediaItemPropertyDiscNumber; NSString *const MPMediaItemPropertyDiscCount; NSString *const MPMediaItemPropertyArtwork; NSString *const MPMediaItemPropertyLyrics; NSString *const MPMediaItemPropertyPodcastTitle; You can also use user-defined properties. These properties have a variable value, depending upon a user’s multimedia collection use. Listing 18-4 lists the user-defined properties. Listing 18-4 User-defined properties NSString *const MPMediaItemPropertyPlayCount; NSString *const MPMediaItemPropertySkipCount; NSString *const MPMediaItemPropertyRating; NSString *const MPMediaItemPropertyLastPlayedDate; The MPMediaItemCollection class is a collection of media items. You obtain a collection’s media items through its items property. This property returns an NSArray of MPMediaItem objects. Other properties include count, mediaTypes, and the representativeItem properties. The count property returns a count of the collection’s multimedia items as an NSUInteger. You use the mediaTypes property and representativeItem property to obtain a collection’s media types.
Before playing multimedia, a user must select the media items. The easiest way to allow a user to do this is through the MPMediaPickerController. Similar to the UIImagePickerController, the view it controls is hidden from developers. Also, like the image picker, you can define a delegate for the media picker. That delegate, the MPMediaPickerControllerDelegate, responds to a user’s interaction with the media picker.
The MPMediaPickerController class manages the media picker view. Like the UIImagePickerController, you present this controller’s view as a modal view that overlays the currently displaying view. [self presentModalViewController:mediaController animated:YES]; You can initialize a media picker to only display certain media types using the initWithMediaTypes method. This method takes an MPMediaType; valid types are MPMediaTypeMusic, MPMediaTypePodcast, MPMediaTypeAudioBook, and MPMediaTypeAnyAudio. Notice there is no MPMediaTypeVideo; you cannot select or play an iTunes-loaded video.You can also initialize a media picker to allow a user to select multiple items by setting the allowsPickingMultimediaItems property to YES. If you don’t set this property, its default value is NO.
A media picker requires an MPMediaPickerControllerDelegate if it is to do anything interesting. An MPMediaPickerControllerDelegate has two methods you implement when adopting this protocol: the mediaPicker:didPickMediaItems: and mediaPickerDidCancel: methods.
The mediaPickerDidCancel: method responds to a user canceling the media picker, while the mediaPicker:didPickMediaItems: method responds to a user clicking the Done button after selecting media in the media picker.
A user will probably wish to play multimedia after selecting it. You play an MPMediaItemsCollection using the MPMusicPlayerController. This class is responsible for playing audio media items. There are two player types to choose from: an iPodMusicPlayer and applicationMusicPlayer. An iPodMusicPlayer replaces an iPod’s state, while an applicationMusicPlayer is independent of a user’s iPod. For instance, if you are using an iPodMusicPlayer and set it to shuffle mode, the user’s iPod application will be in shuffle mode the next time he or she uses it. When using the applicationMusicPlayer, this does not happen because it does not modify the iPod’s state.You initialize a music player’s media collection using the setQueueWithQuery:, setQueueWithItems:, or setQueueWithItemCollection: methods. The set with query method takes an MPMediaQuery, the set with items method takes an NSArray of MPMediaItems, and the set with collection method takes an MPMediaItemCollection. You can initialize a media player’s state using the repeatMode and shuffleMode properties. If you do not set these properties, your player’s state is the user’s iPod application state. Other properties you can use to obtain information about a media player are the nowPlayingItem, currentPlaybackTime, and playbackState properties. The nowPlayingItem property is an MPMediaItem that represents the currently playing media item. The currentPlaybackTime is an NSInterval containing the now-playing item’s playback location in seconds. The playbackState property returns the music player’s playback state as an MPMusicPlaybackState. Valid values for these statesincludeMPMusicPlaybackStateStopped, MPMusicPlaybackStatePlaying,MPMusicPlaybackStatePaused,MPMusicPlaybackState Interrupted,MPMusicPlaybackStateSeekingForward,andMPMusicPlaybackStateSeekingBackward.Methods you can use to control a media player are play, pause, stop, beginSeekingForward,beginSeekingBackward,endSeeking,skipToNextItem, skipToBeginning, and skipToPreviousItem. The play, pause, and stop methods should be self-explanatory. The beginSeekingForward and beginSeekingBackward methods are for fast-forwarding and rewinding, respectively, while the endSeeking method stops fast-forwarding or rewinding. The skipToNextItem, skipToPreviousItem, and skipToBeginning methods should also be self-explanatory. The MPMusicPlayerController has no delegate protocol for responding to its events. Instead it provides two notifications: the MPMusicPlayerControllerPlaybackStateDidChange and MPMusicPlayerControllerNowPlayingItemDidChange notifications. The first notification posts when an MPMusicPlayerController’s playback state changes. The second notification posts when an MPMusicPlayerController’s now-playing media item changes. By using these two notifications, your application can respond to the media player’s state, as the next example task illustrates.
Figure 18-2 The application’s canvas in Interface Builder
Figure 18-3 The application running on an iPod touch
This is perhaps the coolest application in this entire book. The user interface isn’t pretty, but the application works well as a simple music player. In fact, I like it more than my iPod touch’s Music application, as I can quickly select all the songs I am interested in and then play them. I have used the application several times for over an hour each time while exercising, so the application works. In the viewDidLoad method, notice the code for initializing a volume control. Although the MPVolumeView class provides a sizeThatFits method, in my opinion, it is better to do it the way presented here. Simply add a UIView as a subview, size it to the desired size, and then add the volume view to the subview. Easy, and it is guaranteed to size correctly. mpVolumeView = [[MPVolumeView alloc] init]; [mpVolumeView setFrame:[self.volumeView bounds]]; [self.volumeView addSubview:mpVolumeView]; As the MPMediaPlayerController does not have a delegate, you made the iPodSongsViewController a listener for the MPMusicPlayerControllerNowPlayingItemDid ChangeNotification event. When a player’s now-playing item changes, the player fires this event. You set the songChanged method as the method called when the event fires. After being called, the songChanged method obtains the now- playing item and updates the view’s labels. The changeState method handles any one of the five control buttons being tapped. Because each button has a different tag, the method can distinguish which button fires the Touch Up Inside Event and handle the message appropriately. In a real-world application, you woulddisable the buttons as appropriate. For instance, when paused, the Pause button should be disabled and only the Play button enabled. However, here, the extra code would have detracted from the example’s intended purpose. In addition to the player’s notification, you created your own notification in MyMediaPickerDelegate. Upon selecting an MPMediaItemCollection using the media picker, this notification is fired. The songsPicked method in iPodSongsViewController responds to this notification. The method takes the NSNotification as a parameter; remember, notifications can contain an object. Here, that object is the selected MPMediaItemCollection. After initializing the media player with the collection, the songsPicked method tells the media player to begin generating playback notifications and then starts playing. Listing 18-5 iPodSongsViewController.h #import <UIKit/UIKit.h> #import <MediaPlayer/MediaPlayer.h> #import "MyMediaPickerDelegate.h" @interface iPodSongsViewController : UIViewController { MyMediaPickerDelegate * mediaControllerDelegate; MPMediaLibrary * mediaLib; IBOutlet UILabel * currentTitle; IBOutlet UILabel * currentArtist; IBOutlet UILabel * currentLengthInSeconds; MPMusicPlayerController * player; IBOutlet UIView * volumeView; MPVolumeView * mpVolumeView; } @property (nonatomic, retain) MyMediaPickerDelegate * mediaControllerDelegate; @property (nonatomic, retain) MPMediaLibrary * mediaLib; @property (nonatomic, retain) UILabel * currentTitle; @property (nonatomic, retain) UILabel * currentArtist; @property (nonatomic, retain) UILabel * currentLengthInSeconds; @property (nonatomic, retain) MPMusicPlayerController * player; @property (nonatomic, retain) IBOutlet UIView * volumeView;
@end Listing 18-6 iPodSongsViewController.m #import "iPodSongsViewController.h" @implementation iPodSongsViewController @synthesize mediaControllerDelegate; @synthesize mediaLib; @synthesize currentTitle; @synthesize currentArtist; @synthesize currentLengthInSeconds; @synthesize player; @synthesize volumeView; int currentItem = 0; - (void) viewDidLoad { [MPMediaLibrary defaultMediaLibrary]; [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(songsPicked:) name:@"SongsPicked" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(songChanged) name: @"MPMusicPlayerControllerNowPlayingItemDidChangeNotification" object:nil]; mpVolumeView = [[MPVolumeView alloc] init]; [mpVolumeView setFrame:[self.volumeView bounds]]; [self.volumeView addSubview:mpVolumeView]; } - (void) songsPicked: (NSNotification *) notification { player = [MPMusicPlayerController applicationMusicPlayer]; player.repeatMode = MPMusicRepeatModeNone; player.shuffleMode = MPMusicShuffleModeOff; [player setQueueWithItemCollection:(MPMediaItemCollection *) [notification object]]; [player beginGeneratingPlaybackNotifications]; [player play]; } - (void) songChanged { MPMediaItem * tempMediaItem = (MPMediaItem *)player.nowPlayingItem; [self.currentTitle setText:[tempMediaItem valueForProperty: MPMediaItemPropertyTitle]]; [self.currentArtist setText: [tempMediaItem valueForProperty: MPMediaItemPropertyArtist]]; [self.currentLengthInSeconds setText: [NSString stringWithFormat:@"%i", [tempMediaItem valueForProperty:MPMediaItemPropertyPlaybackDuration]]]; }
case 1: [player pause]; break; case 2: [player play]; break; case 3: [player stop]; break; case 4: [player skipToPreviousItem]; break; case 5: [player skipToNextItem]; break; } }
}
[[NSNotificationCenter defaultCenter] removeObserver:self]; [mediaControllerDelegate release]; [player stop]; [mpVolumeView release]; [super dealloc]; } @end Listing 18-7 MyMediaPickerDelegate.h #import <Foundation/Foundation.h> #import <MediaPlayer/MediaPlayer.h> @interface MyMediaPickerDelegate : NSObject <MPMediaPickerController Delegate> { } @end Listing 18-8 MyMediaPickerDelegate.m #import "MyMediaPickerDelegate.h" @implementation MyMediaPickerDelegate - (void) mediaPicker: (MPMediaPickerController *) mediaPicker didPickMediaItems: (MPMediaItemCollection *) mediaItemCollection { NSArray * mediaItems = [mediaItemCollection items]; NSEnumerator * enumer = [mediaItems objectEnumerator]; id myObject; while (myObject = [enumer nextObject]) { MPMediaItem * tempMediaItem = (MPMediaItem *) myObject; NSLog(@"Title: %@", [tempMediaItem valueForProperty: MPMediaItemPropertyTitle]); NSLog(@"id:%@",[tempMediaItem valueForProperty:MPMediaItemProperty Artist]); NSLog(@"id: %i", [tempMediaItem valueForProperty: MPMediaItemPropertyPersistentID]); NSLog(@"-----------------------"); } [mediaPicker.parentViewController dismissModalViewControllerAnimated :YES]; [mediaPicker release]; [[NSNotificationCenter defaultCenter] postNotificationName: @"SongsPicked" object: mediaItemCollection]; }
[[NSNotificationCenter defaultCenter] removeObserver: self]; [super dealloc]; }@end
The MPMoviePlayerController plays video bundled as part of your application. It can also stream video from a server. However, despite its name, it cannot use video loaded on a user’s device by iTunes. The MPMoviePlayerController presents a modal, full-screen video when it plays. You can only minimally modify its appearance using properties. What you can change are a movie player’s background color, a movie’s scaling, and the controls presented to the user when playing a movie. You change a movie’s background color using the backgroundColor property. You change a movie’s scaling using the scalingMode property, and you change the controls presented using the movieControlMode property. The backgroundColor property changes the movie player’s background color when playing a movie. For instance, when you watch a video using the Video application on an iPod touch, the background is black. If you wish, you can change that color using this property. The scalingMode property changes a movie’s scaling. Valid values for scalingMode are MPMovieScalingModeNone, MPMovieScalingModeAspectFit, MPMovieScalingModeAspect Fill, and MPMovieScalingModeFill. The movieControlMode property determines the visible controls when a movie plays. For instance, when playing a movie using the Video application, if you tap the screen, it presents a control showing the volume, a control showing the location in the video, a scaling control, and a Done button. You can modify which control a player presents using this property. Valid values are MPMovieControlModeDefault, MPMovieControlModeVolumeOnly, and MPMovie ControlModeHidden. You initialize a movie player using the initWithContentURL: method. This method takes an NSURL to the movie’s location. This location must be within your application’s sandbox or available via an Internet server (it’s a URL). After initializing the player, you call the prepareToPlay and play methods. If you wish to be notified that the player has stopped playing, you register as a notification listener, listening for the player’s MPMoviePlayerPlay backDidFinishNotification notification.
Like the music player, you register to listen to the finished playing event in the viewDidLoad method. Notice that unlike the previous task, you didn’t place the event’s name in quotations. This omission was intentional. The event and the event’s NSString name are the same, and you can use either. The viewDidLoad method also initializes the movie player with the short.3gp movie’s location. In a real- world application, loading a movie when a view loads is probably not a good idea, as movies are usually quite large.When a user taps the button, the movie player begins playing the movie in landscape mode (Figure 18-4). Upon tapping the Done button, the user returns to the application’s view in portrait mode. The controller also receives the notification that the player stopped playing and releases the player. Listing 18-9 MovieplayerViewController.h #import <UIKit/UIKit.h> #import <MediaPlayer/MediaPlayer.h> @interface MovieplayerViewController : UIViewController { MPMoviePlayerController * movieplayer; } @property (nonatomic, retain) MPMoviePlayerController * movieplayer; -(IBAction) playMovie: (id) sender; - (void) playingDone; @end
Figure 18-4 The application running in iPhone Simulator
Listing 18-10 MovieplayerViewController.m #import "MovieplayerViewController.h" @implementation MovieplayerViewController @synthesize movieplayer; - (void) viewDidLoad { [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector (playingDone) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; }
movieplayer = [[MPMoviePlayerController alloc] initWithContentOfURL: [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"short" ofType:@"3gp"]]]; [movieplayer play]; }
}
[[NSNotificationCenter defaultCenter] removeObserver:self]; [movieplayer release]; [super dealloc]; } @end
You liked the article?
Like: 0
Vote for difficulty
Current difficulty (Avg): Medium
TekSlate is the best online training provider in delivering world-class IT skills to individuals and corporates from all parts of the globe. We are proven experts in accumulating every need of an IT skills upgrade aspirant and have delivered excellent services. We aim to bring you all the essentials to learn and master new technologies in the market with our articles, blogs, and videos. Build your career success with us, enhancing most in-demand skills in the market.