AVFoundation-播放和录制音频

目标

学习AVAudioPlayer和AVAudioRecorder类来实现音频播放及录制功能

理解音频会话

iOS默认音频会话的预配置

  • 激活了音频播放,但是音频录制未激活
  • 当用户切换响铃/静音开关到“静音”模式时,应用程序播放的所有音频都会消失
  • 当设备显示解锁屏幕时,应用程序的音频处于静音状态
  • 当应用程序播放音频时,所有后台播放的音频都会处于静音状态

音频会话分类

屏幕快照 2019-11-12 下午10.05.39.png
  • 上述分类提供满足大部分需求的常见行为,如果需要更加复杂的功能,其中一些分类通过使用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属性才支持对音频进行测量

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,069评论 4 358
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,212评论 1 287
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 105,912评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,424评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,741评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,194评论 1 206
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,553评论 2 307
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,289评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,923评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,251评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,775评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,144评论 2 249
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,698评论 3 228
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,936评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,658评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,214评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,159评论 2 258

推荐阅读更多精彩内容