添加音频动画

This commit is contained in:
2026-02-02 19:49:56 +08:00
parent 993ec623af
commit f1b52151be
2 changed files with 71 additions and 14 deletions

View File

@@ -14,6 +14,8 @@
@property(nonatomic, assign) float currentRMS; @property(nonatomic, assign) float currentRMS;
@property(nonatomic, assign) float targetRMS; @property(nonatomic, assign) float targetRMS;
@property(nonatomic, assign) BOOL isAnimating; @property(nonatomic, assign) BOOL isAnimating;
@property(nonatomic, assign) NSInteger debugFrameCount;
@property(nonatomic, assign) CGSize lastLayoutSize;
@end @end
@implementation KBAiWaveformView @implementation KBAiWaveformView
@@ -49,6 +51,11 @@
- (void)layoutSubviews { - (void)layoutSubviews {
[super layoutSubviews]; [super layoutSubviews];
if (CGSizeEqualToSize(self.lastLayoutSize, self.bounds.size) &&
self.barLayers.count == self.barCount) {
return;
}
self.lastLayoutSize = self.bounds.size;
[self setupBars]; [self setupBars];
} }
@@ -73,7 +80,7 @@
barLayer.cornerRadius = self.barWidth / 2; barLayer.cornerRadius = self.barWidth / 2;
CGFloat x = startX + i * (self.barWidth + self.barSpacing); CGFloat x = startX + i * (self.barWidth + self.barSpacing);
CGFloat height = minHeight; CGFloat height = maxHeight; // transform.scale.y
if (self.barHeightPattern.count > i) { if (self.barHeightPattern.count > i) {
CGFloat base = [self.barHeightPattern[i] floatValue]; CGFloat base = [self.barHeightPattern[i] floatValue];
base = MIN(MAX(base, 0.15), 0.9); base = MIN(MAX(base, 0.15), 0.9);
@@ -81,7 +88,11 @@
} }
CGFloat y = (maxHeight - height) / 2; CGFloat y = (maxHeight - height) / 2;
barLayer.frame = CGRectMake(x, y, self.barWidth, height); barLayer.frame = CGRectMake(x, 0, self.barWidth, maxHeight);
barLayer.anchorPoint = CGPointMake(0.5, 0.5);
barLayer.position = CGPointMake(x + self.barWidth / 2, maxHeight / 2);
CGFloat scale = height / maxHeight;
barLayer.transform = CATransform3DMakeScale(1, scale, 1);
barLayer.backgroundColor = self.waveColor.CGColor; barLayer.backgroundColor = self.waveColor.CGColor;
[self.layer addSublayer:barLayer]; [self.layer addSublayer:barLayer];
@@ -94,24 +105,42 @@
- (void)updateWithRMS:(float)rms { - (void)updateWithRMS:(float)rms {
self.targetRMS = MIN(MAX(rms, 0), 1); self.targetRMS = MIN(MAX(rms, 0), 1);
NSLog(@"[KBAiWaveformView] updateWithRMS: %.3f, targetRMS=%.3f, barCount=%ld, size=%@",
rms, self.targetRMS, (long)self.barLayers.count, NSStringFromCGRect(self.bounds));
if (!self.displayLink) {
self.currentRMS = self.targetRMS;
[self updateBarsWithRMS:self.currentRMS];
}
} }
- (void)startIdleAnimation { - (void)startIdleAnimation {
if (self.isAnimating) NSLog(@"[KBAiWaveformView] startIdleAnimation (animating=%d, bars=%ld, size=%@)",
self.isAnimating, (long)self.barLayers.count, NSStringFromCGRect(self.bounds));
if (self.isAnimating && self.displayLink) {
return; return;
}
self.isAnimating = YES; self.isAnimating = YES;
self.displayLink = self.debugFrameCount = 0;
[CADisplayLink displayLinkWithTarget:self [self.displayLink invalidate];
selector:@selector(updateAnimation)]; self.displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(updateAnimation)];
if (@available(iOS 10.0, *)) {
self.displayLink.preferredFramesPerSecond = 60;
}
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop]
forMode:NSRunLoopCommonModes]; forMode:NSRunLoopCommonModes];
NSLog(@"[KBAiWaveformView] displayLink started");
} }
- (void)stopAnimation { - (void)stopAnimation {
NSLog(@"[KBAiWaveformView] stopAnimation (bars=%ld)", (long)self.barLayers.count);
self.isAnimating = NO; self.isAnimating = NO;
[self.displayLink invalidate]; [self.displayLink invalidate];
self.displayLink = nil; self.displayLink = nil;
for (CAShapeLayer *layer in self.barLayers) {
[layer removeAnimationForKey:@"kb_idle_scale"];
}
} }
- (void)reset { - (void)reset {
@@ -123,8 +152,16 @@
#pragma mark - Animation #pragma mark - Animation
- (void)updateAnimation { - (void)updateAnimation {
if (!self.isAnimating) {
return;
}
self.debugFrameCount += 1;
if (self.debugFrameCount % 30 == 0) {
NSLog(@"[KBAiWaveformView] tick (target=%.3f, current=%.3f, bars=%ld)",
self.targetRMS, self.currentRMS, (long)self.barLayers.count);
}
// RMS // RMS
CGFloat smoothing = 0.3; CGFloat smoothing = 0.65;
self.currentRMS = self.currentRMS =
self.currentRMS + (self.targetRMS - self.currentRMS) * smoothing; self.currentRMS + (self.targetRMS - self.currentRMS) * smoothing;
@@ -144,25 +181,26 @@
// //
CGFloat phase = (CGFloat)i / self.barLayers.count * M_PI * 2; 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 wave = sin(time * 8 + phase) * 0.3 + 0.7; // 0.4 - 1.0
CGFloat baseFactor = 0.2; CGFloat baseFactor = 0.2;
if (self.barHeightPattern.count > i) { if (self.barHeightPattern.count > i) {
baseFactor = [self.barHeightPattern[i] floatValue]; baseFactor = [self.barHeightPattern[i] floatValue];
baseFactor = MIN(MAX(baseFactor, 0.15), 0.9); baseFactor = MIN(MAX(baseFactor, 0.15), 0.9);
} }
//
CGFloat idleWave = sin(time * 10 + phase) * 0.25 + 0.85; // 0.6 - 1.1
CGFloat baseWave = MIN(MAX(baseFactor * idleWave, 0.15), 0.95);
// + RMS // + RMS
CGFloat dynamicFactor = rms * (0.35 + 0.15 * wave); // 0.35~0.5 CGFloat dynamicFactor = rms * (0.45 + 0.20 * wave); // 0.45~0.65
CGFloat heightFactor = MIN(1.0, baseFactor + dynamicFactor * (1.0 - baseFactor)); CGFloat heightFactor = MIN(1.0, baseWave + dynamicFactor * (1.0 - baseWave));
CGFloat height = maxHeight * heightFactor; CGFloat height = maxHeight * heightFactor;
height = MAX(minHeight, MIN(maxHeight, height)); height = MAX(minHeight, MIN(maxHeight, height));
CGFloat scale = height / maxHeight;
//
CGFloat y = (maxHeight - height) / 2;
[CATransaction begin]; [CATransaction begin];
[CATransaction setDisableActions:YES]; [CATransaction setDisableActions:YES];
layer.frame = CGRectMake(layer.frame.origin.x, y, self.barWidth, height); layer.transform = CATransform3DMakeScale(1, scale, 1);
[CATransaction commit]; [CATransaction commit];
} }
} }

