处理语音
This commit is contained in:
@@ -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];
|
||||
}
|
||||
|
||||
// 开始轮询请求(最多5次,每次间隔0.5秒)
|
||||
[self pollAudioForMessage:message atIndexPath:indexPath retryCount:0 maxRetries:5];
|
||||
// 开始轮询请求(最多10次,每次间隔1秒,共10秒)
|
||||
[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];
|
||||
|
||||
// 开始轮询请求(最多10次,每次间隔1秒,共10秒)
|
||||
[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 {
|
||||
|
||||
Reference in New Issue
Block a user