AudioRecorder.m 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. //
  2. // AudioRecorder.m
  3. // mask
  4. //
  5. // Created by 翟玉磊 on 2019/1/3.
  6. // Copyright © 2019 翟玉磊. All rights reserved.
  7. //
  8. #import "AudioRecorder.h"
  9. // 导入录音头文件(注意添加framework:AVFoundation.framework、AudioToolbox.framework)
  10. #import <AudioToolbox/AudioToolbox.h>
  11. #import <AVFoundation/AVFoundation.h>
  12. #import "NSTimer+Weak.h"
  13. @interface AudioRecorder () <AVAudioRecorderDelegate, AVAudioPlayerDelegate>
  14. @property (nonatomic, strong) NSTimer *audioRecorderTimer; // 录音音量计时器
  15. @property (nonatomic, strong) NSMutableDictionary *audioRecorderSetting; // 录音设置
  16. @property (nonatomic, strong) AVAudioRecorder *audioRecorder; // 录音
  17. @property (nonatomic, strong) AVAudioPlayer *audioPlayer; // 播放
  18. @property (nonatomic, assign) double audioRecorderTime; // 录音时长
  19. @end
  20. @implementation AudioRecorder
  21. /// 录音文件保存路径
  22. NSString *GetFilePathWithDate()
  23. {
  24. NSDate *currentDate = [NSDate date];
  25. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  26. [dateFormatter setDateFormat:@"YYYYMMddHHmmss"];
  27. NSString *filePath = [dateFormatter stringFromDate:currentDate];
  28. filePath = [NSString stringWithFormat:@"%@.aac", filePath];
  29. NSString *tmpPath = [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"];
  30. filePath = [tmpPath stringByAppendingFormat:@"/%@",filePath];
  31. return filePath;
  32. }
  33. /// 获取文件名(包含后缀,如:xxx.acc;不包含文件类型,如xxx)
  34. NSString *GetFileNameWithFilePath(NSString *filePath, BOOL hasFileType)
  35. {
  36. NSString *fileName = [filePath stringByDeletingLastPathComponent];
  37. if (hasFileType)
  38. {
  39. fileName = [filePath lastPathComponent];
  40. }
  41. return fileName;
  42. }
  43. /// 获取文件大小
  44. long long GetFileSizeWithFilePath(NSString *filePath)
  45. {
  46. BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
  47. if (isExist)
  48. {
  49. NSDictionary *fileDict = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
  50. long long fileSize = fileDict.fileSize;
  51. return fileSize;
  52. }
  53. return 0.0;
  54. }
  55. /// 删除文件
  56. void DeleteFileWithFilePath(NSString *filePath)
  57. {
  58. BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
  59. if (isExist)
  60. {
  61. [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
  62. }
  63. }
  64. #pragma mark - 实例化
  65. - (instancetype)init
  66. {
  67. self = [super init];
  68. if (self)
  69. {
  70. // 参数设置 格式、采样率、录音通道、线性采样位数、录音质量
  71. self.audioRecorderSetting = [NSMutableDictionary dictionary];
  72. [self.audioRecorderSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
  73. [self.audioRecorderSetting setValue:[NSNumber numberWithInt:11025] forKey:AVSampleRateKey];
  74. [self.audioRecorderSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
  75. [self.audioRecorderSetting setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
  76. [self.audioRecorderSetting setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];
  77. }
  78. return self;
  79. }
  80. /// 录音单例
  81. + (AudioRecorder *)shareManager
  82. {
  83. static AudioRecorder *staticAudioRecorde;
  84. static dispatch_once_t once;
  85. dispatch_once(&once, ^{
  86. staticAudioRecorde = [[self alloc] init];
  87. });
  88. return staticAudioRecorde;
  89. }
  90. // 内存释放
  91. - (void)dealloc
  92. {
  93. // 内存释放前先停止录音,或音频播放
  94. [self audioStop];
  95. [self audioRecorderStop];
  96. // 内存释放
  97. if (self.audioRecorderTime)
  98. {
  99. [self.audioRecorderTimer invalidate];
  100. self.audioRecorderTimer = nil;
  101. }
  102. if (self.audioRecorderSetting)
  103. {
  104. self.audioRecorderSetting = nil;
  105. }
  106. if (self.audioRecorder)
  107. {
  108. self.audioRecorder = nil;
  109. }
  110. if (self.audioPlayer)
  111. {
  112. self.audioPlayer = nil;
  113. }
  114. }
  115. #pragma mark - 音频处理-录音
  116. /// 开始录音
  117. - (void)audioRecorderStartWithFilePath:(NSString *)filePath
  118. {
  119. // 生成录音文件
  120. NSURL *urlAudioRecorder = [NSURL fileURLWithPath:filePath];
  121. self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:urlAudioRecorder settings:self.audioRecorderSetting error:nil];
  122. // 开启音量检测
  123. [self.audioRecorder setMeteringEnabled:YES];
  124. [self.audioRecorder setDelegate:self];
  125. if (self.audioRecorder)
  126. {
  127. // 录音时设置audioSession属性,否则不兼容Ios7
  128. AVAudioSession *recordSession = [AVAudioSession sharedInstance];
  129. [recordSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
  130. [recordSession setActive:YES error:nil];
  131. if ([self.audioRecorder prepareToRecord])
  132. {
  133. [self.audioRecorder record];
  134. // 设置定时检测
  135. WeakSelf
  136. self.audioRecorderTimer = [NSTimer scheduledTimerWithTimeInterval:0 event:^(NSTimer * _Nonnull timer) {
  137. [weakSelf detectionVoice];
  138. }];
  139. }
  140. }
  141. }
  142. /// 录音音量显示
  143. - (void)detectionVoice
  144. {
  145. // 刷新音量数据
  146. [self.audioRecorder updateMeters];
  147. // // 获取音量的平均值
  148. // [self.audioRecorder averagePowerForChannel:0];
  149. // // 音量的最大值
  150. // [self.audioRecorder peakPowerForChannel:0];
  151. double lowPassResults = pow(10, (0.05 * [self.audioRecorder peakPowerForChannel:0]));
  152. double currentTime = self.audioRecorder.currentTime;
  153. // NSLog(@"lowPassResults:%.2f", lowPassResults);
  154. if (self.detectionVoiceBlock) {
  155. self.detectionVoiceBlock(lowPassResults, currentTime);
  156. }
  157. }
  158. /// 停止录音
  159. - (void)audioRecorderStop
  160. {
  161. if (self.audioRecorder)
  162. {
  163. if ([self.audioRecorder isRecording])
  164. {
  165. // 获取录音时长
  166. self.audioRecorderTime = [self.audioRecorder currentTime];
  167. [self.audioRecorder stop];
  168. // 停止录音后释放掉
  169. self.audioRecorder = nil;
  170. }
  171. }
  172. // 释放计时器
  173. [self.audioRecorderTimer invalidate];
  174. self.audioRecorderTimer = nil;
  175. }
  176. /// 根据录音文件获取录音时长
  177. - (NSTimeInterval)durationAudioRecorderWithFilePath:(NSString *)filePath
  178. {
  179. NSURL *urlFile = [NSURL fileURLWithPath:filePath];
  180. self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:urlFile error:nil];
  181. NSTimeInterval time = self.audioPlayer.duration;
  182. self.audioPlayer = nil;
  183. return time;
  184. }
  185. #pragma mark - 音频处理-播放/停止
  186. /// 音频开始播放或停止
  187. - (void)audioPlayWithFilePath:(NSString *)filePath
  188. {
  189. if (self.audioPlayer)
  190. {
  191. // 判断当前与下一个是否相同
  192. // 相同时,点击时要么播放,要么停止
  193. // 不相同时,点击时停止播放当前的,开始播放下一个
  194. NSString *currentStr = [self.audioPlayer.url relativeString];
  195. // currentStr包含字符"file://location/",通过判断filePath是否为currentPath的子串,是则相同,否则不同
  196. NSRange range = [currentStr rangeOfString:filePath];
  197. if (range.location != NSNotFound)
  198. {
  199. if ([self.audioPlayer isPlaying])
  200. {
  201. [self.audioPlayer stop];
  202. self.audioPlayer = nil;
  203. }
  204. else
  205. {
  206. self.audioPlayer = nil;
  207. [self audioPlayerPlay:filePath];
  208. }
  209. }
  210. else
  211. {
  212. [self audioPlayerStop];
  213. [self audioPlayerPlay:filePath];
  214. }
  215. }
  216. else
  217. {
  218. [self audioPlayerPlay:filePath];
  219. }
  220. }
  221. /// 音频播放停止
  222. - (void)audioStop
  223. {
  224. [self audioPlayerStop];
  225. }
  226. - (BOOL)playing {
  227. return self.audioPlayer.playing;
  228. }
  229. /// 音频播放器开始播放
  230. - (void)audioPlayerPlay:(NSString *)filePath
  231. {
  232. // 判断将要播放文件是否存在
  233. BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
  234. if (!isExist)
  235. {
  236. return;
  237. }
  238. NSURL *urlFile = [NSURL fileURLWithPath:filePath];
  239. self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:urlFile error:nil];
  240. self.audioPlayer.delegate = self;
  241. if (self.audioPlayer)
  242. {
  243. if ([self.audioPlayer prepareToPlay])
  244. {
  245. // 播放时,设置喇叭播放否则音量很小
  246. AVAudioSession *playSession = [AVAudioSession sharedInstance];
  247. [playSession setCategory:AVAudioSessionCategoryPlayback error:nil];
  248. [playSession setActive:YES error:nil];
  249. [self.audioPlayer play];
  250. }
  251. }
  252. }
  253. /// 音频播放器停止播放
  254. - (void)audioPlayerStop
  255. {
  256. if (self.audioPlayer)
  257. {
  258. if ([self.audioPlayer isPlaying])
  259. {
  260. [self.audioPlayer stop];
  261. }
  262. self.audioPlayer = nil;
  263. }
  264. if (self.playerFinished) {
  265. self.playerFinished();
  266. }
  267. }
  268. - (void)clear {
  269. if (_audioRecorderTimer) {
  270. [_audioRecorderTimer invalidate];
  271. _audioRecorderTimer = nil;
  272. }
  273. if (_audioRecorder) {
  274. [_audioRecorder stop];
  275. _audioRecorder = nil;
  276. }
  277. if (_audioPlayer) {
  278. [_audioPlayer stop];
  279. _audioPlayer = nil;
  280. }
  281. }
  282. #pragma mark - AVAudioPlayerDelegate
  283. /// 播放完成时调用的方法 (代理里的方法),需要设置代理才可以调用
  284. - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
  285. [self audioPlayerStop];
  286. }
  287. @end