录音

http://www.devdiv.com/forum.php?mod=viewthread&tid=125220


9.5 在其他活动声音上面播放音频

问题
你可能想在播放音频时让其他程序静音,或者在其他程序的音频回放之上播放音频。

解决
使用音频会话设置你的应用程序所使用的音频类别。

讨论
AVAudioSession类由AVFoundation框架引入。每个IOS应用都有一个音频会话。这个会话可以被AVAudioSession类的sharedInstance类方法访问,如下:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];

在获得一个AVAudioSession类的实例后,你就能通过调用音频会话对象的setCategory:error:实例方法,来从IOS应用可用的不同类别中作出选择。下面列出了可供使用的音频会话类别:
AVAudioSessionCategorySoloAmbient

这个类别非常像AVAudioSessionCategoryAmbient类别,除了会停止其他程序的音频回放,比如iPod程序。当设备被设置为静音模式,你的音频回放将会停止。

AVAudioSessionCategoryRecord
这会停止其他应用的声音(比如iPod)并让你的应用也不能初始化音频回放(比如AVAudioPlayer)。在这种模式下,你只能进行录音。使用这个类别,调用AVAudioPlayer的prepareToPlay会返回YES,但是调用play方法将返回NO。主UI界面会照常工作。这时,即使你的设备屏幕被用户锁定了,应用的录音仍会继续。

AVAudioSessionCategoryPlayback
这个类别会静止其他应用的音频回放(比如iPod应用的音频回放)。你可以使用AVAudioPlayer的prepareToPlay和play方法,在你的应用中播放声音。主UI界面会照常工作。这时,即使屏幕被锁定或者设备为静音模式,音频回放都会继续。

AVAudioSessionCategoryPlayAndRecord
这个类别允许你的应用中同时进行声音的播放和录制。当你的声音录制或播放开始后,其他应用的声音播放将会停止。主UI界面会照常工作。这时,即使屏幕被锁定或者设备为静音模式,音频回放和录制都会继续。

AVAudioSessionCategoryAudioProcessing
这个类别用于应用中进行音频处理的情形,而不是音频回放或录制。设置了这种模式,你在应用中就不能播放和录制任何声音。调用AVAPlayer的prepareToPlay和play方法都将返回NO。其他应用的音频回放,比如iPod,也会在此模式下停止。

AVAudioSessionCategoryAmbient
这个类别不会停止其他应用的声音,相反,它允许你的音频播放于其他应用的声音之上,比如iPod。你的应用的主UI县城会工作正常。调用AVAPlayer的prepareToPlay和play方法都将返回YES。当用户锁屏时,你的应用将停止所有正在回放的音频。仅当你的应用是唯一播放该音频文件的应用时,静音模式将停止你程序的音频回放。如果正当iPod播放一手歌时,你开始播放音频,将设备设为静音模式并不能停止你的音频回放。

9.7 为视频文件捕获缩略图

问题
你在使用MPMoviePlayerController类的实例播放一个视频文件时,可能想要在某个时刻从影片捕获一张截图。

解决
使用MPMoviePlayerController的requestThumbnailImagesAtTimes:timeOption:实例方法,如下:

/* Capture the frame at the third second into the movie */ NSNumber *thirdSecondThumbnail = [NSNumber numberWithFloat:3.0f];
/* We can ask to capture as many frames as we
want. But for now, we are just asking to capture one frame */
NSArray *requestedThumbnails =
[NSArray arrayWithObject:thirdSecondThumbnail];
/* Ask the movie player to capture this frame for us */
[self.moviePlayer
requestThumbnailImagesAtTimes:requestedThumbnails timeOption:MPMovieTimeOptionExact];
讨论
MPMoviePlayerController的一个实例能够从最近播放的影片中捕获缩略图,同步和异步都可以。在这里,我们将关注这个类的异步图像捕获。

我们可以使用MPMoviePlayerController的requestThumbnailImagesAtTimes:timeOption:实例方法来异步访问缩略图。当我说“异步”时,我的意思是,在缩略图正在被捕获和被报告给你指派的对象(我们很快能看到)的过程中,视频播放器将继续它的工作,且不会阻塞回放。我们必须观察MPMoviePlayerThumbnailImage RequestDidFinishNotification通知消息,这个消息由视频播放器发送给默认消息中心,以便知道我们的缩略图什么时候可用:

