音频录制需要的权限,需要在plist文件中设置:Privacy - Microphone Usage Description

AVAudioRecorder视频录制,AVAudioPlayer视频播放,AVAudioSession我们也是需要了解的,通过它可以实现对App当前上下文音频资源的控制,比如插拔耳机、接电话、是否和其他音频数据混音等,经常我们遇到的一些问题登录,如果有问题,或者又补充,可以联系我,应该常用的转态我都覆盖了的

 

#import <AVFoundation/AVFoundation.h>
#import <Masonry.h>
@interface AVAudioVC ()<AVAudioRecorderDelegate>
@property(nonatomic,strong)AVAudioRecorder *audioRecorder;//音频录制

@property(nonatomic,strong)UIButton *audioRecorderBeginBtn;//音频录制开始
@property(nonatomic,strong)UIButton *audioRecorderPauseBtn;//暂停录音
@property(nonatomic,strong)UIButton *audioRecorderStopBtn;//音频停止录制
/**AVAudioSession 我们也是需要了解的,通过它可以实现对App当前上下文音频资源的控制,比如插拔耳机、接电话、是否和其他音频数据混音等,经常我们遇到的一些问题,比如下面的这些:
 1、是进行录音还是播放?
 
 2、当系统静音键按下时该如何表现?
 
 3、是从扬声器还是从听筒里面播放声音?
 
 4、插拔耳机后如何表现?
 
 5、来电话/闹钟响了后如何表现?
 
 6、其他音频App启动后如何表现?*/
@property (nonatomic, strong) AVAudioSession *session;
@property (nonatomic, strong) AVAudioPlayer *player; //播放器
@end

@implementation AVAudioVC

-(AVAudioSession *)session{
    if(!_session){
        //AVAudioSession是一个单例 实例化
        _session =[AVAudioSession sharedInstance];
        
        NSError *sessionError;
      
        
        if (_session == nil) {
            
            NSLog(@"Error creating session: %@",[sessionError description]);
            
        }else{
            //激活session
            [_session setActive:YES error:nil];
            
        }
    }
    return _session;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setView];
    
    //设备改变
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRouteChange:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
    //当前的流程被中断了
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
}

//拔出耳机等
- (void)handleRouteChange:(NSNotification *)noti {
    
    NSDictionary *info = noti.userInfo;
    AVAudioSessionRouteChangeReason reason = [info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
    /**
     AVAudioSessionRouteChangeReasonUnknown :未知原因
     AVAudioSessionRouteChangeReasonNewDeviceAvailable :有新设备可用
     AVAudioSessionRouteChangeReasonOldDeviceUnavailable :老设备不可用
     AVAudioSessionRouteChangeReasonCategoryChange :类别变了
     AVAudioSessionRouteChangeReasonOverride :app重置了输出设置
     AVAudioSessionRouteChangeReasonWakeFromSleep :从睡眠转态呼醒
     AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory :当前的category下没有合适的设备
     AVAudioSessionRouteChangeReasonRouteConfigurationChange :router的配置改变了
     */
    //旧音频设备断开
    if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
        
        //获取上一线路描述信息并获取上一线路的输出设备类型
        AVAudioSessionRouteDescription *previousRoute = info[AVAudioSessionRouteChangePreviousRouteKey];
        AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
        NSString *portType = previousOutput.portType;
        
        if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
            
            //在这里暂停播放
        }
    }
}
/*监听系统中断音频播放
来电暂停
QQ 微信语音暂停
其他音乐软件占用
 */
- (void)audioInterruption:(NSNotification *)noti {
    
    NSDictionary *info = noti.userInfo;
    AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    
    if (type == AVAudioSessionInterruptionTypeBegan) {
        //表示中断开始,我们应该暂停播放和采集,取值为AVAudioSessionInterruptionTypeEnded表示中断结束,我们可以继续播放和采集。
    } else {
        //当前只有一种值AVAudioSessionInterruptionOptionShouldResume表示此时也应该恢复继续播放和采集。
        AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
        if (options == AVAudioSessionInterruptionOptionShouldResume) {
            
        }
    }
}


/**
 所有的控件初始化
 */
