音频会话(Audio Session)
音频会话是应用程序和操作系统之间的中间人。应用程序不需要具体知道怎样和音频硬件交互的细节,只需要把所需的音频行为委托给音频会话管理即可。
- 音频会话分类(Audio Session Category)
AV Foundation把音频行为分成了七类。各个分类有着不同的作用,因此各个分类用在不同的功能场景。
以上七个分类能够满足大部分的需求,但是开发者也可以通过使用选项(options)和模式(modes)自定义,增强和修改原有的分类功能。
配置音频会话
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
// 音频会话分类(catehory)和模式(mode)一起决定了应用要使用音频的方式,也可以说是定制音频的行为。通常在激活音频会话之前设置分类和模式。也可以在激活音频会话时设置分类和模式,但是这样会立即路由(route)。
// 如果分类设置成功,但会也是,反则返回no
if([audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]){
// 配置成功
}else{
// 配置失败
}
音频录制
AV Foundation提供了AVAudioRecorder类,用于从内置麦克风或者外置音频设备录制音频。
创建AVAudioRecorder需要提供以下数据信息:
- 录制的音频要存放的位置,也就是本地URL。
- 音频格式、采样率、声道等配置信息,这些信息使用字典保存。
- NSError指针,用于捕捉初始化时可能出现的错误。
创建和准备AudioRecorder对象:
+(instancetype)audioRecordertoPrepare:(NSString *)audioName error:(NSError *)error{
NSString *audioDirectory = ;
if(audioDirectory){
NSString *audioPath = ;
NSDictionary *audioSettings = @{AVFormatIDKey:@(kAudioFormatMPEG4AAC),
AVSampleRateKey:@22050.0f,
AVNumberOfChannelsKey:@1
};
AVAudioRecorder *audioRecorder = [[AVAudioRecorder alloc] initWithURL:audioURL settings:audioSettings error:&error];
// prepareToRecord方法根据URL创建文件,并且执行底层Audio Queue初始化的必要过程,将录制启动时的延迟降到最低。
if([audioRecorder prepareToRecord]){
return audioRecorder;
}
}
return nil;
}
- 音频格式
kAudioFormatMPEG4AAC
压缩格式能在显著减小文件的同时,保证音频的质量。同时Android也支持aac
音频格式。 - 采样率
采样率越高,文件越大,质量越好,反之,文件小,质量相对差一些,但是低于普通的音频,人耳并不能明显的分辨出好坏。最终选取哪一种采样率,由我们的耳朵来判断。建议使用标准的采样率,8000、16000、22050、44100。 - 通道数
AVNumberOfChannelsKey
用于指定记录音频的通道数。1为单声道,2为立体声。除非使用外部硬件进行录制,否则通常使用单声道录制。 - 其他配置
在AV Foundation Audio Session Settings Constants中查看完整的键列表。
音频录制控制
- 启动、恢复音频录制
[AVAudioRecorder record];
```
- 暂停音频录制
[AVAudioRecorder pause];
- 结束录制
[AVAudioRecorder stop];
- 删除录制的音频文件,删除之前必须先停止录制
[self.audioRecorder deleteRecording];
录制和删除录制文件方法返回布尔值,如果操作成功,返回YES,操作失败返回NO,所以在实际的开发中,还应该考虑操作失败状态的处理,给用户以友好的提示。
同时AVAudioRecorder
也提供了AVAdudioRecorderDelegate
协议,用于接收音频录制完成、音频录制过程发生错误的事件。虽然也有定义音频录制过程被中断的方法,但是已经弃用。所以要监听诸如系统来电,闹钟响铃,Facetime……导致的音频录制终端事件,使用AVAudioSession
通知代替。
AVAudioDelegate定义的接口:
@optional
/* 录制完成或者调用stop时,回调用这个方法。但是如果是系统中断录音,则不会调用这个方法。 */
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag;
/* 在录制时出现错误时调用这个代理方法 */
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError * __nullable)error;
#if TARGET_OS_IPHONE
/* AVAudioRecorder INTERRUPTION NOTIFICATIONS 已废弃 - 使用 AVAudioSession 替代. */
/* audioRecorderBeginInterruption: is called when the audio session has been interrupted while the recorder was recording. The recorded file will be closed. */
- (void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder NS_DEPRECATED_IOS(2_2, 8_0);
/* audioRecorderEndInterruption:withOptions: is called when the audio session interruption has ended and this recorder had been interrupted while recording. */
/* Currently the only flag is AVAudioSessionInterruptionFlags_ShouldResume. */
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder withOptions:(NSUInteger)flags NS_DEPRECATED_IOS(6_0, 8_0);
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder withFlags:(NSUInteger)flags NS_DEPRECATED_IOS(4_0, 6_0);
/* audioRecorderEndInterruption: is called when the preferred method, audioRecorderEndInterruption:withFlags:, is not implemented. */
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder NS_DEPRECATED_IOS(2_2, 6_0);
AVAudioSession通知
/** 注册音频录制中断通知 */
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(handleNotification:) name:AVAudioSessionInterruptionNotification object:nil];
// 接收录制中断事件通知,并处理相关事件
-(void)handleNotification:(NSNotification *)notification{
NSArray *allKeys = notification.userInfo.allKeys;
// 判断事件类型
if([allKeys containsObject:AVAudioSessionInterruptionTypeKey]){
AVAudioSessionInterruptionType audioInterruptionType = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
switch (audioInterruptionType) {
case AVAudioSessionInterruptionTypeBegan:
self.statusLabel.text = @"录音被打断…… 开始";
break;
case AVAudioSessionInterruptionTypeEnded:
self.statusLabel.text = @"录音被打断…… 结束";
break;
}
}
// 判断中断的音频录制是否可恢复录制
if([allKeys containsObject:AVAudioSessionInterruptionOptionKey]){
AVAudioSessionInterruptionOptions shouldResume = [[notification.userInfo valueForKey:AVAudioSessionInterruptionOptionKey] integerValue];
if(shouldResume){
self.statusLabel.text = @"录音被打断…… 结束 可以恢复录音了";
}
}
}