diff --git a/keyBoard/Class/AiTalk/V/KBAiWaveformView.h b/keyBoard/Class/AiTalk/V/KBAiWaveformView.h index 3bc1a46..8139315 100644 --- a/keyBoard/Class/AiTalk/V/KBAiWaveformView.h +++ b/keyBoard/Class/AiTalk/V/KBAiWaveformView.h @@ -25,6 +25,9 @@ NS_ASSUME_NONNULL_BEGIN /// 波形条间距 @property(nonatomic, assign) CGFloat barSpacing; +/// 自定义柱状高度基准(0~1),长度需 >= barCount,未设置则使用默认算法 +@property(nonatomic, strong, nullable) NSArray *barHeightPattern; + /// 更新音量值 /// @param rms 音量 RMS 值 (0.0 - 1.0) - (void)updateWithRMS:(float)rms; diff --git a/keyBoard/Class/AiTalk/V/KBAiWaveformView.m b/keyBoard/Class/AiTalk/V/KBAiWaveformView.m index ba8bbc9..93a7948 100644 --- a/keyBoard/Class/AiTalk/V/KBAiWaveformView.m +++ b/keyBoard/Class/AiTalk/V/KBAiWaveformView.m @@ -74,6 +74,11 @@ CGFloat x = startX + i * (self.barWidth + self.barSpacing); CGFloat height = minHeight; + if (self.barHeightPattern.count > i) { + CGFloat base = [self.barHeightPattern[i] floatValue]; + base = MIN(MAX(base, 0.15), 0.9); + height = MAX(minHeight, maxHeight * base); + } CGFloat y = (maxHeight - height) / 2; barLayer.frame = CGRectMake(x, y, self.barWidth, height); @@ -141,9 +146,15 @@ CGFloat phase = (CGFloat)i / self.barLayers.count * M_PI * 2; CGFloat wave = sin(time * 3 + phase) * 0.3 + 0.7; // 0.4 - 1.0 - // 计算高度 - CGFloat heightFactor = rms * wave; - CGFloat height = minHeight + range * heightFactor; + CGFloat baseFactor = 0.2; + if (self.barHeightPattern.count > i) { + baseFactor = [self.barHeightPattern[i] floatValue]; + baseFactor = MIN(MAX(baseFactor, 0.15), 0.9); + } + // 计算高度:基准高度 + 随 RMS 波动 + CGFloat dynamicFactor = rms * (0.35 + 0.15 * wave); // 0.35~0.5 + CGFloat heightFactor = MIN(1.0, baseFactor + dynamicFactor * (1.0 - baseFactor)); + CGFloat height = maxHeight * heightFactor; height = MAX(minHeight, MIN(maxHeight, height)); // 更新位置 diff --git a/keyBoard/Class/AiTalk/V/KBVoiceInputBar.m b/keyBoard/Class/AiTalk/V/KBVoiceInputBar.m index ea40996..1812a5f 100644 --- a/keyBoard/Class/AiTalk/V/KBVoiceInputBar.m +++ b/keyBoard/Class/AiTalk/V/KBVoiceInputBar.m @@ -7,6 +7,7 @@ #import "KBVoiceInputBar.h" #import "KBAiRecordButton.h" +#import "KBAiWaveformView.h" #import @interface KBVoiceInputBar () @@ -34,6 +35,8 @@ /// 录音中视图 @property (nonatomic, strong) UIView *recordingView; @property (nonatomic, strong) UIImageView *recordingCenterIconView; +@property (nonatomic, strong) KBAiWaveformView *leftWaveformView; +@property (nonatomic, strong) KBAiWaveformView *rightWaveformView; /// 取消视图 @property (nonatomic, strong) UIView *cancelView; @@ -146,6 +149,21 @@ make.width.height.mas_equalTo(36); }]; + [self.recordingView addSubview:self.leftWaveformView]; + [self.recordingView addSubview:self.rightWaveformView]; + [self.leftWaveformView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerY.equalTo(self.recordingCenterIconView); + make.right.equalTo(self.recordingCenterIconView.mas_left).offset(-16); + make.width.mas_equalTo(84); + make.height.mas_equalTo(34); + }]; + [self.rightWaveformView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerY.equalTo(self.recordingCenterIconView); + make.left.equalTo(self.recordingCenterIconView.mas_right).offset(16); + make.width.mas_equalTo(84); + make.height.mas_equalTo(34); + }]; + // 取消视图 [self.inputContainer addSubview:self.cancelView]; [self.cancelView mas_makeConstraints:^(MASConstraintMaker *make) { @@ -224,6 +242,11 @@ if (!self.toggleIconButton.hidden) { [self.inputContainer bringSubviewToFront:self.toggleIconButton]; } + if (inputState == KBVoiceInputBarStateRecording) { + [self startRecordingWaveAnimationIfNeeded]; + } else { + [self stopRecordingWaveAnimation]; + } [self updateCenterTextIfNeeded]; } @@ -231,6 +254,11 @@ - (void)updateVolumeRMS:(float)rms { [self.recordButton updateVolumeRMS:rms]; + if (self.inputState == KBVoiceInputBarStateRecording) { + CGFloat safeRMS = MAX(rms, 0.6f); + [self.leftWaveformView updateWithRMS:safeRMS]; + [self.rightWaveformView updateWithRMS:safeRMS]; + } } #pragma mark - KBAiRecordButtonDelegate @@ -370,6 +398,30 @@ return _recordingCenterIconView; } +- (KBAiWaveformView *)leftWaveformView { + if (!_leftWaveformView) { + _leftWaveformView = [[KBAiWaveformView alloc] init]; + _leftWaveformView.waveColor = [UIColor whiteColor]; + _leftWaveformView.barCount = 7; + _leftWaveformView.barWidth = 3; + _leftWaveformView.barSpacing = 6; + _leftWaveformView.barHeightPattern = @[@0.35, @0.85, @0.5, @0.35, @0.9, @0.55, @0.35]; + } + return _leftWaveformView; +} + +- (KBAiWaveformView *)rightWaveformView { + if (!_rightWaveformView) { + _rightWaveformView = [[KBAiWaveformView alloc] init]; + _rightWaveformView.waveColor = [UIColor whiteColor]; + _rightWaveformView.barCount = 7; + _rightWaveformView.barWidth = 3; + _rightWaveformView.barSpacing = 6; + _rightWaveformView.barHeightPattern = @[@0.35, @0.85, @0.5, @0.35, @0.9, @0.55, @0.35]; + } + return _rightWaveformView; +} + - (UIView *)cancelView { if (!_cancelView) { _cancelView = [[UIView alloc] init]; @@ -478,4 +530,20 @@ } } +#pragma mark - Recording Wave + +- (void)startRecordingWaveAnimationIfNeeded { + [self.leftWaveformView startIdleAnimation]; + [self.rightWaveformView startIdleAnimation]; + [self.leftWaveformView updateWithRMS:0.7f]; + [self.rightWaveformView updateWithRMS:0.7f]; +} + +- (void)stopRecordingWaveAnimation { + [self.leftWaveformView stopAnimation]; + [self.rightWaveformView stopAnimation]; + [self.leftWaveformView reset]; + [self.rightWaveformView reset]; +} + @end