美文网首页
iOS简单的AVAudioRecorder录音器解决方案

iOS简单的AVAudioRecorder录音器解决方案

作者: DEMOKING | 来源:发表于2021-04-28 13:57 被阅读0次

    前言

    AVAudioRecorder的简单用法,通过AVAudioRecorder进行录音。将录制好的音频保存的沙盒中。
    内容简单,牛绕行。

    介绍

    DEMO主要特点如下:
    1、设置最大录音时间;
    2、设置最小录音时间;
    3、文件保存为aac格式;
    4、通过沙盒直接获取录音文件;
    5、代理回调获取录音时的音量大小;
    6、判断麦克风权限等;

    核心代码

    1、关于录音器的创建

    - (BOOL)createRecorder {
        if (!_recorder) {
            if (!_recorderParamsDictionary) {
                return NO;
            }
            NSError *error;
            _recorder = [[AVAudioRecorder alloc] initWithURL:[self fileURL]
                                                    settings:self.recorderParamsDictionary
                                                       error:&error];
            _recorder.delegate = self;
            [_recorder prepareToRecord];
            if (!error) {
                return YES;
            }else {
                return NO;
            }
        }
        return YES;
    }
    

    2、设置采样率等

    // 设置录音格式 @(kAudioFormatMPEG4AAC)
    - (void)setObjectFormat:(NSObject *)format // 设置录音格式 @(kAudioFormatMPEG4AAC)
                       rate:(NSObject *)rate // 设置录音采样率,8000是电话采样率 @(8000)
                    channel:(NSObject *)channel // 设置通道 @(1)
          linearPCMBitDepth:(NSObject *)linearPCMBitDepth // 每个采样点位数 @(8)
            linearPCMIsFloat:(NSObject *)linearPCMIsFloat /*是否使用浮点数采样 @(YES)*/ {
        //设置录音格式
        [self.recorderParamsDictionary setObject:format forKey:AVFormatIDKey];
        self.audioFormatID = (AudioFormatID)[[NSString stringWithFormat:@"%@",format] intValue];
        //设置录音采样率,8000是电话采样率,对于一般录音已经够了
        [self.recorderParamsDictionary setObject:rate forKey:AVSampleRateKey];
        //设置通道,这里采用单声道
        [self.recorderParamsDictionary setObject:channel forKey:AVNumberOfChannelsKey];
        //每个采样点位数,分为8、16、24、32
        [self.recorderParamsDictionary setObject:linearPCMBitDepth forKey:AVLinearPCMBitDepthKey];
        //是否使用浮点数采样
        [self.recorderParamsDictionary setObject:linearPCMIsFloat forKey:AVLinearPCMIsFloatKey];
    }
    

    以上是核心代码,通过自己的理解,加以修改,形成自己的录音器。

    .h 中的所有代码

    //
    //  DKRecordManager.h
    //
    //  Created by 王亚振 on 2019/10/29.
    //  Copyright © 2019. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    typedef enum : NSUInteger {
        DKRecordManagerAudioAuthStatusUnknow,
        DKRecordManagerAudioAuthStatusNoAuth,
        DKRecordManagerAudioAuthStatusAuth
    } DKRecordManagerAudioAuthStatus;
    
    @protocol DKRecordManagerDelegate <NSObject>
    @optional
    /// 时间进度条 每1秒反馈一次
    /// @param time time
    - (void)recordManagerDelegateTimeProgress:(NSInteger)time;
    /// level进度条 每0.5秒反馈一次
    /// @param level level
    - (void)recordManagerDelegateLevel:(NSInteger)level;
    /// 录音结束,倒计时时间
    /// @param end end
    - (void)recordManagerDelegateTimeEnd:(BOOL)end;
    /// 录音取消,倒计时时间
    /// @param cancel cancel
    - (void)recordManagerCancel:(BOOL)cancel;
    /// 录音结束
    /// @param success YES||NO
    - (void)recordManagerDelegateRecordEnd:(BOOL)success timeout:(BOOL)timeout duration:(NSInteger)duration;
    /// 录音结束
    /// @param success YES||NO
    - (void)recordManagerDelegateRecordEnd:(BOOL)success timeLess:(BOOL)timeLess duration:(NSInteger)duration;
    /// 录音音量 0~1
    /// @param meter meter
    - (void)recordManagerDelegateMeter:(CGFloat)meter;
    @end
    
    @interface DKRecordManager : NSObject
    
    + (DKRecordManager *)sharedManager;
    @property (assign, nonatomic) id <DKRecordManagerDelegate> delegate;
    
    /// 音量检测
    /// @param able able
    - (void)setMeteringEnable:(BOOL)able;
    /// 设置最大时间
    /// @param time 最大时间
    - (void)setMaxRecordTime:(NSInteger)time;
    /// 设置最小时间
    /// @param time 最小时间
    - (void)setMinRecordTime:(NSInteger)time;
    /// 通配参数
    /// @param format format
    /// @param rate rate
    /// @param channel channel
    /// @param linearPCMBitDepth linearPCMBitDepth
    /// @param linearPCMIsFloat linearPCMIsFloat
    - (void)setObjectFormat:(NSObject *)format // 设置录音格式 @(kAudioFormatMPEG4AAC)
                       rate:(NSObject *)rate // 设置录音采样率,8000是电话采样率 @(8000)
                    channel:(NSObject *)channel // 设置通道 @(1)
          linearPCMBitDepth:(NSObject *)linearPCMBitDepth // 每个采样点位数 @(8)
           linearPCMIsFloat:(NSObject *)linearPCMIsFloat; // 是否使用浮点数采样 @(YES)
    /// 必须先设置参数
    - (BOOL)createRecorder;
    /// 删除录音recorder
    - (void)removeRecorder;
    /// 开始录音
    - (void)startRecorder;
    /// 结束录音
    - (void)stopRecorder;
    /// 移除录音文件
    - (BOOL)removeRecordFile;
    /// 获取录音文件
    - (NSURL *)getFileURL;
    /// 移除代理
    - (void)removeDelegate;
    /// 取消
    - (void)cancelRecorder;
    
    #pragma mark --
    #pragma mark -- PRIVATE 工具类
    
    /// 获取权限
    + (DKRecordManagerAudioAuthStatus)getAudioAuth;
    @end
    
    

    .m 中的所有代码

    //
    //  DKRecordManager.m
    //
    //  Created by 王亚振 on 2019/10/29.
    //  Copyright © 2019 All rights reserved.
    //
    
    #import "DKRecordManager.h"
    #import <AVFoundation/AVFoundation.h>
    
    #define DK_FILE_CACHE_NAME @"/DK_FILE_CACHE_NAME"
    @interface DKRecordManager ()<AVAudioRecorderDelegate>
    /// 录音时长
    @property (assign, nonatomic) NSInteger currentRecordTime;
    /// 录音最大时长
    @property (assign, nonatomic) NSInteger maxRecordTime;
    /// 录音最小时长 0 无限制
    @property (assign, nonatomic) NSInteger minRecordTime;
    /// 录音时长定时器
    @property (strong, nonatomic) dispatch_source_t waitingTimer;
    /// 录音音量监测定时器
    @property (strong, nonatomic) dispatch_source_t meterTimer;
    /// 录音对象
    @property (strong, nonatomic) AVAudioRecorder *recorder;
    /// 录音参数集
    @property (strong, nonatomic) NSMutableDictionary *recorderParamsDictionary;
    /// 录音格式 format kAudioFormatMPEG4AAC
    @property (assign, nonatomic) AudioFormatID audioFormatID;
    /// 监听时间是否结束
    @property (assign, nonatomic) BOOL timeout;
    /// 是否监听l音量
    @property (assign, nonatomic) BOOL meter;
    @property (assign, nonatomic) BOOL isCancel;
    @end
    
    @implementation DKRecordManager
    
    + (DKRecordManager *)sharedManager {
        static DKRecordManager *sharedManager = nil;
        static dispatch_once_t predicate;
        dispatch_once(&predicate, ^{
            sharedManager = [[DKRecordManager alloc] init];
        });
        return sharedManager;
    }
    
    #pragma mark --
    #pragma mark -- PRIVATE 录音区
    
    /// 必须先设置参数
    - (BOOL)createRecorder {
        if (!_recorder) {
            if (!_recorderParamsDictionary) {
                return NO;
            }
            NSError *error;
            _recorder = [[AVAudioRecorder alloc] initWithURL:[self fileURL]
                                                    settings:self.recorderParamsDictionary
                                                       error:&error];
            _recorder.delegate = self;
            [_recorder prepareToRecord];
            if (!error) {
                return YES;
            }else {
                return NO;
            }
        }
        return YES;
    }
    /// 删除录音recorder
    - (void)removeRecorder {
        [_recorder stop];
        _recorder = nil;
        _recorderParamsDictionary = nil;
        [self removeRecordFile];
    }
    /// 开始录音
    - (void)startRecorder {
        self.isCancel = NO;
        if (self.recorder == nil) {
            return;
        }
        
        // 音量
        self.recorder.meteringEnabled = self.meter;
        if (self.meter) {
            [self startMeterTime];
        }
        
        // 录音时长
        self.currentRecordTime = 0;
        
        // 录音
        NSError *error;
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
        [audioSession setActive:YES error:nil];
        if (!error) {
            self.timeout = NO;
            [self.recorder record];
            [self startTime];
        }else {
            NSLog(@"无法录音");
        }
    }
    /// 结束录音
    - (void)stopRecorder {
        [self.recorder stop];
        [self stopTimer];
        [self stopMeterTimer];
        if ([self.delegate respondsToSelector:@selector(recordManagerDelegateTimeProgress:)]) {
            [self.delegate recordManagerDelegateTimeProgress:0];
        }
        if ([self.delegate respondsToSelector:@selector(recordManagerDelegateLevel:)]) {
            [self.delegate recordManagerDelegateLevel:0];
        }
    }
    /// 移除录音文件
    - (BOOL)removeRecordFile  {
        [self.recorder stop];
        return [self.recorder deleteRecording];
    }
    /// 获取录音文件
    - (NSURL *)getFileURL {
        return [self fileURL];
    }
    /// 移除代理
    - (void)removeDelegate {
        self.delegate = nil;
    }
    /// 监测音量
    - (void)meterTimerMethod {
        if (!self.recorder.meteringEnabled) {
            return;
        }
        // 更新测量值
        [self.recorder updateMeters];
        // 取得第一个通道的音频,注意音频强度范围时-160到0
        float power = [self.recorder averagePowerForChannel:0];
        CGFloat progress = (1.0 / 160.0) * (power + 160.0);
        NSLog(@"meterTimerMethod == %.2f",progress);
        if ([self.delegate respondsToSelector:@selector(recordManagerDelegateMeter:)]) {
            [self.delegate recordManagerDelegateMeter:progress];
        }
    }
    /// 取消
    - (void)cancelRecorder {
        self.isCancel = YES;
        [self stopRecorder];
    }
    
    #pragma mark --
    #pragma mark -- PRIVATE 参数设置区
    /// 音量检测
    /// @param able able
    - (void)setMeteringEnable:(BOOL)able {
        self.meter = able;
    }
    - (void)setMaxRecordTime:(NSInteger)maxRecordTime {
        if (maxRecordTime == 0) {
            _maxRecordTime = INTMAX_MAX;
        }else if (time < 0) {
            _maxRecordTime = -1;
            NSLog(@"无效时间");
        }else {
            _maxRecordTime = maxRecordTime;
        }
    }
    - (void)setMinRecordTime:(NSInteger)time {
        if (time == 0) {
            _minRecordTime = INTMAX_MIN;
        }else if (time < 0) {
            _minRecordTime = -1;
            NSLog(@"无效时间");
        }else {
            _minRecordTime = time;
        }
    }
    // 设置录音格式 @(kAudioFormatMPEG4AAC)
    - (void)setObjectFormat:(NSObject *)format // 设置录音格式 @(kAudioFormatMPEG4AAC)
                       rate:(NSObject *)rate // 设置录音采样率,8000是电话采样率 @(8000)
                    channel:(NSObject *)channel // 设置通道 @(1)
          linearPCMBitDepth:(NSObject *)linearPCMBitDepth // 每个采样点位数 @(8)
            linearPCMIsFloat:(NSObject *)linearPCMIsFloat /*是否使用浮点数采样 @(YES)*/ {
        //设置录音格式
        [self.recorderParamsDictionary setObject:format forKey:AVFormatIDKey];
        self.audioFormatID = (AudioFormatID)[[NSString stringWithFormat:@"%@",format] intValue];
        //设置录音采样率,8000是电话采样率,对于一般录音已经够了
        [self.recorderParamsDictionary setObject:rate forKey:AVSampleRateKey];
        //设置通道,这里采用单声道
        [self.recorderParamsDictionary setObject:channel forKey:AVNumberOfChannelsKey];
        //每个采样点位数,分为8、16、24、32
        [self.recorderParamsDictionary setObject:linearPCMBitDepth forKey:AVLinearPCMBitDepthKey];
        //是否使用浮点数采样
        [self.recorderParamsDictionary setObject:linearPCMIsFloat forKey:AVLinearPCMIsFloatKey];
    }
    - (NSMutableDictionary *)recorderParamsDictionary {
        if (!_recorderParamsDictionary) {
            _recorderParamsDictionary = [NSMutableDictionary dictionaryWithCapacity:0];
        }
        return _recorderParamsDictionary;
    }
    
    #pragma mark --
    #pragma mark -- PRIVATE 地址相关处理
    
    + (NSString *)cacheDictionary {
        NSString *cacheDictionary = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        NSLog(@"cacheDictionary -- %@",cacheDictionary);
        return cacheDictionary;
    }
    - (NSString *)appendFileName {
        NSString *address;
        if (self.audioFormatID == kAudioFormatMPEG4AAC) {
            address = @"aac";
        }
        NSString *appendFileName = [NSString stringWithFormat:@"%@%@.%@",[DKRecordManager cacheDictionary],DK_FILE_CACHE_NAME,address];
        return appendFileName;
    }
    - (NSURL *)fileURL {
        NSURL *url = [NSURL URLWithString:[self appendFileName]];
        return url;
    }
    
    #pragma mark --
    #pragma mark -- PRIVATE 权限判断
    
    /// 获取权限
    + (DKRecordManagerAudioAuthStatus)getAudioAuth {
        AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
        switch (authStatus) {
            case AVAuthorizationStatusNotDetermined:
            // 被拒绝
            {
                return DKRecordManagerAudioAuthStatusNoAuth;
            }
            break;
            case AVAuthorizationStatusRestricted:
            // 未授权,家长限制
            {
                return DKRecordManagerAudioAuthStatusNoAuth;
            }
            break;
            case AVAuthorizationStatusDenied:
            // 玩家未授权
            {
                return DKRecordManagerAudioAuthStatusUnknow;
            }
            break;
            case AVAuthorizationStatusAuthorized:
            // 玩家授权
            {
                return DKRecordManagerAudioAuthStatusAuth;
            }
            break;
            default:
                return DKRecordManagerAudioAuthStatusUnknow;
            break;
        }
    }
    
    #pragma mark --
    #pragma mark -- PRIVATE 定时器相关
    
    - (void)startTime {
        if (self.maxRecordTime == 0) {
            return;
        }
        [self stopTimer];
        __weak DKRecordManager *weakSelf = self;
        __block NSInteger time = 0;
        NSInteger timeSpace = 1;
        dispatch_queue_t queue = dispatch_get_main_queue();
        self.waitingTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
        uint64_t interval = (uint64_t)(timeSpace * NSEC_PER_SEC);
        dispatch_source_set_timer(self.waitingTimer, start, interval, 0);
        dispatch_source_set_event_handler(self.waitingTimer, ^{
            // do something
            dispatch_async(dispatch_get_main_queue(), ^{
                if (weakSelf.maxRecordTime == 0) {
                    // 时间未到,继续录音
                    if ([weakSelf.delegate respondsToSelector:@selector(recordManagerDelegateTimeProgress:)]) {
                        [weakSelf.delegate recordManagerDelegateTimeProgress:time];
                    }
                    NSLog(@"录音时间 == %ld",time);
                }else {
                    if (time > weakSelf.maxRecordTime) {
                        weakSelf.timeout = YES;
                        // 录音时间到
                        if ([weakSelf.delegate respondsToSelector:@selector(recordManagerDelegateTimeEnd:)]) {
                            [weakSelf.delegate recordManagerDelegateTimeEnd:YES];
                        }
                        weakSelf.isCancel = YES;
                        [weakSelf stopRecorder];
                    }else {
                        // 时间未到,继续录音
                        if ([weakSelf.delegate respondsToSelector:@selector(recordManagerDelegateTimeProgress:)]) {
                            [weakSelf.delegate recordManagerDelegateTimeProgress:time];
                        }
                        NSLog(@"录音时间 == %ld",time);
                    }
                }
            });
            time ++;
            weakSelf.currentRecordTime = time;
        });
        dispatch_resume(self.waitingTimer);
    }
    - (void)stopTimer {
        if (_waitingTimer) {
            dispatch_cancel(_waitingTimer);
            _waitingTimer = nil;
        }
    }
    - (void)startMeterTime {
        [self stopMeterTimer];
        __weak DKRecordManager *weakSelf = self;
        CGFloat timeSpace = 0.1;
        dispatch_queue_t queue = dispatch_get_main_queue();
        self.meterTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
        uint64_t interval = (uint64_t)(timeSpace * NSEC_PER_SEC);
        dispatch_source_set_timer(self.meterTimer, start, interval, 0);
        dispatch_source_set_event_handler(self.meterTimer, ^{
            // do something
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf meterTimerMethod];
            });
        });
        dispatch_resume(self.meterTimer);
    }
    - (void)stopMeterTimer {
        if (_meterTimer) {
            dispatch_cancel(_meterTimer);
            _meterTimer = nil;
        }
    }
    
    #pragma mark --
    #pragma mark -- DELEGATE <AVAudioRecorderDelegate>
    
    - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
        if (self.isCancel) {
            if ([self.delegate respondsToSelector:@selector(recordManagerCancel:)]) {
                [self.delegate recordManagerCancel:YES];
            }
            return;
        }
        if (self.minRecordTime > 0) {
            if (self.currentRecordTime < self.minRecordTime) {
                if ([self.delegate respondsToSelector:@selector(recordManagerDelegateRecordEnd:timeLess:duration:)]) {
                    [self.delegate recordManagerDelegateRecordEnd:flag timeLess:YES duration:self.currentRecordTime];
                }
                return;
            }else {
                if ([self.delegate respondsToSelector:@selector(recordManagerDelegateRecordEnd:timeout:duration:)]) {
                    [self.delegate recordManagerDelegateRecordEnd:flag timeout:self.timeout duration:self.currentRecordTime];
                }
            }
        }else {
            if ([self.delegate respondsToSelector:@selector(recordManagerDelegateRecordEnd:timeout:duration:)]) {
                [self.delegate recordManagerDelegateRecordEnd:flag timeout:self.timeout duration:self.currentRecordTime];
            }
        }
    }
    - (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError * __nullable)error {
        if (self.isCancel) {
            if ([self.delegate respondsToSelector:@selector(recordManagerCancel:)]) {
                [self.delegate recordManagerCancel:YES];
            }
            return;
        }
        if ([self.delegate respondsToSelector:@selector(recordManagerDelegateRecordEnd:timeout:duration:)]) {
            [self.delegate recordManagerDelegateRecordEnd:NO timeout:self.timeout duration:self.currentRecordTime];
        }
        [self stopTimer];
        [self stopMeterTimer];
    }
    
    @end
    
    

    WX20210429-135620.png

    相关文章

      网友评论

          本文标题:iOS简单的AVAudioRecorder录音器解决方案

          本文链接:https://www.haomeiwen.com/subject/liqwrltx.html