- (void) startPlayingVideo:(id)paramSender{
/* First let's construct the URL of the file in our application bundle
that needs to get played by the movie player */ NSBundle *mainBundle = [NSBundle mainBundle];
NSString *urlAsString = [mainBundle pathForResource:@"Sample"
ofType:@"m4v"];
NSURL *url = [NSURL fileURLWithPath:urlAsString];
/* If we have already created a movie player before, let's try to stop it */
if (self.moviePlayer != nil){
[self stopPlayingVideo:nil]; }
/* Now create a new movie player using the URL */
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:url];
if (self.moviePlayer != nil){
/* Listen for the notification that the movie player sends us whenever it finishes playing an audio file */
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(videoHasFinishedPlaying:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(videoThumbnailIsAvailable:)
name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:self.moviePlayer];
NSLog(@"Successfully instantiated the movie player.");
/* Scale the movie player to fit the aspect ratio */ self.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
/* Let's start playing the video in full screen mode */ [self.moviePlayer play];
[self.view addSubview:self.moviePlayer.view];
[self.moviePlayer setFullscreen:YES animated:YES];
/* Capture the frame at the third second into the movie */ NSNumber *thirdSecondThumbnail = [NSNumber numberWithFloat:3.0f];
/* We can ask to capture as many frames as we
want. But for now, we are just asking to capture one frame */
NSArray *requestedThumbnails =
[NSArray arrayWithObject:thirdSecondThumbnail];
/* Ask the movie player to capture this frame for us */ [self.moviePlayer
requestThumbnailImagesAtTimes:requestedThumbnails timeOption:MPMovieTimeOptionExact];
} else {
NSLog(@"Failed to instantiate the movie player."); }
}
你可以看到,我们我们要求视频播放器捕获影片中第三秒的那个帧。一旦这个任务完成,我们的视图控制器的videoThumbnailIsAvailable:实例方法就会被调用,下面的代码说明了我们如何能访问被捕获的图像:

- (void) videoThumbnailIsAvailable:(NSNotification *)paramNotification{ MPMoviePlayerController *controller = [paramNotification object];
if (controller != nil &&
[controller isEqual:self.moviePlayer]){ NSLog(@"Thumbnail is available");
/* Now get the thumbnail out of the user info dictionary */ UIImage *thumbnail =
[paramNotification.userInfo
objectForKey:MPMoviePlayerThumbnailImageKey];
if (thumbnail != nil){
/* We got the thumbnail image. You can now use it here */
} }
}

因为我们在startPlayingVideo:中实例化视频播放器时,开始了监听MPMoviePlayerThumbnailImageRequestDidFinishNotifi cation
通知,所以也必须在我们停止视频播放器时,停止对这个通知的监听(或者任何你在你的应用框架中认为恰当的时间)。

- (void) stopPlayingVideo:(id)paramSender { if (self.moviePlayer != nil){
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer];
[[NSNotificationCenter defaultCenter]
removeObserver:self name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:self.moviePlayer];
[self.moviePlayer stop];
if ([self.moviePlayer.view.superview isEqual:self.view]){ [self.moviePlayer.view removeFromSuperview];
} }
}

当在调用MPMoviePlayerController的requestThumbnailImagesAtTimes:timeOption:实例方法时,我们可以为timeOption:指定MPMovie TimeOptionExact 或者MPMovieTimeOptionNearestKeyFrame其中之一。前者捕获视频时间线上精确的点,后者虽然不那么精确,但使用更少的系统资源,并在捕获缩略图时整体上有更好的表现。MPMovieTimeOptionNearestKey Frame通常能胜任精确要求,因为它仅仅有那么一两帧的误差。


9.8 访问音乐库

问题
你想要访问用户从她的音乐库中挑选的物品。

解决
使用MPMediaPickerController类:
MPMediaPickerController *mediaPicker = [[MPMediaPickerController alloc]
initWithMediaTypes:MPMediaTypeAny];
讨论
MPMediaPickerController是一个iPod程序显示给用户的视图控制器。只要实例化MPMediaPickerController,你就可以为你的用户展现一个标准的视图控制器,让他们任意从音乐库中进行选择,然后控制重新返回到你的程序中。这点对游戏特别有用,例如,用户玩游戏时,可以让你的程序在背景播放它最喜爱的歌曲。

你可以用成为媒体选择控制器的委托(遵守MPMediaPickerControllerDelegate协议)的方式从它那里获得信息:
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
@interface Accessing_the_Music_LibraryViewController
: UIViewController <MPMediaPickerControllerDelegate>
@end

在你的displayMediaPicker:选择器中,实现所需的代码,来显示媒体选择控制器实例并且向用户展现一个模态视图控制器:
- (void) displayMediaPicker{
MPMediaPickerController *mediaPicker = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeAny];
if (mediaPicker != nil){
NSLog(@"Successfully instantiated a media picker."); mediaPicker.delegate = self; mediaPicker.allowsPickingMultipleItems = NO;
[self.navigationController presentModalViewController:mediaPicker animated:YES];
} else {
NSLog(@"Could not instantiate a media picker."); }
}