View File

@@ -228,6 +228,7 @@
- (void)setInputState:(KBVoiceInputBarState)inputState { - (void)setInputState:(KBVoiceInputBarState)inputState {
_inputState = inputState; _inputState = inputState;
NSLog(@"[KBVoiceInputBar] setInputState: %ld", (long)inputState);
self.textInputView.hidden = (inputState != KBVoiceInputBarStateText); self.textInputView.hidden = (inputState != KBVoiceInputBarStateText);
self.voiceInputView.hidden = (inputState != KBVoiceInputBarStateVoice); self.voiceInputView.hidden = (inputState != KBVoiceInputBarStateVoice);
self.recordingView.hidden = (inputState != KBVoiceInputBarStateRecording); self.recordingView.hidden = (inputState != KBVoiceInputBarStateRecording);
@@ -256,6 +257,7 @@
[self.recordButton updateVolumeRMS:rms]; [self.recordButton updateVolumeRMS:rms];
if (self.inputState == KBVoiceInputBarStateRecording) { if (self.inputState == KBVoiceInputBarStateRecording) {
CGFloat safeRMS = MAX(rms, 0.6f); CGFloat safeRMS = MAX(rms, 0.6f);
NSLog(@"[KBVoiceInputBar] updateVolumeRMS: %.3f (safe=%.3f)", rms, safeRMS);
[self.leftWaveformView updateWithRMS:safeRMS]; [self.leftWaveformView updateWithRMS:safeRMS];
[self.rightWaveformView updateWithRMS:safeRMS]; [self.rightWaveformView updateWithRMS:safeRMS];
} }
@@ -533,6 +535,20 @@
#pragma mark - Recording Wave #pragma mark - Recording Wave
- (void)startRecordingWaveAnimationIfNeeded { - (void)startRecordingWaveAnimationIfNeeded {
NSLog(@"[KBVoiceInputBar] startRecordingWaveAnimationIfNeeded");
self.leftWaveformView.hidden = NO;
self.rightWaveformView.hidden = NO;
[self.inputContainer setNeedsLayout];
[self.recordingView setNeedsLayout];
[self.inputContainer layoutIfNeeded];
[self.recordingView layoutIfNeeded];
[self.leftWaveformView setNeedsLayout];
[self.rightWaveformView setNeedsLayout];
[self.leftWaveformView layoutIfNeeded];
[self.rightWaveformView layoutIfNeeded];
NSLog(@"[KBVoiceInputBar] waveform frames L=%@ R=%@",
NSStringFromCGRect(self.leftWaveformView.frame),
NSStringFromCGRect(self.rightWaveformView.frame));
[self.leftWaveformView startIdleAnimation]; [self.leftWaveformView startIdleAnimation];
[self.rightWaveformView startIdleAnimation]; [self.rightWaveformView startIdleAnimation];
[self.leftWaveformView updateWithRMS:0.7f]; [self.leftWaveformView updateWithRMS:0.7f];
@@ -540,10 +556,13 @@
} }
- (void)stopRecordingWaveAnimation { - (void)stopRecordingWaveAnimation {
NSLog(@"[KBVoiceInputBar] stopRecordingWaveAnimation");
[self.leftWaveformView stopAnimation]; [self.leftWaveformView stopAnimation];
[self.rightWaveformView stopAnimation]; [self.rightWaveformView stopAnimation];
[self.leftWaveformView reset]; [self.leftWaveformView reset];
[self.rightWaveformView reset]; [self.rightWaveformView reset];
self.leftWaveformView.hidden = YES;
self.rightWaveformView.hidden = YES;
} }
@end @end