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

2021-04-28  本文已影响0人  DEMOKING

前言

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
上一篇下一篇

猜你喜欢

热点阅读