媒体选择控制器的allowsPickingMultipleItems属性,让你指定用户能否在隐藏控制器之前选择多于一个物品。它接受一个BOOL值,目前我们就把它设置为NO,稍后我们会看到效果。现在,让我们来实现MPMediaPickerControllerDelegate协议的各种委托消息:
- (void) mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection{
NSLog(@"Media Picker returned");
for (MPMediaItem *thisItem in mediaItemCollection.items){
NSURL [thisItem
NSString [thisItem
NSString [thisItem
*itemURL = valueForProperty:MPMediaItemPropertyAssetURL];
*itemTitle = valueForProperty:MPMediaItemPropertyTitle];
*itemArtist = valueForProperty:MPMediaItemPropertyArtist];
MPMediaItemArtwork *itemArtwork =
[thisItem valueForProperty:MPMediaItemPropertyArtwork];
NSLog(@"Item URL = %@", itemURL); NSLog(@"Item Title = %@", itemTitle); NSLog(@"Item Artist = %@", itemArtist); NSLog(@"Item Artwork = %@", itemArtwork);
}
[mediaPicker dismissModalViewControllerAnimated:YES]; }
你可以使用MPMediaItem的valueForProperty:实例方法访问不同的属性。这个类的实例通过mediaPicker:didPick MediaItems:委托消息的mediaItemCollection参数返回到你的程序中。

现在让我们写一个带有很简单的GUI的一个程序,它允许我们询问用户,来从iPod库中选择一个音乐。在她选择了音乐文件后,我们尝试使用MPMusicPlayerController实例来播放它。我们的GUI有个简单的按钮:“选择”和“播放”,以及“停止播放”。第一个按钮会要用户从iPod库中选择一段音频给我们播放,第二个按钮会停止音频回放(如果我们已经在播放音频了)。我们会从程序的UI设计开始。让我们以简单的方式创建,就像图9-1那样。

图9-1 一个极简单的UI,展示了媒体选择器和AV音频播放器

现在让我们继续在视图控制器的.h中定义这两个按钮:
@interface Accessing_the_Music_LibraryViewController : UIViewController <MPMediaPickerControllerDelegate, AVAudioPlayerDelegate>
@property (nonatomic, strong) MPMusicPlayerController *myMusicPlayer; @property (nonatomic, strong) UIButton *buttonPickAndPlay;
@property (nonatomic, strong) UIButton *buttonStopPlaying;
@end

当我们的视图加载完毕,我们会实例化这两个按钮们将他们放置于视图之上:
- (void)viewDidLoad { [super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.buttonPickAndPlay = [UIButton buttonWithType:UIButtonTypeRoundedRect]; self.buttonPickAndPlay.frame = CGRectMake(0.0f,
0.0f, 200, 37.0f);
self.buttonPickAndPlay.center = CGPointMake(self.view.center.x, self.view.center.y - 50);
[self.buttonPickAndPlay setTitle:@"Pick and Play" forState:UIControlStateNormal];
[self.buttonPickAndPlay addTarget:self action:@selector(displayMediaPickerAndPlayItem)
forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:self.buttonPickAndPlay];
self.buttonStopPlaying = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.buttonStopPlaying.frame = CGRectMake(0.0f, 0.0f,
200, 37.0f);
self.buttonStopPlaying.center = CGPointMake(self.view.center.x,
self.view.center.y + 50); [self.buttonStopPlaying setTitle:@"Stop Playing"
forState:UIControlStateNormal]; [self.buttonStopPlaying addTarget:self
action:@selector(stopPlayingAudio) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.buttonStopPlaying];
[self.navigationController setNavigationBarHidden:YES animated:NO];
}

视图控制器的两个最重要的方法是displayMediaPicker 和 PlayItem and stopPlayingAudio:
- (void) stopPlayingAudio{
if (self.myMusicPlayer != nil){
[[NSNotificationCenter defaultCenter]
removeObserver:self name:MPMusicPlayerControllerPlaybackStateDidChangeNotification object:self.myMusicPlayer];
[[NSNotificationCenter defaultCenter]
removeObserver:self name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification object:self.myMusicPlayer];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMusicPlayerControllerVolumeDidChangeNotification object:self.myMusicPlayer];
[self.myMusicPlayer stop]; }
}
- (void) displayMediaPickerAndPlayItem{
MPMediaPickerController *mediaPicker = [[MPMediaPickerController alloc]
initWithMediaTypes:MPMediaTypeMusic]; if (mediaPicker != nil){
NSLog(@"Successfully instantiated a media picker."); mediaPicker.delegate = self; mediaPicker.allowsPickingMultipleItems = YES;
[self.navigationController presentModalViewController:mediaPicker animated:YES];
} else {
NSLog(@"Could not instantiate a media picker.");
} }

