@@ -1034,11 +1034,126 @@ function init_app() {
10341034 // 先显示选择提示
10351035 showStatusToast ( window . t ? window . t ( 'app.deviceSelected' , { device : deviceName } ) : `已选择 ${ deviceName } ` , 3000 ) ;
10361036 // 延迟重启录音,让用户看到选择提示
1037- await stopMicCapture ( ) ;
1038- // 等待一小段时间,确保选择提示显示出来
1039- await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
1040- if ( wasRecording ) {
1041- await startMicCapture ( ) ;
1037+
1038+ // 保存需要恢复的状态
1039+ const shouldRestartProactiveVision = proactiveVisionEnabled && isRecording ;
1040+ const shouldRestartScreening = videoSenderInterval !== undefined && videoSenderInterval !== null ;
1041+
1042+ // 防止并发切换导致状态混乱
1043+ if ( window . _isSwitchingMicDevice ) {
1044+ console . warn ( '设备切换中,请稍后再试' ) ;
1045+ showStatusToast ( window . t ? window . t ( 'app.deviceSwitching' ) : '设备切换中...' , 2000 ) ;
1046+ return ;
1047+ }
1048+ window . _isSwitchingMicDevice = true ;
1049+
1050+ try {
1051+ // 停止语音期间主动视觉定时
1052+ stopProactiveVisionDuringSpeech ( ) ;
1053+ // 停止屏幕共享
1054+ stopScreening ( ) ;
1055+ // 停止静音检测
1056+ stopSilenceDetection ( ) ;
1057+ // 清理输入analyser
1058+ inputAnalyser = null ;
1059+ // 停止所有轨道
1060+ if ( stream instanceof MediaStream ) {
1061+ stream . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
1062+ stream = null ;
1063+ }
1064+ // 清理 AudioContext 本地资源
1065+ if ( audioContext ) {
1066+ if ( audioContext . state !== 'closed' ) {
1067+ await audioContext . close ( ) . catch ( ( e ) => console . warn ( 'AudioContext close 失败:' , e ) ) ;
1068+ }
1069+ audioContext = null ;
1070+ }
1071+ workletNode = null ;
1072+
1073+ // 等待一小段时间,确保选择提示显示出来
1074+ await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
1075+
1076+ if ( wasRecording ) {
1077+ await startMicCapture ( ) ;
1078+
1079+ // 重启屏幕共享(如果之前正在共享)
1080+ if ( shouldRestartScreening ) {
1081+ if ( typeof startScreenSharing === 'function' ) {
1082+ try {
1083+ await startScreenSharing ( ) ;
1084+ } catch ( e ) {
1085+ console . warn ( '重启屏幕共享失败:' , e ) ;
1086+ }
1087+ }
1088+ }
1089+ // 重启主动视觉(如果之前已启用)
1090+ if ( shouldRestartProactiveVision ) {
1091+ startProactiveVisionDuringSpeech ( ) ;
1092+ }
1093+ }
1094+ } catch ( e ) {
1095+ console . error ( '切换麦克风设备失败:' , e ) ;
1096+ showStatusToast ( window . t ? window . t ( 'app.deviceSwitchFailed' ) : '设备切换失败' , 3000 ) ;
1097+
1098+ // 完整清理:重置状态
1099+ isRecording = false ;
1100+ window . isRecording = false ;
1101+
1102+ // 重置所有按钮状态(参考 stopMicCapture 逻辑)
1103+ micButton . classList . remove ( 'recording' , 'active' ) ;
1104+ muteButton . classList . remove ( 'recording' , 'active' ) ;
1105+ screenButton . classList . remove ( 'active' ) ;
1106+ if ( stopButton ) stopButton . classList . remove ( 'recording' , 'active' ) ;
1107+
1108+ // 同步浮动按钮状态
1109+ syncFloatingMicButtonState ( false ) ;
1110+ syncFloatingScreenButtonState ( false ) ;
1111+
1112+ // 启用/禁用按钮状态
1113+ micButton . disabled = false ;
1114+ muteButton . disabled = true ;
1115+ screenButton . disabled = true ;
1116+ if ( stopButton ) stopButton . disabled = true ;
1117+
1118+ // 显示文本输入区域
1119+ const textInputArea = document . getElementById ( 'text-input-area' ) ;
1120+ if ( textInputArea ) {
1121+ textInputArea . classList . remove ( 'hidden' ) ;
1122+ }
1123+
1124+ // 清理资源
1125+ stopScreening ( ) ;
1126+ stopSilenceDetection ( ) ;
1127+ inputAnalyser = null ;
1128+
1129+ if ( stream instanceof MediaStream ) {
1130+ stream . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
1131+ stream = null ;
1132+ }
1133+
1134+ if ( audioContext ) {
1135+ if ( audioContext . state !== 'closed' ) {
1136+ await audioContext . close ( ) . catch ( ( err ) => console . warn ( 'AudioContext close 失败:' , err ) ) ;
1137+ }
1138+ audioContext = null ;
1139+ }
1140+ workletNode = null ;
1141+
1142+ // 通知后端
1143+ if ( socket . readyState === WebSocket . OPEN ) {
1144+ socket . send ( JSON . stringify ( { action : 'pause_session' } ) ) ;
1145+ }
1146+
1147+ // 如果主动搭话/主动视觉已启用,重置并开始定时
1148+ if ( proactiveChatEnabled || proactiveVisionEnabled ) {
1149+ lastUserInputTime = Date . now ( ) ;
1150+ resetProactiveChatBackoff ( ) ;
1151+ }
1152+
1153+ window . _isSwitchingMicDevice = false ;
1154+ return ;
1155+ } finally {
1156+ window . _isSwitchingMicDevice = false ;
10421157 }
10431158 } else {
10441159 // 如果不在录音,直接显示选择提示
@@ -2524,7 +2639,10 @@ function init_app() {
25242639
25252640 // 停止录屏
25262641 function stopScreening ( ) {
2527- if ( videoSenderInterval ) clearInterval ( videoSenderInterval ) ;
2642+ if ( videoSenderInterval ) {
2643+ clearInterval ( videoSenderInterval ) ;
2644+ videoSenderInterval = null ;
2645+ }
25282646 }
25292647
25302648 // 停止录音
0 commit comments