CWVoiceChangePlayCell.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. //
  2. // CWVoiceChangePlayCell.m
  3. // QQVoiceDemo
  4. //
  5. // Created by chavez on 2017/10/11.
  6. // Copyright © 2017年 陈旺. All rights reserved.
  7. //
  8. #import "CWVoiceChangePlayCell.h"
  9. #import "UIView+CWChat.h"
  10. #import "CWRecordModel.h"
  11. #import "CWAudioPlayer.h"
  12. #import "SoundTouchOperation.h"
  13. static CGFloat const levelWidth = 3.0;
  14. static CGFloat const levelMargin = 2.0;
  15. @interface CWVoiceChangePlayCell ()
  16. @property (nonatomic,weak) UIButton *playButton;
  17. @property (nonatomic,weak) UIButton *titleButton;
  18. @property (nonatomic,strong) NSMutableArray *currentLevels; // 当前振幅数组
  19. @property (nonatomic,strong) NSMutableArray *allLevels; // 所有收集到的振幅,预先保存,用于播放
  20. @property (nonatomic,assign) NSInteger recordDuration; // 录音时长
  21. @property (nonatomic,weak) CAShapeLayer *levelLayer; // 振幅layer
  22. @property (nonatomic,strong) UIBezierPath *levelPath; // 画振幅的path
  23. @property (nonatomic,weak) UILabel *timeLabel; // 录音时长标签
  24. @property (nonatomic,assign) CGFloat progressValue;
  25. @property (nonatomic,weak) CAShapeLayer *circleLayer; // 环形进度条
  26. @property (nonatomic,strong) NSDictionary *pitchDict;
  27. @end
  28. @implementation CWVoiceChangePlayCell
  29. {
  30. NSInteger _allCount; // 记录所有振幅的总个数
  31. NSInteger _callNumbel; // 记录定时器方法调用多少次,根据这个来算秒数(每秒10次)
  32. NSOperationQueue *_soundTouchQueue;
  33. CGFloat _tempoValue;
  34. CGFloat _pitchValue;
  35. CGFloat _rateValue;
  36. }
  37. #pragma mark - lazyLoad
  38. - (NSDictionary *)pitchDict {
  39. if (_pitchDict == nil) {
  40. _pitchDict = @{ASLocalizedString(@"原声"):@0,
  41. ASLocalizedString(@"萝莉"):@12,
  42. ASLocalizedString(@"大叔"):@-7,
  43. ASLocalizedString(@"惊悚"):@-12,
  44. ASLocalizedString(@"空灵"):@3,
  45. ASLocalizedString(@"搞怪"):@7,
  46. };
  47. }
  48. return _pitchDict;
  49. }
  50. - (NSMutableArray *)allLevels {
  51. if (_allLevels == nil) {
  52. _allLevels = [NSMutableArray array];
  53. }
  54. return _allLevels;
  55. }
  56. - (NSMutableArray *)currentLevels {
  57. if (_currentLevels == nil) {
  58. _currentLevels = [NSMutableArray arrayWithArray:@[@0.05,@0.05,@0.05,@0.05,@0.05,@0.05]];
  59. }
  60. return _currentLevels;
  61. }
  62. - (instancetype)initWithFrame:(CGRect)frame
  63. {
  64. self = [super initWithFrame:frame];
  65. if (self) {
  66. [self initSoundTouchQueue];
  67. [self setupSubviews];
  68. _voicePath = [CWRecordModel shareInstance].path;
  69. }
  70. return self;
  71. }
  72. - (void)initSoundTouchQueue {
  73. _soundTouchQueue = [[NSOperationQueue alloc] init];
  74. _soundTouchQueue.maxConcurrentOperationCount = 1;
  75. }
  76. - (void)setupSubviews {
  77. self.backgroundColor = [UIColor whiteColor];
  78. [self setupPlayButton];
  79. [self setupTitleButton];
  80. // [self timeLabel];
  81. }
  82. - (void)layoutSubviews {
  83. self.playButton.center = CGPointMake(self.cw_width / 2.0, self.cw_height / 2.0 - 10);
  84. self.titleButton.cw_centerX = self.cw_width / 2.0;
  85. self.titleButton.cw_centerY = (self.cw_height - self.playButton.cw_bottom) / 2 + self.playButton.cw_bottom;
  86. }
  87. #pragma mark - setupUI
  88. - (void)setupPlayButton {
  89. UIImage *image = [UIImage imageNamed:@"aio_voiceChange_effect_0"];
  90. UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
  91. [button setBackgroundImage:image forState:UIControlStateNormal];
  92. [button setImage:nil forState:UIControlStateNormal];
  93. [button setImage:[UIImage imageNamed:@"aio_voiceChange_effect_selected"] forState:UIControlStateSelected];
  94. [button setImage:[UIImage imageNamed:@"aio_voiceChange_effect_pressed"] forState:UIControlStateHighlighted];
  95. button.frame = CGRectMake(0, 0, image.size.width, image.size.height);
  96. button.center = CGPointMake(self.cw_width / 2.0, self.cw_height / 2.0 - 10);
  97. [button addTarget:self action:@selector(playAudio) forControlEvents:UIControlEventTouchUpInside];
  98. [self addSubview:button];
  99. self.playButton = button;
  100. }
  101. - (void)setupTitleButton {
  102. UIImage *image = [UIImage imageNamed:@"aio_voiceChange_text_select"];
  103. UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
  104. [button setBackgroundImage:nil forState:UIControlStateNormal];
  105. [button setBackgroundImage:image forState:UIControlStateSelected];
  106. [button setTitle:ASLocalizedString(@"原声")forState:UIControlStateNormal];
  107. button.titleLabel.font = [UIFont systemFontOfSize:13];
  108. [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  109. [button setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
  110. button.frame = CGRectMake(0, self.playButton.cw_bottom + 5, image.size.width, image.size.height);
  111. button.cw_centerX = self.cw_width / 2.0;
  112. button.cw_centerY = (self.cw_height - self.playButton.cw_bottom) / 2 + self.playButton.cw_bottom;
  113. [self addSubview:button];
  114. self.titleButton = button;
  115. }
  116. - (UILabel *)timeLabel {
  117. if (_timeLabel == nil) {
  118. UILabel *timeL = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.cw_width, 20)];
  119. timeL.text = @"0:00";
  120. timeL.textAlignment = NSTextAlignmentCenter;
  121. timeL.font = [UIFont systemFontOfSize:12];
  122. timeL.textColor = [UIColor whiteColor];
  123. timeL.cw_top = self.playButton.cw_centerY + 5;
  124. timeL.hidden = YES;
  125. [self addSubview:timeL];
  126. _timeLabel = timeL;
  127. }
  128. return _timeLabel;
  129. }
  130. - (CAShapeLayer *)levelLayer {
  131. if (_levelLayer == nil) {
  132. CGFloat width = 6 * levelWidth + 5 * levelMargin;
  133. CAShapeLayer *layer = [CAShapeLayer layer];
  134. layer.frame = CGRectMake(self.playButton.cw_centerX - width / 2, self.playButton.cw_centerY - 20, width, 20);
  135. layer.strokeColor = [UIColor whiteColor].CGColor;
  136. layer.lineWidth = levelWidth;
  137. [self.layer addSublayer:layer];
  138. _levelLayer = layer;
  139. }
  140. return _levelLayer;
  141. }
  142. - (CAShapeLayer *)circleLayer {
  143. if (_circleLayer == nil) {
  144. CAShapeLayer *layer = [CAShapeLayer layer];
  145. layer.frame = self.playButton.frame;
  146. layer.strokeColor = [UIColorFromRGBA(20, 120, 211, 1.0) CGColor];
  147. // layer.strokeColor = [[UIColor blueColor] CGColor];
  148. layer.fillColor = [UIColor clearColor].CGColor;
  149. layer.lineWidth = 1.5;
  150. // layer.backgroundColor = [UIColor redColor].CGColor;
  151. [self.layer addSublayer:layer];
  152. _circleLayer = layer;
  153. }
  154. return _circleLayer;
  155. }
  156. #pragma mark - setter
  157. - (void)setTitle:(NSString *)title {
  158. _title = title;
  159. [self.titleButton setTitle:title forState:UIControlStateNormal];
  160. [self.playButton setBackgroundImage:[UIImage imageNamed:_imageName] forState:UIControlStateNormal];
  161. }
  162. - (void)setProgressValue:(CGFloat)progressValue {
  163. _progressValue = progressValue;
  164. [self updateCircleLayer];
  165. // [self setNeedsDisplay];
  166. // [self layoutIfNeeded];
  167. }
  168. #pragma mark - 按钮点击
  169. - (void)playAudio {
  170. self.titleButton.selected = !self.playButton.selected;
  171. self.playButton.selected = !self.playButton.selected;
  172. // NSLog(ASLocalizedString(@"按钮点击。。。。%zd"),self.playButton.selected);
  173. __weak typeof(self) weakSelf = self;
  174. if (self.playButton.selected) {
  175. if (_playRecordBlock) _playRecordBlock(weakSelf);
  176. }else {
  177. if (_endPlayBlock) _endPlayBlock(weakSelf);
  178. }
  179. }
  180. #pragma mark - 变声功能
  181. - (void)playAudioWithPath:(NSString *)path {
  182. NSData *data = [NSData dataWithContentsOfFile:path];
  183. MySountTouchConfig config;
  184. config.sampleRate = 11025;
  185. config.tempoChange = 0; // -50 - 100
  186. config.pitch = [self.pitchDict[self.title] intValue]; // -12 - 12
  187. config.rate = 0; // -50 - 100
  188. SoundTouchOperation *sdop = [[SoundTouchOperation alloc] initWithTarget:self
  189. action:@selector(playVoiceChange:)
  190. SoundTouchConfig:config soundFile:data];
  191. [_soundTouchQueue cancelAllOperations];
  192. [_soundTouchQueue addOperation:sdop];
  193. }
  194. - (void)playVoiceChange:(NSString *)path {
  195. [[CWAudioPlayer shareInstance] playAudioWith:path];
  196. self.voicePath = path;
  197. }
  198. // 准备播放
  199. - (void)preparePlayAudio {
  200. _callNumbel = 0;
  201. _recordDuration = 0;
  202. [self updateTimeLabel];
  203. _progressValue = 0;
  204. self.levelLayer.hidden = NO;
  205. self.timeLabel.hidden = NO;
  206. self.allLevels = [[CWRecordModel shareInstance].levels mutableCopy];
  207. [self.currentLevels removeAllObjects];
  208. _allCount = self.allLevels.count;
  209. for (NSInteger i = self.allLevels.count - 1 ; i >= self.allLevels.count - 6 ; i--) {
  210. CGFloat l = 0.05;
  211. if (i >= 0) {
  212. l = [self.allLevels[i] floatValue];
  213. }
  214. [self.currentLevels addObject:@(l)];
  215. }
  216. }
  217. #pragma mark - 公有方法
  218. - (void)playingRecord {
  219. // NSLog(@"playingRecord");
  220. [self preparePlayAudio];
  221. // 播放音频
  222. if ([self.title isEqualToString:ASLocalizedString(@"原声")]) {
  223. [self playVoiceChange:[CWRecordModel shareInstance].path];
  224. }else {
  225. [self playAudioWithPath:[CWRecordModel shareInstance].path];
  226. }
  227. }
  228. - (void)updateLevels {
  229. // NSLog(@"updateLevels:::::::::%zd",self.allLevels.count);
  230. CGFloat value = 1 - (CGFloat)self.allLevels.count / _allCount;
  231. if (value == 1 || self.allLevels.count == 0) {
  232. __weak typeof(self) weakSelf = self;
  233. if (_endPlayBlock) {
  234. _endPlayBlock(weakSelf);
  235. }
  236. return;
  237. }
  238. // 振幅更新
  239. [self updateLevelLayer];
  240. // 圆形进度条更新
  241. self.progressValue = value;
  242. _callNumbel++;
  243. // 刷新10次增加一秒
  244. if (_callNumbel % 10 == 0) [self addSeconed];
  245. }
  246. - (void)endPlay {
  247. // 取消layer的隐式动画
  248. [CATransaction begin];
  249. [CATransaction setDisableActions:YES];
  250. self.levelLayer.hidden = YES;
  251. [CATransaction commit];
  252. self.timeLabel.hidden = YES;
  253. self.playButton.selected = NO;
  254. self.titleButton.selected = NO;
  255. [[CWAudioPlayer shareInstance] stopCurrentAudio]; // 停止播放音频
  256. self.progressValue = 0;
  257. }
  258. #pragma mark - 私有方法
  259. - (void)addSeconed {
  260. if (_recordDuration == [CWRecordModel shareInstance].duration) {
  261. // [self.audioTimer invalidate];
  262. return;
  263. }
  264. _recordDuration++;
  265. [self updateTimeLabel];
  266. }
  267. - (void)updateTimeLabel {
  268. NSString *text ;
  269. if (_recordDuration < 60) {
  270. text = [NSString stringWithFormat:@"0:%02zd",_recordDuration];
  271. }else {
  272. NSInteger minutes = _recordDuration / 60;
  273. NSInteger seconed = _recordDuration % 60;
  274. text = [NSString stringWithFormat:@"%zd:%02zd",minutes,seconed];
  275. }
  276. self.timeLabel.text = text;
  277. }
  278. - (void)updateLevelLayer {
  279. CGFloat level = [self.allLevels.firstObject floatValue];
  280. [self.currentLevels removeLastObject];
  281. [self.currentLevels insertObject:@(level) atIndex:0];
  282. [self.allLevels removeObjectAtIndex:0];
  283. self.levelPath = [UIBezierPath bezierPath];
  284. CGFloat height = CGRectGetHeight(self.levelLayer.frame);
  285. for (int i = 0; i < self.currentLevels.count; i++) {
  286. CGFloat x = i * (levelWidth + levelMargin) + levelWidth / 2.0;
  287. CGFloat pathH = [self.currentLevels[i] floatValue] * height;
  288. CGFloat startY = height / 2.0 - pathH / 2.0;
  289. CGFloat endY = height / 2.0 + pathH / 2.0;
  290. [_levelPath moveToPoint:CGPointMake(x, startY)];
  291. [_levelPath addLineToPoint:CGPointMake(x, endY)];
  292. }
  293. self.levelLayer.path = _levelPath.CGPath;
  294. }
  295. - (void)updateCircleLayer {
  296. // NSLog(@"==========================%f",_progressValue);
  297. UIBezierPath *path = [UIBezierPath bezierPath];
  298. CGFloat width = CGRectGetWidth(self.circleLayer.frame);
  299. CGFloat startAngle = -M_PI_2;
  300. CGFloat angle = _progressValue * M_PI * 2;
  301. CGFloat endAngle = startAngle + angle;
  302. [path addArcWithCenter:CGPointMake(width / 2.0, width / 2.0) radius:width / 2.0 - 1.0 startAngle:startAngle endAngle:endAngle clockwise:YES];
  303. self.circleLayer.path = path.CGPath;
  304. }
  305. @end