-(void)setView{
    
    //音频录制
    UIView *audioRecorderView = [UIView new];
    audioRecorderView.layer.cornerRadius = 5.0f;
    audioRecorderView.layer.borderColor = [UIColor blackColor].CGColor;
    audioRecorderView.layer.borderWidth = 1.0f;
    [self.view addSubview:audioRecorderView];
    audioRecorderView.backgroundColor = [UIColor redColor];
    [audioRecorderView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.offset(90);
        make.left.offset(12);
        make.right.offset(-12);
        make.height.offset(74);
    }];
    
    
    
    UILabel *audioRecorderTitleLab = [UILabel new];
    audioRecorderTitleLab.text = @"音频录制";
    [audioRecorderView addSubview:audioRecorderTitleLab];
    [audioRecorderTitleLab mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.offset(12);
        make.height.offset(30);
    }];
    
    
    self.audioRecorderBeginBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.audioRecorderBeginBtn setTitle:@"开始" forState:UIControlStateNormal];
    [self.audioRecorderBeginBtn addTarget:self action:@selector(audioRecorderBeginBtnClick) forControlEvents:UIControlEventTouchUpInside];
    [audioRecorderView addSubview:self.audioRecorderBeginBtn];
    [self.audioRecorderBeginBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(audioRecorderTitleLab.mas_bottom);
        make.left.offset(12);
        make.bottom.offset(0);
        make.width.offset(80);
    }];
    
    self.audioRecorderPauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.audioRecorderPauseBtn setTitle:@"暂停" forState:UIControlStateNormal];
    [self.audioRecorderPauseBtn setTitle:@"开始" forState:UIControlStateSelected];
    
    [self.audioRecorderPauseBtn addTarget:self action:@selector(audioRecorderPauseBtnClick) forControlEvents:UIControlEventTouchUpInside];
    [audioRecorderView addSubview:self.audioRecorderPauseBtn];
    [self.audioRecorderPauseBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.audioRecorderBeginBtn.mas_right).offset(12);
        make.size.equalTo(self.audioRecorderBeginBtn);
        make.bottom.equalTo(self.audioRecorderBeginBtn.mas_bottom);
    }];

    
    
    self.audioRecorderStopBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.audioRecorderStopBtn setTitle:@"停止" forState:UIControlStateNormal];
    [self.audioRecorderStopBtn addTarget:self action:@selector(audioRecorderStopBtnClick) forControlEvents:UIControlEventTouchUpInside];
    [audioRecorderView addSubview:self.audioRecorderStopBtn];
    [self.audioRecorderStopBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.audioRecorderPauseBtn.mas_right).offset(12);
        make.size.equalTo(self.audioRecorderBeginBtn);
        make.bottom.equalTo(self.audioRecorderBeginBtn.mas_bottom);
    }];
    
    
    //音频播放
    
    

}
/**
 录音开始
 */
