理解音频会话
音频会话在应用程序和操作系统之间扮演着中间人的角色。意思就是开发者可以利用音频会话与iOS音频环境交互,而不需要关心底层如何实现。
所有的iOS程序都具有音频会话,无论是否使用。默认音频会话来自以下一些配置:
- 激活了音频播放,但是音频录制未激活;
- 用户切换响铃/静音开关到静音模式时,所有音频消失;
- 设备显示解锁屏幕时,所有音频消失;
- 应用程序播放音频时,所有后台播放的音频处于静音状态;
可以通过音频会话分类定制这些需求。
配置音频会话
在应用程序声明周期中一般只在程序启动时配置一遍,即在application:didFinishLaunchingWithOptions:方法中。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
//设置后台播放
if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"%@", error.localizedDescription);
}
//
if (![session setActive:YES error:&error]) {
NSLog(@"%@", error.localizedDescription);
}
return YES;
}
使用AVAudioplayer播放音频
播放音频功能主要包括播放过程控制及播放时参数获取。如播放、暂停、停止、修改音量、修改播放速率、循环播放、立体声播放以及音频计量。
创建AVAudioPlayer
获取待播放文件url或data
可通过url或data创建播放器实例对象AVAudioPlayer
NSError *error = nil;
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
准备播放工作
/**
准备播放 建议显式调用
该方法会取得需要的音频硬件并预加载Audio Queue缓冲区,
降低调用play方法和听到声音输出之间的延时。
当直接调用play方法时也会隐式调用prepareToPlay方法
*/
[_audioPlayer prepareToPlay];
播放音频
//允许控制播放速率 注意调用此方法要在prepareToPlay之前
//官方说明:set this property to YES after you initialize the player and before you call the prepareToPlay instance method for the player.
_audioPlayer.enableRate = YES;
//准备播放 建议显式调用
[_audioPlayer prepareToPlay];
//播放
[_audioPlayer play];
//循环次数 -1无限循环
_audioPlayer.numberOfLoops = -1;
控制播放参数
//设置播放速率 0.5-2.0之间倍速播放
_audioPlayer.rate = slider.value;
//立体声播放 -1.0-1.0之间
_audioPlayer.pan = slider.value;
//控制播放音量 在0.0-1.0之间
_audioPlayer.volume = slider.value;
通过音频会话分类处理中断事件
上面我们在AppDelegate中将session的分类设置为后台播放,仅仅设置这个还是不能满足后台播放条件的。还需要在Info.plist文件中添加一个新的Required background modes类型的数组,在其中添加App plays audio or streams audio/video using AirPlay。如图:
BackgroundPlist.png处理中断事件
在播放音频时,呼入电话或者FaceTime等都会打断正在播放的音频,因此需要监听中断通知来处理中断事件。
当中断发生时,系统会自动处理中断信息,停止音频播放,并不需要我们做任何事情。但是当中断结束后,你会发现不管是界面上的按钮状态还是播放状态,都没有恢复,因此我们需要手动处理这些中断。
NSNotificationCenter *nsnc = [NSNotificationCenter defaultCenter];
[nsnc addObserver:self
selector:@selector(handleInterrupt:)
name:AVAudioSessionInterruptionNotification
object:[AVAudioSession sharedInstance]];
通知返回的userInfo 中会包含必要的中断类型信息,通过检索AVAudioSessionInterruptionTypeKey可以获取AVAudioSessionInterruptionType,这是一个枚举类型。
- (void)handleInterrupt:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
AVAudioSessionInterruptionType type = [userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (type == AVAudioSessionInterruptionTypeBegan) {
//中断开始 暂停播放,设置暂停播放UI
} else if (type == AVAudioSessionInterruptionTypeEnded) {
//中断恢复 恢复播放,恢复UI
}
}
响应线路改变
在iOS设备上添加或移除音频输入输出线路时,会发生线路变化,如用户插拔耳机。当这些事件发生时,AVAudioSession会广播一个描述该变化的通知给监听者。
注册通知
[nsnc addObserver:self
selector:@selector(handleRouteChanged:)
name:AVAudioSessionRouteChangeNotification
object:[AVAudioSession sharedInstance]];
接收到通州后首先判断线路变更发生的原因,查看保存在userInfo中的表示原因的AVAudioSessionRouteChangeReasonKey
- (void)handleRouteChanged:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
//线路发生变更原因
AVAudioSessionRouteChangeReason reason = [userInfo[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
}
需要特别注意的是耳机断开事件,该事件对应的原因是AVAudioSessionRouteChangeReasonOldDeviceUnavailable
知道有设备断开连接后,我们可以从userInfo中获取上次的连接方式,做出响应的操作。如耳机断开后,需要停止播放音频。线路的描述信息整合在一个输入NSArray 和一个输出NSArray中。数组中的元素都是AVAudioSessionPortDescription。从上次线路描述中找到第一个输出接口并判断其是否为耳机,并调用对应方法。
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
//获取上一次路线状态
AVAudioSessionRouteDescription *previousRoute = userInfo[AVAudioSessionRouteChangePreviousRouteKey];
//获取第一个输出接口
AVAudioSessionPortDescription *portDescription = previousRoute.outputs[0];
if ([portDescription.portType isEqualToString:AVAudioSessionPortHeadphones]) {
//判断上一次是耳机状态
}
}
使用AVAudioRecorder录制音频
创建AVAudioRecorder实例时需要为其提供数据的一些信息,分别是:
- 用于表示音频流写入文件的本地文件URL;
- 包含用于配置录音会话键值信息的NSDictionary对象;
- 用于捕捉初始化阶段各种错误的NSError指针。
NSString *directory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO)[0];
directory = [directory stringByAppendingPathComponent:@"voice.m4a"];
NSURL *url = [NSURL URLWithString:directory];
NSDictionary *setting = @{AVFormatIDKey: @(kAudioFormatMPEG4AAC),
AVSampleRateKey: @22050.0f,
AVNumberOfChannelsKey: @1
};
NSError *error = nil;
_audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:setting error:&error];
if (_audioRecorder) {
[_audioRecorder prepareToRecord];
} else {
//创建失败
}
音频格式
AVFormatIDKey:定义了写入内容的音频格式;如kAudioFormatLinearPCM会将未压缩的音频流写入到文件中,这种格式的保真度最高,不过文件最大。特别注意的是指定的音频格式一定要个URL参数定义的文件类型兼容。如录制名为test.wav的文件,那么AVFromatIDKey必须为kAudioFormatLinearPCM,否则报错:
The operation couldn’t be completed. (OSStatus error 1718449215.)
1718449215错误状态是4字节编码的整数值,'fmt?'已至定义了一种不兼容的音频格式。
采样率
AVSampleRateKey:采样率,对于使用什么采样率没有明确定义,不过尽量使用标准的采样率,如8000、16000、22050等。
通道数
AVNumberOfChannelsKey:定义记录音频内容的通道数,1意味着单声道录制,2意味着立体声录制,除非使用外部硬件,否则通常创建单声道。
配置录制音频的音频会话
默认音频会话分类为AVAudioSessionCategorySoloAmbient,该分类不支持音频输入,所以最合适的分类是AVAudioSessionCategoryPlayAndRecord,具体视应用程序功能而定。
此处应注意:在使用麦克风之前,操作系统要求应用程序必须得到用户的明确许可,需要在Info.plist文件中配置响应权限。
网友评论