TJRecorderTool.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. //
  2. // TJRecorderTool.m
  3. // TJAudioRecorderAndPlayer
  4. //
  5. // Created by TanJian on 17/5/19.
  6. // Copyright © 2017年 Joshpell. All rights reserved.
  7. //
  8. #import "TJRecorderTool.h"
  9. static const CGFloat volumeObserverMargin = 1; //录音时timer间隔
  10. static const CGFloat playerTimeObserverMargin = 0.05; //音量监听的timer间隔
  11. @interface TJRecorderTool ()
  12. {
  13. AVAudioSession *session;
  14. //录音器设置
  15. NSDictionary *recorderSettingsDict;
  16. //音量监控定时器
  17. NSTimer *volumeTimer;
  18. double lowPassResults;
  19. id timeObserve;
  20. }
  21. //录音器
  22. @property (nonatomic, strong) AVAudioRecorder *recorder;
  23. //本地播放器
  24. @property (nonatomic, strong) AVAudioPlayer *playerLocal;
  25. //网络播放器
  26. @property (nonatomic, strong) AVPlayer *networkAudioPlayer;
  27. //网路播放item
  28. @property (nonatomic, strong) AVPlayerItem *songItem;
  29. //当前播放url
  30. @property (nonatomic, copy) NSString *currentURL;
  31. //网络媒体总时长
  32. @property (nonatomic, assign) NSString *netAudioTime;
  33. //当前播放时间(秒)
  34. @property (nonatomic, copy) NSString *currentPlayTime;
  35. //当前录音时长(秒)
  36. @property (nonatomic, copy) NSString *currentRecordTime;
  37. @end
  38. @implementation TJRecorderTool
  39. //添加getter
  40. @synthesize configureRecorder = _configureRecorder;
  41. @synthesize configureNetwork = _configureNetwork;
  42. @synthesize startRecord = _startRecord;
  43. @synthesize continueRecord = _continueRecord;
  44. @synthesize stopRecorder = _stopRecorder;
  45. @synthesize playRecorderLocal = _playRecorderLocal;
  46. @synthesize pauseRecorderPlayLocal = _pauseRecorderPlayLocal;
  47. @synthesize continueRecorderPlayLocal = _continueRecorderPlayLocal;
  48. @synthesize stopRecorderPlayLocal = _stopRecorderPlayLocal;
  49. @synthesize downloadNetworkaAudio = _downloadNetworkaAudio;
  50. @synthesize playNetwork = _playNetwork;
  51. @synthesize pauseNetwork = _pauseNetwork;
  52. @synthesize stopNetwork = _stopNetwork;
  53. @synthesize getRecorderDataBlock = _getRecorderDataBlock;
  54. @synthesize pauseRecord = _pauseRecord;
  55. + (instancetype)sharedInstance {
  56. static TJRecorderTool *instance;
  57. static dispatch_once_t onceToken;
  58. dispatch_once(&onceToken, ^{
  59. instance = [TJRecorderTool new];
  60. });
  61. return instance;
  62. }
  63. -(configerOrAction)configureRecorder{
  64. if (!_configureRecorder) {
  65. __weak typeof(self) weakSelf = self;
  66. return ^(){
  67. //录音设置
  68. self->recorderSettingsDict = [[NSDictionary alloc] initWithObjectsAndKeys:
  69. [NSNumber numberWithInt:kAudioFormatMPEG4AAC],AVFormatIDKey,
  70. [NSNumber numberWithInt:8000],AVSampleRateKey,
  71. [NSNumber numberWithInt:1],AVNumberOfChannelsKey,
  72. [NSNumber numberWithInteger:AVAudioQualityHigh],AVEncoderAudioQualityKey,
  73. nil];
  74. return weakSelf;
  75. };
  76. }
  77. return _configureRecorder;
  78. }
  79. -(configerOrAction)configureNetwork{
  80. if (!_configureNetwork) {
  81. __weak typeof(self) weakSelf = self;
  82. return ^(){
  83. NSError *sessionError;
  84. //AVAudioSessionCategoryPlay用于播放
  85. if(self->session == nil){
  86. self->session = [AVAudioSession sharedInstance];
  87. [self->session setCategory:AVAudioSessionCategoryPlayback error:&sessionError];
  88. [self->session setActive:YES error:nil];
  89. }
  90. if (sessionError) {
  91. NSLog(@"Error creating session: %@", [sessionError description]);
  92. }
  93. if (![self isHeadsetPluggedIn]) {
  94. NSError *audioError = nil;
  95. BOOL success = [self->session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&audioError];
  96. if(!success)
  97. {
  98. NSLog(@"error doing outputaudioportoverride - %@", [audioError localizedDescription]);
  99. }
  100. }
  101. return weakSelf;
  102. };
  103. }
  104. return _configureNetwork;
  105. }
  106. -(configerOrAction)startRecord{
  107. if (!_startRecord) {
  108. //开始录音
  109. __weak typeof(self) weakSelf = self;
  110. return ^(){
  111. if ([weakSelf canRecord]) {
  112. self->session = [AVAudioSession sharedInstance];
  113. [self->session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
  114. [self->session setActive:YES error:nil];
  115. weakSelf.currentRecordTime = @"0";
  116. if (weakSelf.recorder) {
  117. self->_recorder.meteringEnabled = YES;
  118. [self->_recorder prepareToRecord];
  119. [self->_recorder record];
  120. //启动定时器
  121. if (!self->volumeTimer) {
  122. self->volumeTimer = [NSTimer scheduledTimerWithTimeInterval:volumeObserverMargin target:weakSelf selector:@selector(levelTimer:) userInfo:nil repeats:YES];
  123. }
  124. NSLog(@"开始录音");
  125. } else{
  126. NSLog(@"录音机初始化失败");
  127. }
  128. }else{
  129. NSLog(@"权限失败");
  130. }
  131. return weakSelf;
  132. };
  133. }
  134. return _startRecord;
  135. }
  136. -(configerOrAction)continueRecord{
  137. if (!_continueRecord) {
  138. __weak typeof(self) weakself = self;
  139. return ^(){
  140. self->_recorder.meteringEnabled = YES;
  141. [self->_recorder prepareToRecord];
  142. [self->_recorder record];
  143. //启动定时器
  144. if (!self->volumeTimer) {
  145. self->volumeTimer = [NSTimer scheduledTimerWithTimeInterval:volumeObserverMargin target:weakself selector:@selector(levelTimer:) userInfo:nil repeats:YES];
  146. }
  147. NSLog(@"开始录音");
  148. return weakself;
  149. };
  150. }
  151. return _configureNetwork;
  152. }
  153. -(configerOrAction)stopRecorder{
  154. if (!_stopRecorder) {
  155. __weak typeof(self) weakSelf = self;
  156. return ^(){
  157. //录音停止
  158. [weakSelf.recorder stop];
  159. weakSelf.recorder = nil;
  160. //结束定时器
  161. [self->volumeTimer invalidate];
  162. self->volumeTimer = nil;
  163. NSLog(@"停止录音");
  164. return weakSelf;
  165. };
  166. }
  167. return _stopRecorder;
  168. }
  169. -(configerOrAction)pauseRecord{
  170. if (!_pauseRecord) {
  171. __weak typeof(self) weakSelf = self;
  172. return ^(){
  173. [weakSelf.recorder pause];
  174. //结束定时器
  175. [self->volumeTimer invalidate];
  176. self->volumeTimer = nil;
  177. NSLog(@"暂停录音");
  178. return weakSelf;
  179. };
  180. }
  181. return _pauseRecord;
  182. }
  183. -(configerOrAction)playRecorderLocal{
  184. if (!_playRecorderLocal) {
  185. if ([self.recorder isRecording]) {
  186. [self.recorder stop];
  187. self.recorder = nil;
  188. //结束定时器
  189. [volumeTimer invalidate];
  190. volumeTimer = nil;
  191. }
  192. __weak typeof(self) weakSelf = self;
  193. return ^(){
  194. if (![self isHeadsetPluggedIn]) {
  195. //无耳机,打开外放模式
  196. NSError *audioError = nil;
  197. BOOL success = [self->session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&audioError];
  198. if(!success)
  199. {
  200. NSLog(@"error doing outputaudioportoverride - %@", [audioError localizedDescription]);
  201. }
  202. }
  203. if (weakSelf.playerLocal){
  204. [weakSelf.playerLocal play];
  205. NSLog(@"播放本地录音");
  206. }else{
  207. NSLog(@"本地录音播放器初始化失败");
  208. }
  209. return weakSelf;
  210. };
  211. }
  212. return _playRecorderLocal;
  213. }
  214. -(configerOrAction)pauseRecorderPlayLocal{
  215. if (!_pauseRecorderPlayLocal) {
  216. __weak typeof(self) weakSelf = self;
  217. return ^(){
  218. if (self->_playerLocal){
  219. [weakSelf.playerLocal pause];
  220. NSLog(@"暂停播放本地录音");
  221. }
  222. return weakSelf;
  223. };
  224. }
  225. return _pauseRecorderPlayLocal;
  226. }
  227. -(configerOrAction)continueRecorderPlayLocal{
  228. if (!_continueRecorderPlayLocal) {
  229. __weak typeof(self) weakself = self;
  230. return ^(){
  231. if (self->_playerLocal) {
  232. [weakself.playerLocal play];
  233. NSLog(@"继续播放本地录音");
  234. }
  235. return weakself;
  236. };
  237. }
  238. return _continueRecorderPlayLocal;
  239. }
  240. -(configerOrAction)stopRecorderPlayLocal{
  241. if (!_stopRecorderPlayLocal) {
  242. __weak typeof(self) weakSelf = self;
  243. return ^(){
  244. if (self->_playerLocal){
  245. [weakSelf.playerLocal stop];
  246. weakSelf.playerLocal = nil;
  247. NSLog(@"停止播放本地录音");
  248. }
  249. return weakSelf;
  250. };
  251. }
  252. return _stopRecorderPlayLocal;
  253. }
  254. -(configureURL)downloadNetworkaAudio{
  255. if (!_downloadNetworkaAudio) {
  256. __weak typeof(self) weakSelf = self;
  257. return ^(NSString *url){
  258. weakSelf.songItem = [[AVPlayerItem alloc]initWithURL:[NSURL URLWithString:url]];
  259. weakSelf.networkAudioPlayer = [[AVPlayer alloc]initWithPlayerItem:self->_songItem];
  260. weakSelf.currentURL = url;
  261. //给当前歌曲添加监控
  262. [weakSelf addObserver];
  263. return weakSelf;
  264. };
  265. }
  266. return _downloadNetworkaAudio;
  267. }
  268. -(configerOrAction)playNetwork{
  269. if (!_playNetwork) {
  270. __weak typeof(self) weakself = self;
  271. return ^(){
  272. [self->_networkAudioPlayer play];
  273. return weakself;
  274. };
  275. }
  276. return _playNetwork;
  277. }
  278. -(configerOrAction)pauseNetwork{
  279. if (!_pauseNetwork) {
  280. __weak typeof(self) weakself = self;
  281. return ^(){
  282. [self->_networkAudioPlayer pause];
  283. return weakself;
  284. };
  285. }
  286. return _playNetwork;
  287. }
  288. -(configerOrAction)stopNetwork{
  289. if (!_stopNetwork) {
  290. __weak typeof(self) weakself = self;
  291. return ^(){
  292. [weakself.networkAudioPlayer pause];
  293. weakself.currentPlayTime = @"0";
  294. weakself.currentURL = nil;
  295. weakself.networkAudioPlayer = nil;
  296. weakself.songItem = nil;
  297. [weakself removeObserverAction];
  298. return weakself;
  299. };
  300. }
  301. return _pauseNetwork;
  302. }
  303. - (void)playerItemDidReachEnd:(NSNotification *)notification {
  304. self.netAudioTime = @"0";
  305. if (self.playerFinished) {
  306. self.playerFinished();
  307. }
  308. }
  309. -(configerRecorderData)getRecorderDataBlock{
  310. if (!_getRecorderDataBlock) {
  311. return ^(){
  312. NSData *data = [NSData dataWithContentsOfFile:[self getRecorderPath]];
  313. NSLog(@"录音文件长度%ld",data.length);
  314. return data;
  315. };
  316. }
  317. return _getRecorderDataBlock;
  318. }
  319. #pragma mark 辅助方法
  320. - (void)addObserver {
  321. //监控状态属性,注意AVPlayer也有一个status属性,通过监控它的status也可以获得播放状态
  322. [_songItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
  323. //监控网络加载情况属性
  324. [_songItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
  325. //网络音频结束监听
  326. [[NSNotificationCenter defaultCenter] addObserver:self
  327. selector:@selector(playerItemDidReachEnd:)
  328. name:AVPlayerItemDidPlayToEndTimeNotification
  329. object:_songItem]; //更新播放器进度
  330. AVPlayerItem *currentItem = self.networkAudioPlayer.currentItem;
  331. __weak typeof(self) weakSelf = self;
  332. timeObserve = [self.networkAudioPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
  333. float current = CMTimeGetSeconds(time);
  334. float total = CMTimeGetSeconds(currentItem.duration);
  335. if (current) {
  336. weakSelf.currentPlayTime = [NSString stringWithFormat:@"%.f",current];
  337. weakSelf.netAudioTime = [NSString stringWithFormat:@"%.2f",total];
  338. if (weakSelf.timeObserverBlock) {
  339. weakSelf.timeObserverBlock(weakSelf.netAudioTime,weakSelf.currentPlayTime);
  340. }
  341. }
  342. }];
  343. //监控状态属性,注意AVPlayer也有一个status属性,通过监控它的status也可以获得播放状态
  344. [currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
  345. //监控网络加载情况属性
  346. [currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
  347. }
  348. -(void)removeObserverAction{
  349. [_songItem removeObserver:self forKeyPath:@"status"];
  350. [_songItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
  351. [[NSNotificationCenter defaultCenter]removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_songItem];
  352. [self.networkAudioPlayer removeTimeObserver:timeObserve];
  353. }
  354. /**
  355. * 通过KVO监控播放器状态
  356. *
  357. * @param keyPath 监控属性
  358. * @param object 监视器
  359. * @param change 状态改变
  360. * @param context 上下文
  361. */
  362. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
  363. AVPlayerItem * songItem = object;
  364. if ([keyPath isEqualToString:@"status"]) {
  365. switch (self.networkAudioPlayer.status) {
  366. case AVPlayerStatusUnknown:
  367. {
  368. NSLog(@"网路音频播放器状态不明");
  369. }
  370. break;
  371. case AVPlayerStatusReadyToPlay:
  372. {
  373. NSLog(@"网路音频播放器状态可播放");
  374. _netAudioTime = [NSString stringWithFormat:@"%.2f", CMTimeGetSeconds(_songItem.asset.duration)];
  375. NSLog(@"网络媒体总时长%f",CMTimeGetSeconds(_songItem.asset.duration));
  376. }
  377. break;
  378. case AVPlayerStatusFailed:
  379. {
  380. NSLog(@"网路音频播放器状态失败");
  381. }
  382. break;
  383. default:
  384. break;
  385. }
  386. }
  387. if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
  388. NSArray * array = songItem.loadedTimeRanges;
  389. CMTimeRange timeRange = [array.firstObject CMTimeRangeValue]; //本次缓冲的时间范围
  390. NSTimeInterval totalBuffer = CMTimeGetSeconds(timeRange.start) + CMTimeGetSeconds(timeRange.duration); //缓冲总长度
  391. NSLog(@"共缓冲%.2f",totalBuffer);
  392. }
  393. }
  394. //判断是否允许使用麦克风7.0新增的方法requestRecordPermission
  395. -(BOOL)canRecord
  396. {
  397. __block BOOL bCanRecord = YES;
  398. if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0"] == NSOrderedAscending)
  399. {
  400. AVAudioSession *audioSession = [AVAudioSession sharedInstance];
  401. if ([audioSession respondsToSelector:@selector(requestRecordPermission:)]) {
  402. [audioSession performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {
  403. if (granted) {
  404. bCanRecord = YES;
  405. }else {
  406. bCanRecord = NO;
  407. dispatch_async(dispatch_get_main_queue(), ^{
  408. [[[UIAlertView alloc] initWithTitle:nil
  409. message:@"app需要访问您的麦克风。\n请启用麦克风-设置/隐私/麦克风"
  410. delegate:nil
  411. cancelButtonTitle:@"关闭"
  412. otherButtonTitles:nil] show];
  413. });
  414. }
  415. }];
  416. }
  417. }
  418. return bCanRecord;
  419. }
  420. -(void)levelTimer:(NSTimer*)timer_
  421. {
  422. CGFloat currentRecordTimeFloat = self.currentRecordTime.floatValue;
  423. currentRecordTimeFloat ++;
  424. self.currentRecordTime = [NSString stringWithFormat:@"%.0f",currentRecordTimeFloat];
  425. NSLog(@"当前录音时长%.0f",currentRecordTimeFloat);
  426. //外部的录音时长监听回调
  427. if ( self.recordTimeObserverBlock) {
  428. self.recordTimeObserverBlock(0,self.currentRecordTime);
  429. }
  430. //call to refresh meter values刷新平均和峰值功率,此计数是以对数刻度计量的,-160表示完全安静,0表示最大输入值
  431. //输入声音的分贝大小计算
  432. [_recorder updateMeters];
  433. const double ALPHA = 0.05;
  434. double peakPowerForChannel = pow(10, (0.05 * [_recorder peakPowerForChannel:0]));
  435. lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
  436. //分贝测试打印
  437. //NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [_recorder averagePowerForChannel:0], [_recorder peakPowerForChannel:0], lowPassResults);
  438. if (lowPassResults>=0.8) {
  439. }else if(lowPassResults>=0.7){
  440. }else if(lowPassResults>=0.6){
  441. }else if(lowPassResults>=0.5){
  442. }else if(lowPassResults>=0.4){
  443. }else if(lowPassResults>=0.3){
  444. }else if(lowPassResults>=0.2){
  445. }else if(lowPassResults>=0.1){
  446. }else{
  447. }
  448. }
  449. -(CGFloat)getLocalRecordTime{
  450. if (_playerLocal) {
  451. return _playerLocal.duration;
  452. }
  453. return 0;
  454. }
  455. //录音文件路径
  456. -(NSString *)getRecorderPath{
  457. NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  458. return [NSString stringWithFormat:@"%@/play.aac",docDir];
  459. }
  460. //判断是否插入耳机
  461. - (BOOL)isHeadsetPluggedIn {
  462. AVAudioSessionRouteDescription* route = [session currentRoute];
  463. for (AVAudioSessionPortDescription* desc in [route outputs]) {
  464. if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])
  465. return YES;
  466. }
  467. return NO;
  468. }
  469. -(BOOL)isRecording{
  470. return self.recorder.isRecording;
  471. }
  472. #pragma mark lazy=====================
  473. -(AVAudioRecorder *)recorder{
  474. if (!_recorder) {
  475. NSError *error = nil;
  476. //必须真机上测试,模拟器上可能会崩溃
  477. _recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL URLWithString:[self getRecorderPath]] settings:recorderSettingsDict error:&error];
  478. }
  479. return _recorder;
  480. }
  481. -(AVAudioPlayer *)playerLocal{
  482. if (!_playerLocal) {
  483. NSError *playerError;
  484. _playerLocal = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:[self getRecorderPath]] error:&playerError];
  485. }
  486. return _playerLocal;
  487. }
  488. @end