概述
参看文章 写的很用心
音频播放和录制
1、音频会话
音频会话分类
Category | 播放类型 | 后台播放 | 静音或屏幕关闭 | 音频输入 | 音频输出 | 作用 |
---|---|---|---|---|---|---|
AVAudioSessionCategoryAmbient | 播放混合 | 有影响 | 支出 | 游戏背景音乐 | ||
AVAudioSessionCategorySoloAmbient(默认) | 独占播放 | 有影响 | 支持 | 微信中播放语音 | ||
AVAudioSessionCategoryPlayback | 可选 | 支持 | 支持 | 音频播放器 | ||
AVAudioSessionCategoryRecord | 独占录音 | 支持 | 支持 | 微信中录制语音 | ||
AVAudioSessionCategoryPlayAndRecord | 可选 | 支持 | 支持 | 支持 | 微信语音聊天 | |
AVAudioSessionCategoryAudioProcessing | – | – | – | 硬件解码音频 | ||
AVAudioSessionCategoryMultiRoute | 支持 | 支持 | 多设备输入输出 |
上述分类所提供的几种场景的行为可以满足大部分程序的需要,如果需要更复杂的功能,上述其中一种分类可以通过使用options 和 modes 方法进一步自定义开发.
激活音频会话
/**
激活音频会话
*/
-(void)activeAudioSessionCagegory{
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *err = nil;
// 注意:
// 如果分类允许后台播放, 则应该打开Capabilities 中 background Modes, 继而勾选打开后台播放选项
if(![audioSession setCategory:AVAudioSessionCategoryPlayback error:&err]){
NSLog(@"category err: %@",err);
}
if(![audioSession setActive:YES error:&err]){
NSLog(@"active err: %@ ",err);
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self activeAudioSessionCagegory];
return YES;
}
如果分类允许后台播放,则应该打开 Capabilities 中的 Background Modes 继而勾选后台播放音频选项
2、音频播放
除非需要从网络流中播放音频、需要访问原始音频样本,或者非常低的时延,否则**AVAudioPlayer ** 都能胜任.
-(void)initSetupPlayer{
NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"" withExtension:@""];
NSError *err = nil;
// 注意: player 必须使用强指针引用
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:&err];
if (self.player != nil) {
self.player.numberOfLoops = -1; // 循环播放
// 说明: prepareToPlay 方法是可选的, 在调用 player 的 play 方法前 也会自动调用, 作用是取得需要的音频硬件并预加载
// Audio Queue 的缓冲区, 降低调用 play 方法后的延时
[self.player prepareToPlay]; // 缓冲
}
}
-(void)play{
[self.player play];
}
/**
通过pause 和 stop 方法停止的音频会继续播放.
最主要的区别在底层处理上, 调用 stop 方法会撤销调用 prepareToPlay时所作的设置, 而调用 pause 方法则不会.
*/
-(void)pause{
[self.player pause];
}
-(void)stop{
[self.player stop];
self.player.currentTime = 0.0f;
}
/**
音量 0~1
*/
-(void)voiceSlider:(UISlider *)slider{
self.player.volume = slider.value;
}
/**
声道 -1~1
*/
-(void)vocalTractSlider:(UISlider *)slider{
self.player.pan = slider.value * 2.0 -1.0;
}
/**
速率 0.5 ~ 2
*/
-(void)speedSlider:(UISlider *)slider{
self.player.rate = slider.value * 1.5 + 0.5;
// 注意:
// 如果要改变速率, 在初始化AVAudioPlayer 时, 应该做出如下设置
// self.player.enableRate = YES;
}
3、处理中断事件
当有电话呼入、闹铃响起时,播放中的音频会慢慢消失和暂停,但是终止通话后,播放、停止按钮的控件和音频的播放没有恢复, 为了优化用户体验,需要监听这些事件,并作出处理.
- 添加中断监听通知
- (void)viewDidLoad {
[super viewDidLoad];
// 添加中断通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(udioSessionInterruptionNotice:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
}
-(void)udioSessionInterruptionNotice:(NSNotification *)notice{
NSDictionary *infoDic = notice.userInfo;
AVAudioSessionInterruptionType interruptType = [infoDic[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
// 判断中断的类型
if(interruptType == AVAudioSessionInterruptionTypeBegan){
// 中断开始, 设置停止音乐
[self.player pause];
}
else{
// 中断结束, 判断是否允许继续播放
AVAudioSessionInterruptionOptions interruptOption = [infoDic[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
if (interruptOption == AVAudioSessionInterruptionOptionShouldResume) {
// 允许继续播放, 则继续播放
[self.player play];
}
}
}
4、对线路改变的响应
播放音频期间插入耳机, 音频输出线路变成耳机插孔并继续播放. 断开耳机连接,音频线路再次回到设备的内置扬声器播放. 虽然线路变化和预期一样,不过按照苹果官方文档,认为该音频应该处于静音状态.
// 音频线路改变通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionRouteChangeNotice:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
/**
音频线路变化通知
播放音频期间插入耳机, 音频输出线路变成耳机插孔并继续播放.
断开耳机连接,音频线路再次回到设备的内置扬声器播放.
虽然线路变化和预期一样,不过按照苹果官方文档,认为该音频应该处于静音状态.
*/
-(void)audioSessionRouteChangeNotice:(NSNotification *)notice{
NSDictionary *infoDic = notice.userInfo;
AVAudioSessionRouteChangeReason routerChangeReason = [infoDic[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
if (routerChangeReason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
AVAudioSessionRouteDescription *previousRoute = infoDic[AVAudioSessionRouteChangePreviousRouteKey];
AVAudioSessionPortDescription *previousOutput = [previousRoute.outputs firstObject];
if ([previousOutput.portType isEqualToString:AVAudioSessionPortHeadphones]) {
// 停止播放音乐
[self.player stop];
}
}
}
5、 音频录制
一般情况存在录音功能,必然会存在播放功能,所以不能使用默认的录制的音频会话,应该使用既可以录制又能播放的AVAudioSessionCategoryPalyAndRecord
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *fileUrl = [NSURL fileURLWithPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"record.caf"]];
/**
在录制过程中,Core Audio Format (caf) 通常是最好的容器格式,因为他和内容无关可以保存Core Audio 支持的任何音频格式.
在设置字典中是定的键值信息也是值得讨论一番的
*/
NSDictionary *settting = @{
AVFormatIDKey : @(kAudioFormatAppleIMA4),
AVSampleRateKey : @(22050.0f),
AVNumberOfChannelsKey : @(1)
};
NSError *err = nil;
self.recorder = [[AVAudioRecorder alloc] initWithURL:fileUrl settings:settting error:&err];
if (self.recorder) {
self.recorder.delegate = self;
[self.recorder prepareToRecord];
}
}
-(void)startRecord{
[self.recorder record];
}
-(void)recordFinish{
[self.recorder stop];
}
#pragma mark- AVAudioRecorderDelegate
/**
音频录制完成调用
*/
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
if(flag){
// 一般把录制好的音频复制或者剪切到目录的文件夹下
NSURL *sourceUrl = self.recorder.url;
NSURL *targetUrl = [NSURL fileURLWithPath:@"目标文件夹/音频文件名.caf"];
NSError *err = nil;
[[NSFileManager defaultManager] copyItemAtURL:sourceUrl toURL:targetUrl error:&err];
}
}
音频格式
AVFormatIDKey 定义了写入内容的音频格式,下面是常用格式
- kAudioFormatLinearPCM : 将未压缩的音频流写入到文件中. 保真度最高,文件也最大.
- kAudioFormatMPEG4AAC (AAC) 或 kAudioFormat-AppleIMA4 (Apple IMA4) 文件显著缩小还能保证高质量音频.
采样率
**AVSampleRateKey **定义了录音器的采样率, 采样率定义了对输入的模拟信号每一秒的采样数. 采样率越高,越能得到高质量的内容,不过文件也越大,标准的采样率是: 8000, 16000,22050,44100
通道数
AVNumberOfChannelsKey 定义了记录音频内容的通道数.默认值1 是单声道录制, 2 是立体声录制. 除非使用外部硬件录制,否则应该创建单声道录制.
6、 音频测量
AVAudioPlayer 和 AVAudioRecorder 中最使用的功能就是对音频进行测量. AVAudio Metering 可以读取音频的平均平均分贝和峰值分贝数据, 并使用这些数据以可视化方式将声音大小呈现给用户.
首先在初始化 AVAudioPlayer 或 AVAudioRecorder 时应做出如下设置
self.recorder.meteringEnabled = YES;
self.player.meteringEnabled = YES;
点击音频播放或者音频录制, 开始测量
-(void)startMeterTimer{
[self.link invalidate];
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateMeter)];
link.frameInterval = 4; // 间隔时间为刷帧率的 1/4
[link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
self.link = link;
}
-(void)stopMeterTimer{
[self.link invalidate];
self.link = nil;
}
/**
返回用于表示声音分贝(dB)等级的浮点值,这个值的范围是 -160dB ~ 0dB
*/
-(void)updateMeter{
[self.recorder updateMeters];
CGFloat num1 = [self.recorder averagePowerForChannel:0];
CGFloat num2 = [self.recorder peakPowerForChannel:0];
NSLog(@"录音平均分贝: %f, 峰值分贝: %f",num1, num2);
// [self.player updateMeters]; // 刷新
//
// CGFloat num1 = [self.player averagePowerForChannel:0];
// CGFloat num2 = [self.player peakPowerForChannel:0];
//
// NSLog(@"播放平均分贝:%f, 峰值分贝:%f", num1, num2);
}
上面方法都会返回用于表示声音分贝(dB)等级的浮点值,这个值的范围是 -160dB ~ 0dB
网上录音后播放声音小解决方案
1)录音的时候
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error: nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
UInt32 doChangeDefault = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(doChangeDefault), &doChangeDefault);
2)播放的时候
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *err = nil;
[audioSession setCategory :AVAudioSessionCategoryPlayback error:&err];
这样就行了。
最后
以上就是迷路小松鼠为你收集整理的(二)AVFoundation 音频播放和录制的全部内容,希望文章能够帮你解决(二)AVFoundation 音频播放和录制所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复