当我们的媒体选择控制器成功完成,mediaPicker:didPickMediaItems消息会在委托对象中被调用(这个例子中就是视图控制器)。另一方面,如果用户取消了媒体播放器,我们会得到mediaPicker:mediaPickerDid Cancel消息。下面的代码实现了每种情况下将会被调用的方法。
- (void) mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection{
NSLog(@"Media Picker returned");
/* First, if we have already created a music player, let's deallocate it */
self.myMusicPlayer = nil;
self.myMusicPlayer = [[MPMusicPlayerController alloc] init]; [self.myMusicPlayer beginGeneratingPlaybackNotifications];
/* Get notified when the state of the playback changes */ [[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(musicPlayerStateChanged:)
name:MPMusicPlayerControllerPlaybackStateDidChangeNotification object:self.myMusicPlayer];
/* Get notified when the playback moves from one item
to the other. In this recipe, we are only going to allow our user to pick one music file */
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(nowPlayingItemIsChanged:) name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification object:self.myMusicPlayer];
/* And also get notified when the volume of the music player is changed */
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(volumeIsChanged:)
name:MPMusicPlayerControllerVolumeDidChangeNotification object:self.myMusicPlayer];
/* Start playing the items in the collection */
[self.myMusicPlayer setQueueWithItemCollection:mediaItemCollection]; [self.myMusicPlayer play];
/* Finally dismiss the media picker controller */ [mediaPicker dismissModalViewControllerAnimated:YES];
}
- (void) mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker{
/* The media picker was cancelled */
NSLog(@"Media Picker was cancelled");
[mediaPicker dismissModalViewControllerAnimated:YES];
}

我么在监听音乐播放器通过通知发送的事件。下面是用于处理被监听的音乐播放器通知消息的三个方法:
- (void) musicPlayerStateChanged:(NSNotification *)paramNotification{ NSLog(@"Player State Changed");
/* Let's get the state of the player */ NSNumber *stateAsObject = [paramNotification.userInfo
objectForKey:@"MPMusicPlayerControllerPlaybackStateKey"]; NSInteger state = [stateAsObject integerValue];
/* Make your decision based on the state of the player */ switch (state){
case MPMusicPlaybackStateStopped:{
/* Here the media player has stopped playing the queue. */
break; }
case MPMusicPlaybackStatePlaying:{
/* The media player is playing the queue. Perhaps you
can reduce some processing that your application that is using to give more processing power
to the media player */ break;
}
case MPMusicPlaybackStatePaused:{
/* The media playback is paused here. You might want to indicate by showing graphics to the user */
break; }
case MPMusicPlaybackStateInterrupted:{
/* An interruption stopped the playback of the media queue */
break; }
case MPMusicPlaybackStateSeekingForward:{
/* The user is seeking forward in the queue */
break; }
case MPMusicPlaybackStateSeekingBackward:{
/* The user is seeking backward in the queue */
break; }
} /* switch (State){ */ }
                -  (void) nowPlayingItemIsChanged:(NSNotification *)paramNotification{ NSLog(@"Playing Item Is Changed"); 
NSString *persistentID = [paramNotification.userInfo 
objectForKey:@"MPMusicPlayerControllerNowPlayingItemPersistentIDKey"]; 
/* Do something with Persistent ID */ NSLog(@"Persistent ID = %@", persistentID); 
} 
                -  (void) volumeIsChanged:(NSNotification *)paramNotification{ 
NSLog(@"Volume Is Changed"); 
/* The userInfo dictionary of this notification is normally empty */ } 

我们还会实现视图控制器的viewDidUnload方法,来确保不会留下任何内存泄漏:
        .        - (void) viewDidUnload{ [super viewDidUnload]; 
[self stopPlayingAudio]; 
self.myMusicPlayer = nil; }
接下来,运行我们的程序,点击视图控制器的“点击”和“播放”按钮,我们会看到媒体选择控制器。一旦控制器出现,和iPod相同的UI将展现给用户。在用户选择了一项之后(或者取消整个对话框),我们会在视图控制器中得到相应的委托消息(当我们的视图控制器是媒体选择器的视图控制器时)。在物品被选择后(在这里我们只允许选一个),我们会开始我们的音乐播放器,并开始播放整个选择集合。
如果你想允许用户一次选择多于一项,只要设置你的媒体选择器的allowsPickingMultipleItems属性为YES:
mediaPicker.allowsPickingMultipleItems = YES;

有时,当在使用媒体选择控制器时(MPMedia PickerController),“MPMediaPicker:失去到iPod库的连接”消息将会输出到控制台屏幕。这是因为媒体选择器在显示给用户时,因为诸如iTunes同步这样的事件而被打断。这时,你的mediaPickerDidCancel:委托方法立刻会被调用。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值