前言
AVAudioRecorder的簡(jiǎn)單用法,通過(guò)AVAudioRecorder進(jìn)行錄音拍鲤。將錄制好的音頻保存的沙盒中。
內(nèi)容簡(jiǎn)單汞扎,牛繞行季稳。
介紹
DEMO主要特點(diǎn)如下:
1、設(shè)置最大錄音時(shí)間澈魄;
2景鼠、設(shè)置最小錄音時(shí)間;
3痹扇、文件保存為aac格式铛漓;
4、通過(guò)沙盒直接獲取錄音文件帘营;
5票渠、代理回調(diào)獲取錄音時(shí)的音量大小芬迄;
6问顷、判斷麥克風(fēng)權(quán)限等;
核心代碼
1禀梳、關(guān)于錄音器的創(chuàng)建
- (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杜窄、設(shè)置采樣率等
// 設(shè)置錄音格式 @(kAudioFormatMPEG4AAC)
- (void)setObjectFormat:(NSObject *)format // 設(shè)置錄音格式 @(kAudioFormatMPEG4AAC)
rate:(NSObject *)rate // 設(shè)置錄音采樣率,8000是電話采樣率 @(8000)
channel:(NSObject *)channel // 設(shè)置通道 @(1)
linearPCMBitDepth:(NSObject *)linearPCMBitDepth // 每個(gè)采樣點(diǎn)位數(shù) @(8)
linearPCMIsFloat:(NSObject *)linearPCMIsFloat /*是否使用浮點(diǎn)數(shù)采樣 @(YES)*/ {
//設(shè)置錄音格式
[self.recorderParamsDictionary setObject:format forKey:AVFormatIDKey];
self.audioFormatID = (AudioFormatID)[[NSString stringWithFormat:@"%@",format] intValue];
//設(shè)置錄音采樣率算途,8000是電話采樣率塞耕,對(duì)于一般錄音已經(jīng)夠了
[self.recorderParamsDictionary setObject:rate forKey:AVSampleRateKey];
//設(shè)置通道,這里采用單聲道
[self.recorderParamsDictionary setObject:channel forKey:AVNumberOfChannelsKey];
//每個(gè)采樣點(diǎn)位數(shù),分為8、16嘴瓤、24扫外、32
[self.recorderParamsDictionary setObject:linearPCMBitDepth forKey:AVLinearPCMBitDepthKey];
//是否使用浮點(diǎn)數(shù)采樣
[self.recorderParamsDictionary setObject:linearPCMIsFloat forKey:AVLinearPCMIsFloatKey];
}
以上是核心代碼莉钙,通過(guò)自己的理解,加以修改筛谚,形成自己的錄音器磁玉。
.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
/// 時(shí)間進(jìn)度條 每1秒反饋一次
/// @param time time
- (void)recordManagerDelegateTimeProgress:(NSInteger)time;
/// level進(jìn)度條 每0.5秒反饋一次
/// @param level level
- (void)recordManagerDelegateLevel:(NSInteger)level;
/// 錄音結(jié)束,倒計(jì)時(shí)時(shí)間
/// @param end end
- (void)recordManagerDelegateTimeEnd:(BOOL)end;
/// 錄音取消驾讲,倒計(jì)時(shí)時(shí)間
/// @param cancel cancel
- (void)recordManagerCancel:(BOOL)cancel;
/// 錄音結(jié)束
/// @param success YES||NO
- (void)recordManagerDelegateRecordEnd:(BOOL)success timeout:(BOOL)timeout duration:(NSInteger)duration;
/// 錄音結(jié)束
/// @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;
/// 音量檢測(cè)
/// @param able able
- (void)setMeteringEnable:(BOOL)able;
/// 設(shè)置最大時(shí)間
/// @param time 最大時(shí)間
- (void)setMaxRecordTime:(NSInteger)time;
/// 設(shè)置最小時(shí)間
/// @param time 最小時(shí)間
- (void)setMinRecordTime:(NSInteger)time;
/// 通配參數(shù)
/// @param format format
/// @param rate rate
/// @param channel channel
/// @param linearPCMBitDepth linearPCMBitDepth
/// @param linearPCMIsFloat linearPCMIsFloat
- (void)setObjectFormat:(NSObject *)format // 設(shè)置錄音格式 @(kAudioFormatMPEG4AAC)
rate:(NSObject *)rate // 設(shè)置錄音采樣率蚊伞,8000是電話采樣率 @(8000)
channel:(NSObject *)channel // 設(shè)置通道 @(1)
linearPCMBitDepth:(NSObject *)linearPCMBitDepth // 每個(gè)采樣點(diǎn)位數(shù) @(8)
linearPCMIsFloat:(NSObject *)linearPCMIsFloat; // 是否使用浮點(diǎn)數(shù)采樣 @(YES)
/// 必須先設(shè)置參數(shù)
- (BOOL)createRecorder;
/// 刪除錄音recorder
- (void)removeRecorder;
/// 開(kāi)始錄音
- (void)startRecorder;
/// 結(jié)束錄音
- (void)stopRecorder;
/// 移除錄音文件
- (BOOL)removeRecordFile;
/// 獲取錄音文件
- (NSURL *)getFileURL;
/// 移除代理
- (void)removeDelegate;
/// 取消
- (void)cancelRecorder;
#pragma mark --
#pragma mark -- PRIVATE 工具類
/// 獲取權(quán)限
+ (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>
/// 錄音時(shí)長(zhǎng)
@property (assign, nonatomic) NSInteger currentRecordTime;
/// 錄音最大時(shí)長(zhǎng)
@property (assign, nonatomic) NSInteger maxRecordTime;
/// 錄音最小時(shí)長(zhǎng) 0 無(wú)限制
@property (assign, nonatomic) NSInteger minRecordTime;
/// 錄音時(shí)長(zhǎng)定時(shí)器
@property (strong, nonatomic) dispatch_source_t waitingTimer;
/// 錄音音量監(jiān)測(cè)定時(shí)器
@property (strong, nonatomic) dispatch_source_t meterTimer;
/// 錄音對(duì)象
@property (strong, nonatomic) AVAudioRecorder *recorder;
/// 錄音參數(shù)集
@property (strong, nonatomic) NSMutableDictionary *recorderParamsDictionary;
/// 錄音格式 format kAudioFormatMPEG4AAC
@property (assign, nonatomic) AudioFormatID audioFormatID;
/// 監(jiān)聽(tīng)時(shí)間是否結(jié)束
@property (assign, nonatomic) BOOL timeout;
/// 是否監(jiān)聽(tīng)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 錄音區(qū)
/// 必須先設(shè)置參數(shù)
- (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];
}
/// 開(kāi)始錄音
- (void)startRecorder {
self.isCancel = NO;
if (self.recorder == nil) {
return;
}
// 音量
self.recorder.meteringEnabled = self.meter;
if (self.meter) {
[self startMeterTime];
}
// 錄音時(shí)長(zhǎng)
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(@"無(wú)法錄音");
}
}
/// 結(jié)束錄音
- (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;
}
/// 監(jiān)測(cè)音量
- (void)meterTimerMethod {
if (!self.recorder.meteringEnabled) {
return;
}
// 更新測(cè)量值
[self.recorder updateMeters];
// 取得第一個(gè)通道的音頻,注意音頻強(qiáng)度范圍時(shí)-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 參數(shù)設(shè)置區(qū)
/// 音量檢測(cè)
/// @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(@"無(wú)效時(shí)間");
}else {
_maxRecordTime = maxRecordTime;
}
}
- (void)setMinRecordTime:(NSInteger)time {
if (time == 0) {
_minRecordTime = INTMAX_MIN;
}else if (time < 0) {
_minRecordTime = -1;
NSLog(@"無(wú)效時(shí)間");
}else {
_minRecordTime = time;
}
}
// 設(shè)置錄音格式 @(kAudioFormatMPEG4AAC)
- (void)setObjectFormat:(NSObject *)format // 設(shè)置錄音格式 @(kAudioFormatMPEG4AAC)
rate:(NSObject *)rate // 設(shè)置錄音采樣率吮铭,8000是電話采樣率 @(8000)
channel:(NSObject *)channel // 設(shè)置通道 @(1)
linearPCMBitDepth:(NSObject *)linearPCMBitDepth // 每個(gè)采樣點(diǎn)位數(shù) @(8)
linearPCMIsFloat:(NSObject *)linearPCMIsFloat /*是否使用浮點(diǎn)數(shù)采樣 @(YES)*/ {
//設(shè)置錄音格式
[self.recorderParamsDictionary setObject:format forKey:AVFormatIDKey];
self.audioFormatID = (AudioFormatID)[[NSString stringWithFormat:@"%@",format] intValue];
//設(shè)置錄音采樣率时迫,8000是電話采樣率,對(duì)于一般錄音已經(jīng)夠了
[self.recorderParamsDictionary setObject:rate forKey:AVSampleRateKey];
//設(shè)置通道,這里采用單聲道
[self.recorderParamsDictionary setObject:channel forKey:AVNumberOfChannelsKey];
//每個(gè)采樣點(diǎn)位數(shù),分為8谓晌、16掠拳、24、32
[self.recorderParamsDictionary setObject:linearPCMBitDepth forKey:AVLinearPCMBitDepthKey];
//是否使用浮點(diǎn)數(shù)采樣
[self.recorderParamsDictionary setObject:linearPCMIsFloat forKey:AVLinearPCMIsFloatKey];
}
- (NSMutableDictionary *)recorderParamsDictionary {
if (!_recorderParamsDictionary) {
_recorderParamsDictionary = [NSMutableDictionary dictionaryWithCapacity:0];
}
return _recorderParamsDictionary;
}
#pragma mark --
#pragma mark -- PRIVATE 地址相關(guān)處理
+ (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 權(quán)限判斷
/// 獲取權(quán)限
+ (DKRecordManagerAudioAuthStatus)getAudioAuth {
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
switch (authStatus) {
case AVAuthorizationStatusNotDetermined:
// 被拒絕
{
return DKRecordManagerAudioAuthStatusNoAuth;
}
break;
case AVAuthorizationStatusRestricted:
// 未授權(quán)扎谎,家長(zhǎng)限制
{
return DKRecordManagerAudioAuthStatusNoAuth;
}
break;
case AVAuthorizationStatusDenied:
// 玩家未授權(quán)
{
return DKRecordManagerAudioAuthStatusUnknow;
}
break;
case AVAuthorizationStatusAuthorized:
// 玩家授權(quán)
{
return DKRecordManagerAudioAuthStatusAuth;
}
break;
default:
return DKRecordManagerAudioAuthStatusUnknow;
break;
}
}
#pragma mark --
#pragma mark -- PRIVATE 定時(shí)器相關(guān)
- (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) {
// 時(shí)間未到碳想,繼續(xù)錄音
if ([weakSelf.delegate respondsToSelector:@selector(recordManagerDelegateTimeProgress:)]) {
[weakSelf.delegate recordManagerDelegateTimeProgress:time];
}
NSLog(@"錄音時(shí)間 == %ld",time);
}else {
if (time > weakSelf.maxRecordTime) {
weakSelf.timeout = YES;
// 錄音時(shí)間到
if ([weakSelf.delegate respondsToSelector:@selector(recordManagerDelegateTimeEnd:)]) {
[weakSelf.delegate recordManagerDelegateTimeEnd:YES];
}
weakSelf.isCancel = YES;
[weakSelf stopRecorder];
}else {
// 時(shí)間未到,繼續(xù)錄音
if ([weakSelf.delegate respondsToSelector:@selector(recordManagerDelegateTimeProgress:)]) {
[weakSelf.delegate recordManagerDelegateTimeProgress:time];
}
NSLog(@"錄音時(shí)間 == %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