-(void)audioRecorderBeginBtnClick{
    /**
     一下是两个初始化的方法,两个方法差不多,NSDictionary是配置信息
    - (nullable instancetype)initWithURL:(NSURL *)url settings:(NSDictionary<NSString *, id> *)settings error:(NSError **)outError;
    - (nullable instancetype)initWithURL:(NSURL *)url format:(AVAudioFormat *)format error:(NSError **)outError API_AVAILABLE(macos(10.12), ios(10.0), watchos(4.0)) API_UNAVAILABLE(tvos);
     
     准备开始录音
     - (BOOL)prepareToPlay;
     暂停录音
     - (void)pause;
     停止录音
     - (void)stop;
     
     */
    if(self.audioRecorderBeginBtn.selected){
        //已经开始了
        NSLog(@"-------------------------------录音已经开始了,不需要重复录制");
       
    }else{
        
        /**
         设置session属性
         AVAudioSessionCategoryAmbient:  当用户锁屏或者静音的时候静音,支持混音,只支持播放
         AVAudioSessionCategorySoloAmbient:  当用户锁屏或者静音的时候静音,不支持混音,只支持播放
         AVAudioSessionCategoryPlayback: 当用户锁屏或者静音的时候不静音,不支持混音,只支持播放
         AVAudioSessionCategoryRecord :当用户锁屏或者静音的时候不静音,不支持混音,只支持录音
         AVAudioSessionCategoryPlayAndRecord:当用户锁屏或者静音的时候不静音,支持混音,可以录音和播放
         AVAudioSessionCategoryAudioProcessing:都不支持
         AVAudioSessionCategoryMultiRoute:当用户锁屏或者静音的时候不静音,不支持混音,可以录音和播放
         */
        [self.session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
        
        
        //开始
        self.audioRecorder = [[AVAudioRecorder alloc]initWithURL:[self getSavePath] settings:[self getAudioSetting] error:nil];
        self.audioRecorder.delegate = self;
//        self.audioRecorder.meteringEnabled = YES;//开启音频测量
        [self.audioRecorder prepareToRecord];//
        [self.audioRecorder record];
        
//
        [self.audioRecorder updateMeters];
        [self.audioRecorder averagePowerForChannel:0];//指定频道分贝值
        [self.audioRecorder peakPowerForChannel:0];//平均分贝值
        NSLog(@"-------------------------------录音已经开始了  %@",self.audioRecorder);
    }
    self.audioRecorderBeginBtn.selected = YES;
    
    
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         NSLog(@"isRecording:1----------------------------------------:%d",self.audioRecorder.isRecording);
    });
}


/*
 音频的存储url
 */
-(NSURL *)getSavePath{
    NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    urlStr = [urlStr stringByAppendingPathComponent:@"myRecord.caf"];
    NSLog(@"file path:%@",urlStr);
    NSURL *url=[NSURL fileURLWithPath:urlStr];
    return url;
}
/*
 @return 录音设置
 */
-(NSDictionary *)getAudioSetting{
    NSMutableDictionary *dicM=[NSMutableDictionary dictionary];
    //设置录音格式
    [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
    //设置录音采样率,8000是电话采样率,对于一般录音已经够了
    [dicM setObject:@(8000) forKey:AVSampleRateKey];
    //设置通道,这里采用单声道
    [dicM setObject:@(1) forKey:AVNumberOfChannelsKey];
    //每个采样点位数,分为8、16、24、32
    [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];
    //是否使用浮点数采样
    [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
    //录音质量
    [dicM setObject:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];
    
    //....其他设置等
    return dicM;
}


/**
 录音暂停
 */
-(void)audioRecorderPauseBtnClick{
    if(!self.audioRecorder ){
        return;
    }

    if(self.audioRecorderPauseBtn.selected){
        //录音音频开始
         [self.audioRecorder record];
    }else{
        //录音音频暂停
         [self.audioRecorder pause];
    }
    self.audioRecorderPauseBtn.selected = !self.audioRecorderPauseBtn.selected;
}


/**
 音频停止录制
 */
-(void)audioRecorderStopBtnClick{
   
    //是否还在录
    [self.audioRecorder stop];
    self.audioRecorderBeginBtn.selected = NO;

}


#pragma mark AVAudioRecorderDelegate

/* audioRecorderDidFinishRecording:successfully: is called when a recording has been finished or stopped. This method is NOT called if the recorder is stopped due to an interruption. */
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
    //录音完成
    NSLog(@"--------------:录音完成");
    
//    AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:[self getSavePath] error:nil];
//    if ([audioPlayer prepareToPlay] == YES &&
//        [audioPlayer play] == YES) {
//        NSLog(@"--------------------------------:开始播放");
//    }else{
//         NSLog(@"--------------------------------:失败");
//    }

    
}

/* if an error occurs while encoding it will be reported to the delegate. */
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError * __nullable)error{
    NSLog(@"--------------:录音编码发生错误");
}


/**
 音频播放
 */
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.audioRecorder stop];
    self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:[self getSavePath] error:nil];
//    NSLog(@"%li",self.player.data.length/1024);
    [self.session setCategory:AVAudioSessionCategoryPlayback error:nil];
    
    if([self.player prepareToPlay] == YES && [self.player play] == YES){
        NSLog(@"-------------------:播放");
    }else{
         NSLog(@"-------------------:停止");
    }
  
    
}


-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


@end