YOUPAILZSoundRecordView.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. //
  2. // YOUPAILZSoundRecordView.m
  3. // MSYOUPAI
  4. //
  5. // Created by CY on 2022/3/16.
  6. // Copyright © 2022 MS. All rights reserved.
  7. //
  8. #import "YOUPAILZSoundRecordView.h"
  9. #import "NSURL+TransferFileFormat.h"
  10. #import "LZCircleView.h"
  11. @interface YOUPAILZSoundRecordView ()<AVAudioPlayerDelegate,AVAudioRecorderDelegate>
  12. @property (nonatomic, weak)UILabel *youpaiptimeL;
  13. @property (nonatomic, weak)LZCircleView *youpaipcircleView;
  14. /// 录制
  15. @property (nonatomic, strong)AVAudioRecorder *youpaiprecorder;
  16. @property (nonatomic, strong)NSURL *youpaiprecordAudioUrl;
  17. /// 播放
  18. @property (nonatomic, strong)AVAudioPlayer *youpaipaudioPlayer;
  19. @property (nonatomic,strong)NSTimer *youpaiptimer;
  20. @property (nonatomic,assign)NSInteger youpaipplayTime;
  21. @property (nonatomic,assign) LZSoundRecordState youpaipstate;
  22. @end
  23. @implementation YOUPAILZSoundRecordView
  24. - (instancetype)initWithFrame:(CGRect)frame{
  25. if (self = [super initWithFrame:frame]) {
  26. [self youpaifinitValue];
  27. [self youpaifsetupUI];
  28. self.youpaipstate = LZSoundRecordStateNone;
  29. }
  30. return self;
  31. }
  32. - (void)youpaifinitValue{
  33. self.youpaipminTimer = 2;
  34. self.youpaipmaxTimer = 8;
  35. self.youpaiptype = LZAudioTypeWithPM3;
  36. }
  37. - (void)youpaifsetupUI{
  38. UILabel *timeL = [[UILabel alloc] init];
  39. timeL.font = LCFont12;
  40. timeL.textColor = LZ273145Color;
  41. timeL.text = @"0s";
  42. [self addSubview:timeL];
  43. self.youpaiptimeL = timeL;
  44. [timeL mas_makeConstraints:^(MASConstraintMaker *make) {
  45. make.centerX.equalTo(self);
  46. make.top.offset(-10.0f);
  47. }];
  48. LZCircleView *circleView = [[LZCircleView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 74.0f, 74.0f)];
  49. circleView.progress = 0.0f;
  50. circleView.progerssColor = HexColorFromRGB(0xFDD45E);
  51. circleView.progerssBackgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.13f];
  52. circleView.progerWidth = 2.5f;
  53. circleView.backgroundProgerWidth = 1.0f;
  54. circleView.hidden = YES;
  55. [self addSubview:circleView];
  56. self.youpaipcircleView = circleView;
  57. [circleView mas_makeConstraints:^(MASConstraintMaker *make) {
  58. make.centerX.equalTo(self);
  59. make.top.equalTo(timeL.mas_bottom).offset(14.0f);
  60. make.size.mas_offset(CGSizeMake(74.0f, 74.0f));
  61. }];
  62. UIButton *centerBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  63. [centerBtn addTarget:self action:@selector(youpaifcenterBtnClick) forControlEvents:UIControlEventTouchUpInside];
  64. [self addSubview:centerBtn];
  65. self.youpaiprecordBtn = centerBtn;
  66. [centerBtn mas_makeConstraints:^(MASConstraintMaker *make) {
  67. make.center.equalTo(circleView);
  68. make.size.mas_offset(CGSizeMake(65.0f, 65.0f));
  69. }];
  70. UILabel *descL = [[UILabel alloc] init];
  71. descL.font = LCFont12;
  72. descL.textColor = LZA3AABEColor;
  73. descL.text = [NSString stringWithFormat:@"录制一段%@~%@秒的语音",@(self.youpaipminTimer),@(self.youpaipmaxTimer)];
  74. [self addSubview:descL];
  75. self.youpaipdescL = descL;
  76. [descL mas_makeConstraints:^(MASConstraintMaker *make) {
  77. make.centerX.equalTo(self);
  78. make.top.equalTo(centerBtn.mas_bottom).offset(15.0f);
  79. }];
  80. UIButton *leftBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  81. [leftBtn addTarget:self action:@selector(youpaifleftBtnClick) forControlEvents:UIControlEventTouchUpInside];
  82. leftBtn.titleLabel.font = LCFont16;
  83. leftBtn.backgroundColor = LZF5F4F7Color;
  84. leftBtn.layer.cornerRadius = 24.0f;
  85. leftBtn.clipsToBounds = YES;
  86. [leftBtn setTitleColor:LZ273145Color forState:UIControlStateNormal];
  87. [leftBtn setTitle:@"重录" forState:UIControlStateNormal];
  88. leftBtn.hidden = YES;
  89. [self addSubview:leftBtn];
  90. self.youpaipreRecordBtn = leftBtn;
  91. [leftBtn mas_makeConstraints:^(MASConstraintMaker *make) {
  92. make.top.equalTo(descL.mas_bottom).offset(20.0f);
  93. make.size.mas_offset(CGSizeMake(135.0f, 48.0f));
  94. make.centerX.equalTo(self);
  95. }];
  96. }
  97. - (void)youpaifleftBtnClick{
  98. [self youpaifstopTimer];
  99. self.youpaipstate = LZSoundRecordStateNone;
  100. self.youpaiprecordAudioUrl = nil;
  101. self.youpaiprecorder = nil;
  102. // 停止播放
  103. [self.youpaipaudioPlayer stop];
  104. self.youpaipaudioPlayer = nil;
  105. }
  106. - (void)youpaifcenterBtnClick{
  107. if(self.youpaipstate == LZSoundRecordStateNone){
  108. self.youpaipstate = LZSoundRecordStateStart;
  109. //创建录音通道
  110. [self setAudioSession];
  111. // 开始录音
  112. [self.youpaiprecorder record];
  113. // 检查权限
  114. [self youpaifsetupAuthority];
  115. self.youpaipaudioTime = 0;
  116. [self youpaifstartTimer];
  117. }else if(self.youpaipstate == LZSoundRecordStateFinish){
  118. self.youpaipstate = LZSoundRecordStatePlay;
  119. // UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
  120. // AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);
  121. UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
  122. AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
  123. AVAudioSession * session = [AVAudioSession sharedInstance];
  124. [session setActive:YES error:nil];
  125. [session setCategory:AVAudioSessionCategoryPlayback error:nil];
  126. NSError *error;
  127. self.youpaipaudioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:self.youpaiprecordAudioUrl error:&error];
  128. if (error) {
  129. NSLog(@"%@",error);
  130. }
  131. self.youpaipaudioPlayer.delegate = self;
  132. // 播放
  133. [self.youpaipaudioPlayer play];
  134. self.youpaipplayTime = self.youpaipaudioTime;
  135. [self youpaifstartTimer];
  136. }else if (self.youpaipstate == LZSoundRecordStatePlay){
  137. self.youpaipstate = LZSoundRecordStateFinish;
  138. // 停止播放
  139. [self.youpaipaudioPlayer stop];
  140. self.youpaipaudioPlayer = nil;
  141. [self youpaifstopTimer];
  142. }else if (self.youpaipstate == LZSoundRecordStateStart){
  143. self.youpaipstate = LZSoundRecordStateFinish;
  144. // 停止录制
  145. [self.youpaiprecorder stop];
  146. self.youpaiprecorder = nil;
  147. [self youpaifstopTimer];
  148. if (self.youpaipaudioTime < self.youpaipminTimer) {
  149. [ZCHUDHelper showTitle:[NSString stringWithFormat:@"录音不可少于%@秒",@(self.youpaipminTimer)]];
  150. [self youpaifleftBtnClick];
  151. }
  152. }
  153. }
  154. - (NSURL*)getSubmitSound{
  155. [self youpaifstopTimer];
  156. // 停止播放
  157. [self.youpaipaudioPlayer stop];
  158. if (_youpaipstate < LZSoundRecordStateFinish){
  159. return nil;
  160. }
  161. NSURL *returnurl = nil;
  162. switch (self.youpaiptype) {
  163. case LZAudioTypeWithPM3:
  164. returnurl = [self.youpaiprecordAudioUrl transformCAFToMP3];
  165. break;
  166. case LZAudioTypeWithAAC:{
  167. returnurl = self.youpaiprecordAudioUrl;
  168. }
  169. break;
  170. default:
  171. break;
  172. }
  173. return returnurl;
  174. }
  175. - (void)setYoupaipstate:(LZSoundRecordState)state{
  176. _youpaipstate = state;
  177. NSString *time = @"";
  178. NSString *desc = @"";
  179. self.youpaipreRecordBtn.hidden = YES;
  180. // self.youpaiprightBtn.hidden = YES;
  181. self.youpaipcircleView.hidden = YES;
  182. self.youpaipcircleView.progress = 0.0f;
  183. UIImage *centerImg = nil;
  184. // UIImage *leftImg = nil;
  185. // UIImage *rightImg = nil;
  186. switch (state) {
  187. case LZSoundRecordStateNone:{
  188. time = @"0s";
  189. desc = [NSString stringWithFormat:@"录制一段%@~%@秒的语音",@(self.youpaipminTimer),@(self.youpaipmaxTimer)];
  190. centerImg = [UIImage imageNamed:@"vqu_images_big_cast_audio_none"];
  191. }
  192. break;
  193. case LZSoundRecordStateStart:{
  194. time = @"0s";
  195. desc = @"点击结束录音";
  196. centerImg = [UIImage imageNamed:@"vqu_images_big_cast_audio_start"];
  197. self.youpaipcircleView.hidden = NO;
  198. [self youpaifprogressAnimation];
  199. }
  200. break;
  201. case LZSoundRecordStateFinish:{
  202. time = [NSString stringWithFormat:@"%@s",@(self.youpaipaudioTime)];
  203. desc = @"点击试听";
  204. centerImg = [UIImage imageNamed:@"vqu_images_big_cast_audio_finish"];
  205. self.youpaipreRecordBtn.hidden = NO;
  206. // leftImg = [UIImage imageNamed:@"vqu_images_big_case_audio_re_record"];
  207. // rightImg = [UIImage imageNamed:@"vqu_images_big_cast_audio_submit"];
  208. // self.youpaipleftBtn.hidden = NO;
  209. // self.youpaiprightBtn.hidden = NO;
  210. // [self.youpaipleftBtn setTitle:@"重录" forState:UIControlStateNormal];
  211. // [self.youpaiprightBtn setTitle:@"确定" forState:UIControlStateNormal];
  212. }
  213. break;
  214. case LZSoundRecordStatePlay:{
  215. time = [NSString stringWithFormat:@"%@s",@(self.youpaipaudioTime)];
  216. desc = @"播放中";
  217. centerImg = [UIImage imageNamed:@"vqu_images_big_cast_audio_play1"];
  218. self.youpaipreRecordBtn.hidden = NO;
  219. }
  220. break;
  221. case LZSoundRecordStateSubmit:{
  222. // time = [self timeFormatted:self.audioTime];
  223. // desc = @"已录制";
  224. // centerImg = [UIImage imageNamed:@"vqu_images_game_audio_success"];
  225. }
  226. break;
  227. default:
  228. break;
  229. }
  230. self.youpaiptimeL.text = time;
  231. self.youpaipdescL.text = desc;
  232. // [self.youpaipleftBtn setImage:leftImg forState:UIControlStateNormal];
  233. // [self.youpaiprightBtn setImage:rightImg forState:UIControlStateNormal];
  234. [self.youpaiprecordBtn setBackgroundImage:centerImg forState:UIControlStateNormal];
  235. // [self youpaifinitButton:self.youpaipleftBtn];
  236. // [self youpaifinitButton:self.youpaiprightBtn];
  237. }
  238. #pragma mark - AVAudioPlayerDelegate
  239. - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
  240. self.youpaipstate = LZSoundRecordStateFinish;
  241. // 停止播放
  242. [self.youpaipaudioPlayer stop];
  243. self.youpaipaudioPlayer = nil;
  244. [self youpaifstopTimer];
  245. }
  246. - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error{
  247. self.youpaipstate = LZSoundRecordStateFinish;
  248. // 停止播放
  249. [self.youpaipaudioPlayer stop];
  250. self.youpaipaudioPlayer = nil;
  251. [self youpaifstopTimer];
  252. }
  253. - (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player{
  254. self.youpaipstate = LZSoundRecordStateFinish;
  255. // 停止播放
  256. [self.youpaipaudioPlayer stop];
  257. self.youpaipaudioPlayer = nil;
  258. [self youpaifstopTimer];
  259. }
  260. #pragma mark - AVAudioRecorderDelegate
  261. - (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError * _Nullable)error{
  262. self.youpaipstate = LZSoundRecordStateFinish;
  263. // 停止录制
  264. [self.youpaiprecorder stop];
  265. self.youpaiprecorder = nil;
  266. [self youpaifstopTimer];
  267. }
  268. - (void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder{
  269. self.youpaipstate = LZSoundRecordStateFinish;
  270. // 停止录制
  271. [self.youpaiprecorder stop];
  272. self.youpaiprecorder = nil;
  273. [self youpaifstopTimer];
  274. }
  275. // 开始计时
  276. - (void)youpaifstartTimer{
  277. self.youpaiptimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(youpaiftimerAction) userInfo:nil repeats:YES];
  278. [[NSRunLoop currentRunLoop] addTimer:self.youpaiptimer forMode:NSRunLoopCommonModes];
  279. }
  280. // 结束计时
  281. - (void)youpaifstopTimer{
  282. [self.youpaiptimer invalidate];
  283. self.youpaiptimer = nil;
  284. self.youpaiptimeL.text = [NSString stringWithFormat:@"%@s",@(self.youpaipaudioTime)];
  285. }
  286. - (void)youpaifprogressAnimation{
  287. __block CGFloat timeout = 0.0f;
  288. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  289. dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
  290. dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 0.025f * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
  291. //3.设置定时器要执行的任务
  292. dispatch_source_set_event_handler(_timer, ^{
  293. if(timeout >= 1.0f){
  294. dispatch_source_cancel(_timer);
  295. dispatch_async(dispatch_get_main_queue(), ^{
  296. });
  297. }else{
  298. timeout += 0.025f;
  299. CGFloat currentTime = (CGFloat)self.youpaipaudioTime + timeout;
  300. dispatch_async(dispatch_get_main_queue(), ^{
  301. self.youpaipcircleView.progress = currentTime / (CGFloat)self.youpaipmaxTimer;
  302. });
  303. }
  304. });
  305. dispatch_resume(_timer);
  306. }
  307. - (void)youpaiftimerAction{
  308. if (self.youpaipstate == LZSoundRecordStateStart) {
  309. self.youpaipaudioTime ++;
  310. self.youpaiptimeL.text = [NSString stringWithFormat:@"%@s",@(self.youpaipaudioTime)];
  311. [self youpaifprogressAnimation];
  312. if (self.youpaipaudioTime >= self.youpaipmaxTimer) {
  313. self.youpaipstate = LZSoundRecordStateFinish;
  314. // 停止录制
  315. [self.youpaiprecorder stop];
  316. self.youpaiprecorder = nil;
  317. [self youpaifstopTimer];
  318. }
  319. }else if (self.youpaipstate == LZSoundRecordStatePlay){
  320. self.youpaipplayTime --;
  321. self.youpaiptimeL.text = [NSString stringWithFormat:@"%@s",@(self.youpaipplayTime)];
  322. }
  323. }
  324. //- (NSString *)timeFormatted:(NSInteger)totalSeconds{
  325. // NSInteger seconds = totalSeconds % 60;
  326. // NSInteger minutes = (totalSeconds / 60);
  327. // return [NSString stringWithFormat:@"%02ld:%02ld", minutes, seconds];
  328. //}
  329. /// 视频本地url路径
  330. - (NSURL *)youpaiprecordAudioUrl{
  331. if (!_youpaiprecordAudioUrl) {
  332. NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) lastObject];
  333. NSString *fileName;
  334. if (self.youpaiptype == LZAudioTypeWithAAC) {
  335. fileName = @"audioVerify.aac";
  336. }else{
  337. fileName = @"audioVerify.caf";
  338. }
  339. _youpaiprecordAudioUrl = [NSURL fileURLWithPath:[path stringByAppendingPathComponent:fileName]];
  340. if ([[NSFileManager defaultManager] fileExistsAtPath:_youpaiprecordAudioUrl.resourceSpecifier]){
  341. [[NSFileManager defaultManager] removeItemAtPath:_youpaiprecordAudioUrl.resourceSpecifier error:nil];
  342. }
  343. }
  344. return _youpaiprecordAudioUrl;
  345. }
  346. - (AVAudioRecorder *)youpaiprecorder{
  347. if (_youpaiprecorder == nil) {
  348. //创建录音机
  349. NSError *error=nil;
  350. _youpaiprecorder = [[AVAudioRecorder alloc] initWithURL:self.youpaiprecordAudioUrl settings:[self getAudioSetting] error:&error];
  351. _youpaiprecorder.delegate = self;
  352. if (error) {
  353. NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription);
  354. return nil;
  355. }
  356. }
  357. return _youpaiprecorder;
  358. }
  359. /**
  360. * 取得录音文件设置
  361. *
  362. * @return 录音设置
  363. */
  364. -(NSDictionary *)getAudioSetting{
  365. if (self.youpaiptype == LZAudioTypeWithAAC) {
  366. NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc]init] ;
  367. //设置录音格式 AVFormatIDKey==kAudioFormatLinearPCM
  368. [recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
  369. //设置录音采样率(Hz) 如:AVSampleRateKey==8000/44100/96000(影响音频的质量)
  370. [recordSetting setValue:[NSNumber numberWithFloat:44100] forKey:AVSampleRateKey];
  371. //录音通道数 1 或 2
  372. [recordSetting setValue:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
  373. //线性采样位数 8、16、24、32
  374. [recordSetting setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
  375. //录音的质量
  376. [recordSetting setValue:[NSNumber numberWithInt:AVAudioQualityMedium] forKey:AVEncoderAudioQualityKey];
  377. return recordSetting.copy;
  378. }else{
  379. NSDictionary *recordSetting = @{
  380. AVFormatIDKey : @(kAudioFormatLinearPCM),
  381. AVSampleRateKey : @(44100),
  382. AVNumberOfChannelsKey : @(2),
  383. AVLinearPCMBitDepthKey : @(16),
  384. AVLinearPCMIsNonInterleaved : @NO,
  385. AVLinearPCMIsFloatKey : @NO,
  386. AVLinearPCMIsBigEndianKey : @NO,
  387. };
  388. return recordSetting;
  389. }
  390. }
  391. /// 权限
  392. - (void)youpaifsetupAuthority{
  393. [UCAuthorityManager microPhoneAuthority:^{
  394. } denied:^{
  395. [self youpaifshowAlertVCWithTitle:@"请在iphone的“设置-隐私-麦克风”选项中,允许APP访问您的麦克风。"];
  396. }];
  397. }
  398. - (void)youpaifshowAlertVCWithTitle:(NSString *)title{
  399. UIAlertController *systemAlert = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
  400. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"现在去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action){
  401. if ([[UIDevice currentDevice].systemVersion floatValue] < 10.0)
  402. {
  403. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
  404. }
  405. else
  406. {
  407. // 去系统设置页面
  408. if (@available(iOS 10.0, *)) {
  409. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
  410. } else {
  411. // Fallback on earlier versions
  412. }
  413. }
  414. }];
  415. UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
  416. [systemAlert addAction:cancelAction];
  417. [systemAlert addAction:action];
  418. dispatch_async(dispatch_get_main_queue(), ^{
  419. [[LCTools getCurrentVC] presentViewController: systemAlert animated: YES completion: nil];
  420. });
  421. }
  422. - (void)setAudioSession{
  423. AVAudioSession *session = [AVAudioSession sharedInstance];
  424. NSError *sessionError;
  425. [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
  426. if(session == nil){
  427. NSLog(@"Error creating session: %@", [sessionError description]);
  428. }
  429. else{
  430. [session setActive:YES error:nil];
  431. }
  432. }
  433. - (void)youpaifstopSoundRecord{
  434. [self youpaifstopTimer];
  435. self.youpaiprecordAudioUrl = nil;
  436. self.youpaiprecorder = nil;
  437. // 停止播放
  438. [self.youpaipaudioPlayer stop];
  439. self.youpaipaudioPlayer = nil;
  440. }
  441. - (void)dealloc{
  442. [self youpaifstopSoundRecord];
  443. }
  444. @end