目标
学习AVAudioPlayer和AVAudioRecorder类来实现音频播放及录制功能
理解音频会话
iOS默认音频会话的预配置
- 激活了音频播放,但是音频录制未激活
- 当用户切换响铃/静音开关到“静音”模式时,应用程序播放的所有音频都会消失
- 当设备显示解锁屏幕时,应用程序的音频处于静音状态
- 当应用程序播放音频时,所有后台播放的音频都会处于静音状态
音频会话分类
-
上述分类提供满足大部分需求的常见行为,如果需要更加复杂的功能,其中一些分类通过使用options和modes方法进一步自定义开发
- options让开发者使用一些附加行为(如Playback分类允许输出音频和背景声音混合)
- modes可以通过引入被定制的行为进一步对分类进行修改以满足特殊需求
配置音频会话
- 音频会话在应用程序的生命周期中是可以修改的,但通常只配置一次,就是在应用程序启动时
- 通过设置合适的分类,可为音频的播放指定需要的音频会话,其中定制一些行为
//配置音频会话
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
if ([session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]) {
NSLog(@"AVAudioSession setCategory error = %@",error.userInfo);
}
if ([session setActive:YES error:&error]) {
NSLog(@"AVAudioSession setActive error = %@",error.userInfo);
}
AVAudioPlayer播放音频
- 初始化AVAudioPlayer(使用包含播放音频的内存版本的NSData,或本地音频文件的NSURL)
- 建议调用其prepareToPlay方法,这样会取得需要的音频硬件并预加载Audio Queue的缓冲区,降低调用play方法和听到声音输出之间的延时
- 通过pause和stop方法停止的音频都会继续播放,主要区别在底层处理上(stop方法会撤销调用prepareToPlay时所作的设置,而pause方法则不会)
- 指定位置播放:currentTime
- 修改播放器的音量:volume
- 修改播放器的pan值: -1.0(极左), 1.0(极右),默认0.0居中
- 调整播放率rate:iOS5加入,范围(0.5 - 2.0)半速-2倍速。前提条件需要设置enableRate属性
- 无缝循环numberOfLoops:大于0可实现n次循环,-1无限循环(音频循环可以是未压缩的线性PCM音频,也可以是AAC之类的压缩格式音频。但是MP3格式音频要实现循环的目的通常需要使用特殊工具处理,建议使用AAC或AppleLossless格式循环)
-(void)adjustPan:(float)pan{
self.player.pan = pan;
}
-(void)adjustVolume:(float)volume{
self.player.volume = volume;
}
- (void)adjustRate:(float)rate{
self.player.rate = rate;
}
-(AVAudioPlayer *)playerWithFile:(NSString *)file{
NSURL *fileUrl = [NSURL fileURLWithPath:file];
NSError *error = nil;
AVAudioPlayer *player = [[AVAudioPlayer alloc]initWithContentsOfURL:fileUrl error:&error];
if (player) {
//无限循环
player.numberOfLoops = -1;
player.delegate = self;
//循序修改倍速
player.enableRate = YES;
[player prepareToPlay];
}else{
if ([self.delegate respondsToSelector:@selector(DKAudioPlayerManagerDidFailedWithError:target:)]) {
[self.delegate DKAudioPlayerManagerDidFailedWithError:error target:self];
}
}
return player;
}
音频会话通知
中断通知AVAudioSessionInterruptionNotification,在通知响应函数中可以通过userInfo字典获取重要信息,就可以通过AVAudioSessionInterruptionTypeKey的值来确定中断类型,来标识中断开始或结束,做出响应处理
-
线路改变通知AVAudioSessionRouteChangeNotification,在iOS设备上添加或移除音频输入、输出线路时,会发生线路改变。
userInfo字典中通过AVAudioSessionRouteChangeReasonKey来获取中断原因。
-
userInfo字典中通过AVAudioSessionRouteChangePreviousRouteKey字典来获取线路描述对象。
- 线路描述对象整合了一个inputs和outputs的输入输出数组,数组中都是AVAudioSessionPortDescription实例对象,用于描述不同的I/O接口属性
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeRate:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeRate:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
通知处理
NSDictionary *userInfo = noti.userInfo;
//AVAudioSessionInterruptionTypeKey 确认系统中断类型
//来电、QQ微信语音、其他音乐软件暂停
AVAudioSessionInterruptionType reason = [userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
//中断开始
if (reason == AVAudioSessionInterruptionTypeBegan) {
if (self.isPlaying) {
[self stop];
if ([self.delegate respondsToSelector:@selector(DKAudioPlayerManagerPlayBackStoppedWithTarget:)]) {
[self.delegate DKAudioPlayerManagerPlayBackStoppedWithTarget:self];
}
}
}else if (reason == AVAudioSessionInterruptionTypeEnded){
//中断结束
if (!self.isPlaying) {
[self play];
if ([self.delegate respondsToSelector:@selector(DKAudioPlayerManagerPlayBackBeginWithTarget:)]) {
[self.delegate DKAudioPlayerManagerPlayBackBeginWithTarget:self];
}
}
}
通知处理
//线路切换监听
AVAudioSessionRouteChangeReason reason1 = [userInfo[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
//旧音频设备中断原因
if (reason1 == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
//线路描述信息
AVAudioSessionRouteDescription *previousRoute = userInfo[AVAudioSessionRouteChangePreviousRouteKey];
//第一个输出接口并判断是否是耳机接口
AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
NSString *portType = previousOutput.portType;
if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
[self stop];
//输出到有线耳机
if ([self.delegate respondsToSelector:@selector(DKAudioPlayerManagerPlayBackStoppedWithTarget:)]) {
[self.delegate DKAudioPlayerManagerPlayBackStoppedWithTarget:self];
}
}
}
AVAudioRecorder录制音频
初始化AVAudioRecorder实例(1.用于表示音频流写入文件的本地URL,2.包含用于配置录音会话健值信息的字典,3.用于捕获初始化阶段各种错误饿NSError指针)
prepareToPlay,这个方法执行底层Audio Queue初始化的必要过程。该方法还在URL参数指定的位置创建一个文件,将录制启动时的延迟降到最低
音频格式(指定的音频格式一定要和URL参数定义的文件类型兼容)
- kAudioFormatLinearPCM
- kAudioFormatMPEG4AAC
- kAudioFormatAppleLossless
- kAudioFormatAppleIMA4
- kAudioFormatilBC
- kAudioFormatULaw
采样率 - AVSampleRateKey定义录音器的采样率。采样率定义了对输入的模拟音频信号每一秒的采样数。对音频的质量以及最终文件大小起到重要作用。尽量使用标准的采样率(8000、16000、22050、44100)
通道数 - AVNumberOfChannelsKey用于定义记录音频内容的通道数。1意味着使用单声道,2意味着立体声录制。
指定格式的键 - 处理Linear PCM或压缩音频格式时,可以定义一些其他指定格式的键
配置音频会话 - AVAudioSessionCategoryPlayAndRecord这个分类既可以录音有需要对外播放
currentTime属性,因为该属性不可见,所以无法使用KVO来监听
初始化
-(instancetype)init{
if (self = [super init]) {
NSURL *url = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"memo.caf"]];
NSDictionary *settings = @{
AVFormatIDKey: @(kAudioFormatAppleIMA4),
AVSampleRateKey:@441000.0f,
AVNumberOfChannelsKey:@1,
AVEncoderBitDepthHintKey:@16,
AVEncoderAudioQualityKey:@(AVAudioQualityMedium)
};
NSError *error = nil;
self.recorder = [[AVAudioRecorder alloc]initWithURL:url settings:settings error:&error];
if (self.recorder) {
self.recorder.delegate = self;
self.recorder.meteringEnabled = YES;
[self.recorder prepareToRecord];
}else{
if (self.delegate && [self.delegate respondsToSelector:@selector(DKAudioRecorderManagerDidFailedWithError:)]) {
[self.delegate DKAudioRecorderManagerDidFailedWithError:error];
}
}
}
return self;
}
Audio Metering
可以读取音频的平均分贝(averagePowerForChannel:)和峰值分贝(peakPowerForChannel:),[-160dB, 0db] 最小/禁音分贝-- 最大分贝
在读取之前需要设置录音器的meteringEnabled属性才支持对音频进行测量