1
This commit is contained in:
@@ -9,11 +9,13 @@
|
||||
#import "KBPersonaChatCell.h"
|
||||
#import "KBPersonaModel.h"
|
||||
#import "KBVoiceInputBar.h"
|
||||
#import "KBVoiceRecordManager.h"
|
||||
#import "KBVoiceToTextManager.h"
|
||||
#import "AiVM.h"
|
||||
#import "KBHUD.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface KBAIHomeVC () <UICollectionViewDelegate, UICollectionViewDataSource, KBVoiceToTextManagerDelegate>
|
||||
@interface KBAIHomeVC () <UICollectionViewDelegate, UICollectionViewDataSource, KBVoiceToTextManagerDelegate, KBVoiceRecordManagerDelegate>
|
||||
|
||||
/// 人设列表容器
|
||||
@property (nonatomic, strong) UICollectionView *collectionView;
|
||||
@@ -24,6 +26,9 @@
|
||||
/// 语音转写管理器
|
||||
@property (nonatomic, strong) KBVoiceToTextManager *voiceToTextManager;
|
||||
|
||||
/// 录音管理器
|
||||
@property (nonatomic, strong) KBVoiceRecordManager *voiceRecordManager;
|
||||
|
||||
/// 人设数据
|
||||
@property (nonatomic, strong) NSMutableArray<KBPersonaModel *> *personas;
|
||||
|
||||
@@ -66,6 +71,7 @@
|
||||
|
||||
[self setupUI];
|
||||
[self setupVoiceToTextManager];
|
||||
[self setupVoiceRecordManager];
|
||||
[self loadPersonas];
|
||||
}
|
||||
|
||||
@@ -245,9 +251,17 @@
|
||||
- (void)setupVoiceToTextManager {
|
||||
self.voiceToTextManager = [[KBVoiceToTextManager alloc] initWithInputBar:self.voiceInputBar];
|
||||
self.voiceToTextManager.delegate = self;
|
||||
self.voiceToTextManager.deepgramEnabled = NO;
|
||||
[self.voiceToTextManager prepareConnection];
|
||||
}
|
||||
|
||||
/// 5:录音管理
|
||||
- (void)setupVoiceRecordManager {
|
||||
self.voiceRecordManager = [[KBVoiceRecordManager alloc] init];
|
||||
self.voiceRecordManager.delegate = self;
|
||||
self.voiceRecordManager.minRecordDuration = 1.0;
|
||||
}
|
||||
|
||||
- (NSInteger)currentCompanionId {
|
||||
if (self.personas.count == 0) {
|
||||
return 0;
|
||||
@@ -381,4 +395,38 @@
|
||||
NSLog(@"[KBAIHomeVC] 语音识别失败:%@", error.localizedDescription);
|
||||
}
|
||||
|
||||
- (void)voiceToTextManagerDidBeginRecording:(KBVoiceToTextManager *)manager {
|
||||
[self.voiceRecordManager startRecording];
|
||||
}
|
||||
|
||||
- (void)voiceToTextManagerDidEndRecording:(KBVoiceToTextManager *)manager {
|
||||
[self.voiceRecordManager stopRecording];
|
||||
}
|
||||
|
||||
- (void)voiceToTextManagerDidCancelRecording:(KBVoiceToTextManager *)manager {
|
||||
[self.voiceRecordManager cancelRecording];
|
||||
}
|
||||
|
||||
#pragma mark - KBVoiceRecordManagerDelegate
|
||||
|
||||
- (void)voiceRecordManager:(KBVoiceRecordManager *)manager
|
||||
didFinishRecordingAtURL:(NSURL *)fileURL
|
||||
duration:(NSTimeInterval)duration {
|
||||
NSDictionary *attributes = [[NSFileManager defaultManager]
|
||||
attributesOfItemAtPath:fileURL.path
|
||||
error:nil];
|
||||
unsigned long long fileSize = [attributes[NSFileSize] unsignedLongLongValue];
|
||||
NSLog(@"[KBAIHomeVC] 录音完成,时长: %.2fs,大小: %llu bytes", duration, fileSize);
|
||||
}
|
||||
|
||||
- (void)voiceRecordManagerDidRecordTooShort:(KBVoiceRecordManager *)manager {
|
||||
NSLog(@"[KBAIHomeVC] 录音过短,已忽略");
|
||||
[KBHUD showError:KBLocalized(@"录音时间过短,请重新录音")];
|
||||
}
|
||||
|
||||
- (void)voiceRecordManager:(KBVoiceRecordManager *)manager
|
||||
didFailWithError:(NSError *)error {
|
||||
NSLog(@"[KBAIHomeVC] 录音失败:%@", error.localizedDescription);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -44,6 +44,8 @@ typedef void (^AiVMMessageCompletion)(KBAiMessageResponse *_Nullable response,
|
||||
|
||||
typedef void (^AiVMAudioURLCompletion)(NSString *_Nullable audioURL,
|
||||
NSError *_Nullable error);
|
||||
typedef void (^AiVMUploadAudioCompletion)(NSString *_Nullable fileURL,
|
||||
NSError *_Nullable error);
|
||||
|
||||
@interface AiVM : NSObject
|
||||
|
||||
@@ -58,6 +60,10 @@ typedef void (^AiVMAudioURLCompletion)(NSString *_Nullable audioURL,
|
||||
- (void)requestAudioWithAudioId:(NSString *)audioId
|
||||
completion:(AiVMAudioURLCompletion)completion;
|
||||
|
||||
/// 上传语音文件(m4a)
|
||||
- (void)uploadAudioFileAtURL:(NSURL *)fileURL
|
||||
completion:(AiVMUploadAudioCompletion)completion;
|
||||
|
||||
#pragma mark - 人设相关接口
|
||||
|
||||
/// 分页查询人设列表
|
||||
|
||||
@@ -216,6 +216,51 @@ autoShowBusinessError:NO
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)uploadAudioFileAtURL:(NSURL *)fileURL
|
||||
completion:(AiVMUploadAudioCompletion)completion {
|
||||
if (!fileURL || !fileURL.isFileURL) {
|
||||
NSError *error = [NSError errorWithDomain:@"AiVM"
|
||||
code:-1
|
||||
userInfo:@{NSLocalizedDescriptionKey : @"invalid fileURL"}];
|
||||
if (completion) {
|
||||
completion(nil, error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
[[KBNetworkManager shared] uploadFile:API_AI_AUDIO_UPLOAD
|
||||
fileURL:fileURL
|
||||
name:@"file"
|
||||
mimeType:@"audio/mp4"
|
||||
parameters:nil
|
||||
headers:nil
|
||||
completion:^(NSDictionary *_Nullable json,
|
||||
NSURLResponse *_Nullable response,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
if (completion) {
|
||||
completion(nil, error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *fileURLString = nil;
|
||||
id dataObj = json[@"data"];
|
||||
if ([dataObj isKindOfClass:[NSString class]]) {
|
||||
fileURLString = (NSString *)dataObj;
|
||||
} else if ([dataObj isKindOfClass:[NSDictionary class]]) {
|
||||
id urlObj = dataObj[@"url"] ?: dataObj[@"audioUrl"];
|
||||
if ([urlObj isKindOfClass:[NSString class]]) {
|
||||
fileURLString = (NSString *)urlObj;
|
||||
}
|
||||
}
|
||||
|
||||
if (completion) {
|
||||
completion(fileURLString, nil);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - 人设相关接口
|
||||
|
||||
- (void)fetchPersonasWithPageNum:(NSInteger)pageNum
|
||||
|
||||
47
keyBoard/Class/AiTalk/VM/KBVoiceRecordManager.h
Normal file
47
keyBoard/Class/AiTalk/VM/KBVoiceRecordManager.h
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// KBVoiceRecordManager.h
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2026/1/26.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, KBVoiceRecordManagerErrorCode) {
|
||||
KBVoiceRecordManagerErrorPermissionDenied = 1,
|
||||
KBVoiceRecordManagerErrorSessionFailed = 2,
|
||||
KBVoiceRecordManagerErrorRecorderFailed = 3,
|
||||
KBVoiceRecordManagerErrorTooShort = 4,
|
||||
KBVoiceRecordManagerErrorInterrupted = 5,
|
||||
};
|
||||
|
||||
@class KBVoiceRecordManager;
|
||||
|
||||
@protocol KBVoiceRecordManagerDelegate <NSObject>
|
||||
@optional
|
||||
- (void)voiceRecordManagerDidStartRecording:(KBVoiceRecordManager *)manager;
|
||||
- (void)voiceRecordManager:(KBVoiceRecordManager *)manager
|
||||
didFinishRecordingAtURL:(NSURL *)fileURL
|
||||
duration:(NSTimeInterval)duration;
|
||||
- (void)voiceRecordManagerDidCancelRecording:(KBVoiceRecordManager *)manager;
|
||||
- (void)voiceRecordManagerDidRecordTooShort:(KBVoiceRecordManager *)manager;
|
||||
- (void)voiceRecordManager:(KBVoiceRecordManager *)manager
|
||||
didFailWithError:(NSError *)error;
|
||||
@end
|
||||
|
||||
/// 录音管理器(AAC/M4A,16k,mono)
|
||||
@interface KBVoiceRecordManager : NSObject
|
||||
|
||||
@property(nonatomic, weak) id<KBVoiceRecordManagerDelegate> delegate;
|
||||
@property(nonatomic, assign) NSTimeInterval minRecordDuration;
|
||||
@property(nonatomic, assign, readonly, getter=isRecording) BOOL recording;
|
||||
|
||||
- (void)startRecording;
|
||||
- (void)stopRecording;
|
||||
- (void)cancelRecording;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
289
keyBoard/Class/AiTalk/VM/KBVoiceRecordManager.m
Normal file
289
keyBoard/Class/AiTalk/VM/KBVoiceRecordManager.m
Normal file
@@ -0,0 +1,289 @@
|
||||
//
|
||||
// KBVoiceRecordManager.m
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2026/1/26.
|
||||
//
|
||||
|
||||
#import "KBVoiceRecordManager.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
static NSString *const kKBVoiceRecordManagerErrorDomain =
|
||||
@"KBVoiceRecordManager";
|
||||
|
||||
@interface KBVoiceRecordManager () <AVAudioRecorderDelegate>
|
||||
|
||||
@property(nonatomic, strong) AVAudioRecorder *recorder;
|
||||
@property(nonatomic, strong) NSURL *currentFileURL;
|
||||
@property(nonatomic, assign) BOOL recording;
|
||||
@property(nonatomic, assign) BOOL stopping;
|
||||
@property(nonatomic, assign) BOOL cancelled;
|
||||
@property(nonatomic, assign) BOOL interrupted;
|
||||
@property(nonatomic, assign) NSTimeInterval recordStartTime;
|
||||
|
||||
@end
|
||||
|
||||
@implementation KBVoiceRecordManager
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_minRecordDuration = 1.0;
|
||||
[self setupNotifications];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - Public Methods
|
||||
|
||||
- (void)startRecording {
|
||||
if (self.recording) {
|
||||
return;
|
||||
}
|
||||
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
if (session.recordPermission == AVAudioSessionRecordPermissionUndetermined) {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[session requestRecordPermission:^(BOOL granted) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (granted) {
|
||||
[weakSelf startRecordingInternal];
|
||||
} else {
|
||||
[weakSelf reportErrorWithCode:KBVoiceRecordManagerErrorPermissionDenied
|
||||
message:@"Microphone permission denied"];
|
||||
}
|
||||
});
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.recordPermission != AVAudioSessionRecordPermissionGranted) {
|
||||
[self reportErrorWithCode:KBVoiceRecordManagerErrorPermissionDenied
|
||||
message:@"Microphone permission denied"];
|
||||
return;
|
||||
}
|
||||
|
||||
[self startRecordingInternal];
|
||||
}
|
||||
|
||||
- (void)stopRecording {
|
||||
if (!self.recording || !self.recorder) {
|
||||
return;
|
||||
}
|
||||
self.stopping = YES;
|
||||
self.cancelled = NO;
|
||||
self.interrupted = NO;
|
||||
[self.recorder stop];
|
||||
}
|
||||
|
||||
- (void)cancelRecording {
|
||||
if (!self.recording || !self.recorder) {
|
||||
return;
|
||||
}
|
||||
self.cancelled = YES;
|
||||
self.stopping = NO;
|
||||
self.interrupted = NO;
|
||||
[self.recorder stop];
|
||||
}
|
||||
|
||||
#pragma mark - Private Methods
|
||||
|
||||
- (void)startRecordingInternal {
|
||||
NSError *sessionError = nil;
|
||||
if (![self configureSession:&sessionError]) {
|
||||
[self reportErrorWithCode:KBVoiceRecordManagerErrorSessionFailed
|
||||
message:sessionError.localizedDescription ?: @"Session error"];
|
||||
return;
|
||||
}
|
||||
|
||||
NSURL *fileURL = [self generateFileURL];
|
||||
if (!fileURL) {
|
||||
[self reportErrorWithCode:KBVoiceRecordManagerErrorRecorderFailed
|
||||
message:@"Invalid file URL"];
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *settings = @{
|
||||
AVFormatIDKey : @(kAudioFormatMPEG4AAC),
|
||||
AVSampleRateKey : @(16000),
|
||||
AVNumberOfChannelsKey : @(1),
|
||||
AVEncoderAudioQualityKey : @(AVAudioQualityMedium),
|
||||
AVEncoderBitRateKey : @(32000)
|
||||
};
|
||||
|
||||
NSError *recorderError = nil;
|
||||
self.recorder = [[AVAudioRecorder alloc] initWithURL:fileURL
|
||||
settings:settings
|
||||
error:&recorderError];
|
||||
if (recorderError || !self.recorder) {
|
||||
[self reportErrorWithCode:KBVoiceRecordManagerErrorRecorderFailed
|
||||
message:recorderError.localizedDescription ?: @"Recorder error"];
|
||||
return;
|
||||
}
|
||||
|
||||
self.currentFileURL = fileURL;
|
||||
self.recorder.delegate = self;
|
||||
self.recorder.meteringEnabled = NO;
|
||||
self.stopping = NO;
|
||||
self.cancelled = NO;
|
||||
self.interrupted = NO;
|
||||
|
||||
if (![self.recorder prepareToRecord] || ![self.recorder record]) {
|
||||
[self reportErrorWithCode:KBVoiceRecordManagerErrorRecorderFailed
|
||||
message:@"Recorder start failed"];
|
||||
return;
|
||||
}
|
||||
|
||||
self.recordStartTime = CACurrentMediaTime();
|
||||
self.recording = YES;
|
||||
if ([self.delegate respondsToSelector:@selector(voiceRecordManagerDidStartRecording:)]) {
|
||||
[self.delegate voiceRecordManagerDidStartRecording:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)configureSession:(NSError **)error {
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
BOOL success =
|
||||
[session setCategory:AVAudioSessionCategoryPlayAndRecord
|
||||
mode:AVAudioSessionModeVoiceChat
|
||||
options:(AVAudioSessionCategoryOptionDefaultToSpeaker |
|
||||
AVAudioSessionCategoryOptionAllowBluetooth)
|
||||
error:error];
|
||||
if (!success) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
[session setPreferredSampleRate:16000 error:nil];
|
||||
[session setPreferredIOBufferDuration:0.02 error:nil];
|
||||
if ([session respondsToSelector:@selector(setPreferredInputNumberOfChannels:error:)]) {
|
||||
[session setPreferredInputNumberOfChannels:1 error:nil];
|
||||
}
|
||||
|
||||
return [session setActive:YES error:error];
|
||||
}
|
||||
|
||||
- (NSURL *)generateFileURL {
|
||||
NSString *fileName =
|
||||
[NSString stringWithFormat:@"kb_record_%@.m4a",
|
||||
@((NSInteger)([[NSDate date] timeIntervalSince1970] * 1000))];
|
||||
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
|
||||
return [NSURL fileURLWithPath:path];
|
||||
}
|
||||
|
||||
- (void)cleanupRecorder {
|
||||
self.recording = NO;
|
||||
self.stopping = NO;
|
||||
self.cancelled = NO;
|
||||
self.interrupted = NO;
|
||||
self.recordStartTime = 0;
|
||||
self.recorder.delegate = nil;
|
||||
self.recorder = nil;
|
||||
self.currentFileURL = nil;
|
||||
[[AVAudioSession sharedInstance] setActive:NO error:nil];
|
||||
}
|
||||
|
||||
- (void)removeCurrentFileIfNeeded {
|
||||
if (!self.currentFileURL) {
|
||||
return;
|
||||
}
|
||||
[[NSFileManager defaultManager] removeItemAtURL:self.currentFileURL error:nil];
|
||||
}
|
||||
|
||||
- (void)reportErrorWithCode:(KBVoiceRecordManagerErrorCode)code
|
||||
message:(NSString *)message {
|
||||
NSError *error = [NSError errorWithDomain:kKBVoiceRecordManagerErrorDomain
|
||||
code:code
|
||||
userInfo:@{NSLocalizedDescriptionKey : message ?: @""}];
|
||||
if ([self.delegate respondsToSelector:@selector(voiceRecordManager:didFailWithError:)]) {
|
||||
[self.delegate voiceRecordManager:self didFailWithError:error];
|
||||
}
|
||||
[self cleanupRecorder];
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
- (void)setupNotifications {
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(handleInterruption:)
|
||||
name:AVAudioSessionInterruptionNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(handleRouteChange:)
|
||||
name:AVAudioSessionRouteChangeNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)handleInterruption:(NSNotification *)notification {
|
||||
NSDictionary *info = notification.userInfo;
|
||||
AVAudioSessionInterruptionType type =
|
||||
[info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
|
||||
if (type == AVAudioSessionInterruptionTypeBegan && self.recording) {
|
||||
self.interrupted = YES;
|
||||
[self.recorder stop];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleRouteChange:(NSNotification *)notification {
|
||||
NSDictionary *info = notification.userInfo;
|
||||
AVAudioSessionRouteChangeReason reason =
|
||||
[info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
|
||||
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable && self.recording) {
|
||||
self.interrupted = YES;
|
||||
[self.recorder stop];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - AVAudioRecorderDelegate
|
||||
|
||||
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder
|
||||
successfully:(BOOL)flag {
|
||||
if (!self.recording) {
|
||||
[self cleanupRecorder];
|
||||
return;
|
||||
}
|
||||
|
||||
NSTimeInterval duration = recorder.currentTime;
|
||||
if (duration <= 0 && self.recordStartTime > 0) {
|
||||
duration = CACurrentMediaTime() - self.recordStartTime;
|
||||
}
|
||||
NSURL *fileURL = self.currentFileURL;
|
||||
|
||||
if (self.cancelled || !flag) {
|
||||
[self removeCurrentFileIfNeeded];
|
||||
if ([self.delegate respondsToSelector:@selector(voiceRecordManagerDidCancelRecording:)]) {
|
||||
[self.delegate voiceRecordManagerDidCancelRecording:self];
|
||||
}
|
||||
[self cleanupRecorder];
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.interrupted) {
|
||||
[self removeCurrentFileIfNeeded];
|
||||
[self reportErrorWithCode:KBVoiceRecordManagerErrorInterrupted
|
||||
message:@"Audio session interrupted"];
|
||||
return;
|
||||
}
|
||||
|
||||
if (duration < self.minRecordDuration) {
|
||||
[self removeCurrentFileIfNeeded];
|
||||
if ([self.delegate respondsToSelector:@selector(voiceRecordManagerDidRecordTooShort:)]) {
|
||||
[self.delegate voiceRecordManagerDidRecordTooShort:self];
|
||||
}
|
||||
[self cleanupRecorder];
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileURL && [self.delegate respondsToSelector:@selector(voiceRecordManager:didFinishRecordingAtURL:duration:)]) {
|
||||
[self.delegate voiceRecordManager:self didFinishRecordingAtURL:fileURL duration:duration];
|
||||
}
|
||||
[self cleanupRecorder];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property(nonatomic, weak) id<KBVoiceToTextManagerDelegate> delegate;
|
||||
@property(nonatomic, weak, readonly) KBVoiceInputBar *inputBar;
|
||||
@property(nonatomic, assign) BOOL deepgramEnabled;
|
||||
|
||||
- (instancetype)initWithInputBar:(KBVoiceInputBar *)inputBar;
|
||||
- (void)prepareConnection;
|
||||
|
||||
@@ -20,12 +20,26 @@
|
||||
|
||||
@implementation KBVoiceToTextManager
|
||||
|
||||
- (void)setDeepgramEnabled:(BOOL)deepgramEnabled {
|
||||
if (_deepgramEnabled == deepgramEnabled) {
|
||||
return;
|
||||
}
|
||||
_deepgramEnabled = deepgramEnabled;
|
||||
if (!deepgramEnabled) {
|
||||
[self.deepgramManager cancel];
|
||||
[self resetTranscript];
|
||||
} else {
|
||||
[self.deepgramManager prepareConnection];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithInputBar:(KBVoiceInputBar *)inputBar {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_inputBar = inputBar;
|
||||
_inputBar.delegate = self;
|
||||
_fullText = [[NSMutableString alloc] init];
|
||||
_deepgramEnabled = YES;
|
||||
[self setupDeepgram];
|
||||
}
|
||||
return self;
|
||||
@@ -38,10 +52,16 @@
|
||||
#pragma mark - Public Methods
|
||||
|
||||
- (void)prepareConnection {
|
||||
if (!self.deepgramEnabled) {
|
||||
return;
|
||||
}
|
||||
[self.deepgramManager prepareConnection];
|
||||
}
|
||||
|
||||
- (void)disconnect {
|
||||
if (!self.deepgramEnabled) {
|
||||
return;
|
||||
}
|
||||
[self.deepgramManager disconnect];
|
||||
}
|
||||
|
||||
@@ -70,8 +90,12 @@
|
||||
|
||||
- (void)voiceInputBarDidBeginRecording:(KBVoiceInputBar *)inputBar {
|
||||
[self resetTranscript];
|
||||
inputBar.statusText = @"正在连接...";
|
||||
[self.deepgramManager start];
|
||||
if (self.deepgramEnabled) {
|
||||
inputBar.statusText = @"正在连接...";
|
||||
[self.deepgramManager start];
|
||||
} else {
|
||||
inputBar.statusText = @"正在录音...";
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector
|
||||
(voiceToTextManagerDidBeginRecording:)]) {
|
||||
@@ -80,8 +104,12 @@
|
||||
}
|
||||
|
||||
- (void)voiceInputBarDidEndRecording:(KBVoiceInputBar *)inputBar {
|
||||
inputBar.statusText = @"正在识别...";
|
||||
[self.deepgramManager stopAndFinalize];
|
||||
if (self.deepgramEnabled) {
|
||||
inputBar.statusText = @"正在识别...";
|
||||
[self.deepgramManager stopAndFinalize];
|
||||
} else {
|
||||
inputBar.statusText = @"录音结束";
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector
|
||||
(voiceToTextManagerDidEndRecording:)]) {
|
||||
@@ -92,7 +120,9 @@
|
||||
- (void)voiceInputBarDidCancelRecording:(KBVoiceInputBar *)inputBar {
|
||||
inputBar.statusText = @"已取消";
|
||||
[self resetTranscript];
|
||||
[self.deepgramManager cancel];
|
||||
if (self.deepgramEnabled) {
|
||||
[self.deepgramManager cancel];
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector
|
||||
(voiceToTextManagerDidCancelRecording:)]) {
|
||||
@@ -103,10 +133,16 @@
|
||||
#pragma mark - DeepgramStreamingManagerDelegate
|
||||
|
||||
- (void)deepgramStreamingManagerDidConnect {
|
||||
if (!self.deepgramEnabled) {
|
||||
return;
|
||||
}
|
||||
self.inputBar.statusText = @"正在聆听...";
|
||||
}
|
||||
|
||||
- (void)deepgramStreamingManagerDidDisconnect:(NSError *_Nullable)error {
|
||||
if (!self.deepgramEnabled) {
|
||||
return;
|
||||
}
|
||||
if (!error) {
|
||||
return;
|
||||
}
|
||||
@@ -119,10 +155,16 @@
|
||||
}
|
||||
|
||||
- (void)deepgramStreamingManagerDidUpdateRMS:(float)rms {
|
||||
if (!self.deepgramEnabled) {
|
||||
return;
|
||||
}
|
||||
[self.inputBar updateVolumeRMS:rms];
|
||||
}
|
||||
|
||||
- (void)deepgramStreamingManagerDidReceiveInterimTranscript:(NSString *)text {
|
||||
if (!self.deepgramEnabled) {
|
||||
return;
|
||||
}
|
||||
NSString *displayText = text ?: @"";
|
||||
if (self.fullText.length > 0 && displayText.length > 0) {
|
||||
displayText =
|
||||
@@ -141,6 +183,9 @@
|
||||
}
|
||||
|
||||
- (void)deepgramStreamingManagerDidReceiveFinalTranscript:(NSString *)text {
|
||||
if (!self.deepgramEnabled) {
|
||||
return;
|
||||
}
|
||||
if (text.length > 0) {
|
||||
if (self.fullText.length > 0) {
|
||||
[self.fullText appendString:@" "];
|
||||
@@ -160,6 +205,9 @@
|
||||
}
|
||||
|
||||
- (void)deepgramStreamingManagerDidFail:(NSError *)error {
|
||||
if (!self.deepgramEnabled) {
|
||||
return;
|
||||
}
|
||||
self.inputBar.statusText = @"识别失败";
|
||||
if ([self.delegate respondsToSelector:@selector
|
||||
(voiceToTextManager:didFailWithError:)]) {
|
||||
|
||||
Reference in New Issue
Block a user