处理语音

This commit is contained in:
2026-01-29 20:56:24 +08:00
parent 23c0d14128
commit 36135313d8
8 changed files with 236 additions and 9 deletions

View File

@@ -236,6 +236,19 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
[self updateFooterVisibility];
}
- (void)removeLoadingAssistantMessage {
for (NSInteger i = self.messages.count - 1; i >= 0; i--) {
KBAiChatMessage *message = self.messages[i];
if (message.type == KBAiChatMessageTypeAssistant && message.isLoading) {
[self.messages removeObjectAtIndex:i];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
NSLog(@"[KBChatTableView] 移除 loading AI 消息,索引: %ld", (long)i);
break;
}
}
}
- (void)scrollToBottom {
[self scrollToBottomAnimated:YES];
}
@@ -339,6 +352,11 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
});
});
}
//
if (message.type == KBAiChatMessageTypeAssistant && message.audioId.length > 0) {
[self preloadAudioForMessage:message];
}
}
- (void)reloadWithMessages:(NSArray<KBAiChatMessage *> *)messages
@@ -620,8 +638,8 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
[cell showLoadingAnimation];
}
// 50.5
[self pollAudioForMessage:message atIndexPath:indexPath retryCount:0 maxRetries:5];
// 10110
[self pollAudioForMessage:message atIndexPath:indexPath retryCount:0 maxRetries:10];
}
- (void)pollAudioForMessage:(KBAiChatMessage *)message
@@ -638,6 +656,7 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
dispatch_async(dispatch_get_main_queue(), ^{
// audioURL
if (!error && audioURL.length > 0) {
NSLog(@"[KBChatTableView] 音频 URL 获取成功(第 %ld 次)", (long)(retryCount + 1));
//
[strongSelf downloadAndPlayAudioFromURL:audioURL
forMessage:message
@@ -647,11 +666,11 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
//
if (retryCount < maxRetries - 1) {
NSLog(@"[KBChatTableView] 音频未就绪,0.5秒后重试 (%ld/%ld)",
NSLog(@"[KBChatTableView] 音频未就绪,1秒后重试 (%ld/%ld)",
(long)(retryCount + 1), (long)maxRetries);
// 0.5
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
// 1
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[strongSelf pollAudioForMessage:message
atIndexPath:indexPath
@@ -786,6 +805,115 @@ static const NSTimeInterval kTimestampInterval = 5 * 60; // 5 分钟
}
}
#pragma mark - Audio Preload ()
///
- (void)preloadAudioForMessage:(KBAiChatMessage *)message {
if (!message || message.audioId.length == 0) {
return;
}
//
if (message.audioData && message.audioData.length > 0) {
NSLog(@"[KBChatTableView] 音频已缓存,跳过预加载");
return;
}
NSLog(@"[KBChatTableView] 开始预加载音频audioId: %@", message.audioId);
//
NSDate *startTime = [NSDate date];
// 10110
[self pollPreloadAudioForMessage:message retryCount:0 maxRetries:10 startTime:startTime];
}
///
- (void)pollPreloadAudioForMessage:(KBAiChatMessage *)message
retryCount:(NSInteger)retryCount
maxRetries:(NSInteger)maxRetries
startTime:(NSDate *)startTime {
__weak typeof(self) weakSelf = self;
[self.aiVM requestAudioWithAudioId:message.audioId
completion:^(NSString *_Nullable audioURL, NSError *_Nullable error) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
dispatch_async(dispatch_get_main_queue(), ^{
// audioURL
if (!error && audioURL.length > 0) {
// audioURL
NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"[KBChatTableView] ✅ 预加载音频 URL 获取成功(第 %ld 次),耗时: %.2f 秒", (long)(retryCount + 1), elapsed);
//
[strongSelf downloadAudioFromURL:audioURL forMessage:message startTime:startTime];
return;
}
//
if (retryCount < maxRetries - 1) {
NSLog(@"[KBChatTableView] 预加载音频未就绪1秒后重试 (%ld/%ld)",
(long)(retryCount + 1), (long)maxRetries);
// 1
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[strongSelf pollPreloadAudioForMessage:message
retryCount:retryCount + 1
maxRetries:maxRetries
startTime:startTime];
});
} else {
NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"[KBChatTableView] ❌ 预加载音频失败,已重试 %ld 次,总耗时: %.2f 秒", (long)maxRetries, elapsed);
}
});
}];
}
///
- (void)downloadAudioFromURL:(NSString *)urlString
forMessage:(KBAiChatMessage *)message
startTime:(NSDate *)startTime {
NSURL *url = [NSURL URLWithString:urlString];
if (!url) {
NSLog(@"[KBChatTableView] 预加载:无效的音频 URL: %@", urlString);
return;
}
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDataTask *task = [session dataTaskWithURL:url
completionHandler:^(NSData *_Nullable data,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error || !data || data.length == 0) {
NSLog(@"[KBChatTableView] 预加载:下载音频失败: %@", error.localizedDescription ?: @"");
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
//
message.audioData = data;
//
NSError *playerError = nil;
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:data error:&playerError];
if (!playerError && player) {
message.audioDuration = player.duration;
}
//
NSTimeInterval totalElapsed = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"[KBChatTableView] ✅ 预加载音频完成,音频时长: %.2f秒,总耗时: %.2f 秒", message.audioDuration, totalElapsed);
});
}];
[task resume];
}
#pragma mark - AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {