// // JMTTSManager.m // JSTopBusinessModule // // Created by huangzhongping on 2025/3/3. // #import "JMTTSManager.h" #import "JMMobileConfigSwitch.h" #import #import @interface JMTTSManager() @property (nonatomic,strong) AVSpeechSynthesizer *synthesizer; @property (nonatomic,assign) NSInteger maxPlayCount; @property (nonatomic, assign) NSTimeInterval lastCancelTime; @property (nonatomic, assign) BOOL isCancelAll; @property (nonatomic, assign) BOOL hasShownPermissionAlert; @property (nonatomic, assign) BOOL enableOptimizeMircoPhoneAlertCount; @end @implementation JMTTSManager singleton_implementation(JMTTSManager) - (instancetype)init{ self = [super init]; if (self) { self.synthesizer = [[AVSpeechSynthesizer alloc] init]; self.synthesizer.delegate = self; self.lastCancelTime = 0.0; self.enableOptimizeMircoPhoneAlertCount = [JMMobileConfigSwitch commonFetchLocalSwitch:JMMobileConfigSpaceAppOptimizeKey config:JMMobileConfigOptimizeMircoPhoneAlertCountKey key:@"enable" defaultValue:YES]; } return self; } - (void)playVoiceMsgInQueen:(NSString*)msg{ if (!msg) { return; } if ([self enableOptimizeTTSManager]) { [JMCommonUtils runInMainThread:^{ [self checkMicrophonePermission:msg cancelAll:NO]; }]; } else { [self checkMicrophonePermission:msg cancelAll:NO]; } } - (void)playVoiceMsgAndCancelAll:(NSString*)msg{ if (!msg) { return; } if ([self enableOptimizeTTSManager]) { [JMCommonUtils runInMainThread:^{ [self checkMicrophonePermission:msg cancelAll:YES]; }]; } else { [self checkMicrophonePermission:msg cancelAll:YES]; } } - (void)playMsg:(NSString*)msg cancelAll:(BOOL)cancelAll{ NSTimeInterval currentTS = [[NSDate date] timeIntervalSince1970]; NSString *value = [[JDConfigManager sharedJDConfigManager] getValueWithCode:JMConfigCodeTTSGap]; NSInteger gap = 0; if(value && value.length){ gap = value.integerValue; } if(self.playType == 2){ //TODO: 原来老功能,待验证是否需要删除 // if ((currentTS - self.lastCancelTime) < gap && self.lastCancelTime >0 && gap >0) { // //上次取消时间与当前时间是超过40s, // return; // }else{ // self.lastCancelTime = 0; // } } AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:msg]; utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"]; if(self.synthesizer.isSpeaking){ [self.synthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate]; } [self.synthesizer speakUtterance:utterance]; } - (void)checkMicrophonePermission:(NSString*)msg cancelAll:(BOOL)cancelAll{ AVAudioSession *audioSession = [AVAudioSession sharedInstance]; AVAudioSessionRecordPermission status = [audioSession recordPermission]; switch (status) { case AVAudioSessionRecordPermissionGranted:{ [self playMsg:msg cancelAll:cancelAll]; break; } case AVAudioSessionRecordPermissionDenied:{ [self showPermissionSettings]; break; } case AVAudioSessionRecordPermissionUndetermined: { [audioSession requestRecordPermission:^(BOOL granted) { if ([self enableOptimizeTTSManager]) { [JMCommonUtils runInMainThread:^{ if (granted) { NSLog(@"用户授予了麦克风权限"); [self playMsg:msg cancelAll:cancelAll]; } else { NSLog(@"用户拒绝了麦克风权限"); [self showPermissionSettings]; } }]; } else { if (granted) { NSLog(@"用户授予了麦克风权限"); [self playMsg:msg cancelAll:cancelAll]; } else { NSLog(@"用户拒绝了麦克风权限"); [JMCommonUtils runInMainThread:^{ [self showPermissionSettings]; }]; } } }]; break; } default: // [self showPermissionSettings]; break; } } - (BOOL)hasMicrophonePermission { AVAudioSession *audioSession = [AVAudioSession sharedInstance]; AVAudioSessionRecordPermission status = [audioSession recordPermission]; return status == AVAudioSessionRecordPermissionGranted; } - (void)showMicroPhonePermissionAlert { UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"麦克风权限被拒绝" message:@"请前往设置启用麦克风权限,以便我们能够提供语音录制功能。" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"前往设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if([[UIApplication sharedApplication] canOpenURL:settingsURL]) { [[UIApplication sharedApplication] openURL:settingsURL options:@{} completionHandler:^(BOOL success){ }]; } [self resetPermissionAlertFlag]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { [self resetPermissionAlertFlag]; }]; [alert addAction:settingsAction]; [alert addAction:cancelAction]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ UIViewController *vc = [JMUtils getCurrentVC] ; if ([vc isKindOfClass:[UIViewController class]]) { [vc presentViewController:alert animated:YES completion:nil]; } }); } - (void)showPermissionSettings { if (self.enableOptimizeMircoPhoneAlertCount) { // 如果已经显示过权限提示,则不再重复显示 if (self.hasShownPermissionAlert) { return; } // 标记为已显示 self.hasShownPermissionAlert = YES; [self showMicroPhonePermissionAlert]; }else{ [self showMicroPhonePermissionAlert]; } } - (void)cancelAllVoice{ //记住当前时间 if ([self enableOptimizeTTSManager]) { [JMCommonUtils runInMainThread:^{ if ([self.synthesizer isSpeaking]) { [self.synthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate]; self.lastCancelTime = [[NSDate date] timeIntervalSince1970]; self.isCancelAll = YES; } }]; } else { [self.synthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate]; self.lastCancelTime = [[NSDate date] timeIntervalSince1970]; self.isCancelAll = YES; } } // 实现 AVSpeechSynthesizerDelegate 方法 - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didStartSpeechUtterance:(AVSpeechUtterance *)utterance { // NSLog(@"开始语音合成"); } - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance { // NSLog(@"完成语音合成"); } - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didPauseSpeechUtterance:(AVSpeechUtterance *)utterance { // NSLog(@"语音合成暂停"); } - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didContinueSpeechUtterance:(AVSpeechUtterance *)utterance { // NSLog(@"继续语音合成"); } - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didCancelSpeechUtterance:(AVSpeechUtterance *)utterance { // NSLog(@"取消语音合成"); } - (BOOL)enableOptimizeTTSManager { return [JMMobileConfigSwitch commonFetchLocalSwitch:JMMobileConfigSpaceAppOptimizeKey config:JMMobileConfigOptimizeTTSManagerKey key:@"enable" defaultValue:YES]; } - (void)resetPermissionAlertFlag { if (self.enableOptimizeMircoPhoneAlertCount) { self.hasShownPermissionAlert = NO; } } @end