diff --git a/lib/data/services/ble_manager.dart b/lib/data/services/ble_manager.dart index f367f466b..4572b2ba0 100644 --- a/lib/data/services/ble_manager.dart +++ b/lib/data/services/ble_manager.dart @@ -195,6 +195,11 @@ class BleManager extends GetxService { // 设备信息缓存 final deviceInfo = Rx(null); // 使用 Rx 包装 + //添加编码器状态跟踪 + final _isCodecActive = false.obs; + bool get isCodecActive => _isCodecActive.value; + RxBool get isCodecActiveRx => _isCodecActive; + // 订阅 StreamSubscription? _statusSubscription; StreamSubscription? _dataSubscription; @@ -368,6 +373,7 @@ class BleManager extends GetxService { print(" CONTROL_CODEC: $commandId,$data"); final bool codecSuccess = _isCodecControlSuccess(data); + _isCodecActive.value = codecSuccess; // 更新编解码器状态 if (codecSuccess) { print("lxm--编解码控制成功 (rawData 第二字节非 0)"); diff --git a/lib/modules/translation/controllers/translation_controller.dart b/lib/modules/translation/controllers/translation_controller.dart index f15b1a0d0..ae6019fbf 100644 --- a/lib/modules/translation/controllers/translation_controller.dart +++ b/lib/modules/translation/controllers/translation_controller.dart @@ -87,6 +87,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver { RxInt activeSpeaker = 0.obs; // 0=无激活, 1=按钮1, 2=按钮2 RxInt lastActiveSpeaker = 0.obs; // 上一次状态 final isRecognizing = false.obs; + final isPreparing = false.obs; final isTranslating = false.obs; final isTtsEnabled = true.obs; final isRecording = false.obs; @@ -844,10 +845,16 @@ class TranslationController extends GetxController with WidgetsBindingObserver { await _initializeSession(); await _configureAudioMode(); if (currentMode.value != 'faceToFace') { + if ((currentMode.value == 'audioVideo' || + currentMode.value == 'call') && + !bleManager.isCodecActive) { + return; + } await _startAsrService(); await _finalizeRecognitionStart(); } } catch (e) { + isPreparing.value = false; isRecognizing.value = false; Logger.error('启动语音识别失败: ${e.toString()}'); } @@ -893,7 +900,18 @@ class TranslationController extends GetxController with WidgetsBindingObserver { /// 配置音视频模式 Future _configureAudioVideoMode() async { - bleManager.openDecoder(); + isPreparing.value = true; + await bleManager.openDecoder(); + int attempts = 0; + while (!bleManager.isCodecActive && attempts < 10) { + await Future.delayed(const Duration(milliseconds: 150)); + attempts++; + } + isPreparing.value = false; + if (!bleManager.isCodecActive) { + Get.snackbar('tip'.tr, 'operationFailedPleaseRetry'.tr); + throw Exception('lxm--BLE编解码打开失败'); + } _audioSourceType = true; isTtsEnabled.value = false; Logger.info('发送ble音乐或者通话远端声音'); @@ -907,7 +925,18 @@ class TranslationController extends GetxController with WidgetsBindingObserver { ]; Logger.info('1初始化AST服务,支持语言: $callModeLanguages'); //ast=asr+translate await _astService.initialize(supportedLanguages: callModeLanguages); - bleManager.openA2DPDecoder(); + isPreparing.value = true; + await bleManager.openA2DPDecoder(); + int attempts = 0; + while (!bleManager.isCodecActive && attempts < 10) { + await Future.delayed(const Duration(milliseconds: 150)); + attempts++; + } + isPreparing.value = false; + if (!bleManager.isCodecActive) { + Get.snackbar('tip'.tr, 'operationFailedPleaseRetry'.tr); + throw Exception('lxm--BLE编解码打开失败'); + } _audioSourceType = true; isTtsEnabled.value = false; Logger.info('发送ble系统mic和dac(音乐或者通话远端)声音'); @@ -1379,6 +1408,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver { /// 停止所有操作 Future stopAll() async { try { + isPreparing.value = false; if (currentMode.value == "audioVideo" || currentMode.value == "call") { bleManager.closeCodec(); Logger.info('关闭ble编解码器'); diff --git a/lib/modules/translation/views/translation_view.dart b/lib/modules/translation/views/translation_view.dart index c089c0ff2..595126454 100644 --- a/lib/modules/translation/views/translation_view.dart +++ b/lib/modules/translation/views/translation_view.dart @@ -467,134 +467,122 @@ class TranslationView extends GetView { ), ], ), - child: Obx(() { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - // 原有的按钮区域 - if (controller.currentMode.value == 'faceToFace') - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // 耳机图标按钮 (说话者1)-显示源语言 - Obx(() => _buildHoldToTalkButton( - isDarkMode: isDarkMode, - speakerId: 1, - activeSpeaker: controller.activeSpeaker.value, - icon: Icons.mic_none, - label: controller.sourceLanguage.value - .split(' ')[0] - .tr, // 源语言名称 - )), - SizedBox(width: 16.w), - // 手机图标按钮 (说话者2)-显示目标语言 - Obx(() => _buildHoldToTalkButton( - isDarkMode: isDarkMode, - speakerId: 2, - activeSpeaker: controller.activeSpeaker.value, - icon: Icons.mic_none, - label: controller.targetLanguage.value - .split(' ')[0] - .tr, // 目标语言名称 - )), - ], - ) - else - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // 主要控制按钮 - 开始/暂停语音识别 - Obx(() { - final isRecognizing = controller.isRecognizing.value; - return GestureDetector( - onTap: () { - if (isRecognizing) { - controller.stopAll(); - } else { - controller.startRecognition(); - } - }, - child: Container( - width: 60.w, - height: 60.w, - decoration: BoxDecoration( - color: isRecognizing - ? (isDarkMode ? Colors.red[700] : Colors.red) - : (isDarkMode ? AppColors.primary : Colors.blue), - shape: BoxShape.circle, - ), - child: Icon( - isRecognizing ? Icons.pause : Icons.play_arrow, - color: Colors.white, - size: 30.sp, - ), - ), - ); - }), - ], - ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 原有的按钮区域 + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // 主要控制按钮 - 开始/暂停语音识别 + Obx(() { + final isRecognizing = controller.isRecognizing.value; + final isPreparing = controller.isPreparing.value; + return GestureDetector( + onTap: () { + if (isRecognizing) { + controller.stopAll(); + } else { + controller.startRecognition(); + } + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + width: 60.w, + height: 60.w, + transform: isPreparing + ? (Matrix4.identity()..scale(1.08)) + : Matrix4.identity(), + decoration: BoxDecoration( + color: isRecognizing + ? (isDarkMode ? Colors.red[700] : Colors.red) + : (isDarkMode ? AppColors.primary : Colors.blue), + shape: BoxShape.circle, + boxShadow: isPreparing + ? [ + BoxShadow( + color: (isDarkMode + ? AppColors.primary + : Colors.blue) + .withOpacity(0.5), + blurRadius: 12, + spreadRadius: 2, + ) + ] + : [], + ), + child: Icon( + isRecognizing ? Icons.pause : Icons.play_arrow, + color: Colors.white, + size: 30.sp, + ), + ), + ); + }), + ], + ), - // 添加间距 - //SizedBox(height: 16.h), + // 添加间距 + //SizedBox(height: 16.h), - // 语音识别结果显示框 - // Container( - // width: double.infinity, - // padding: EdgeInsets.all(12.w), - // decoration: BoxDecoration( - // color: isDarkMode ? Colors.grey[800] : Colors.grey[100], - // borderRadius: BorderRadius.circular(8.r), - // border: Border.all( - // color: isDarkMode ? Colors.grey[600]! : Colors.grey[300]!, - // width: 1, - // ), - // ), - // child: Obx(() { - // // 显示最新的语音识别结果 - // final recognitionText = controller.latestRecognitionText.value; - // final detectedLanguage = controller.detectedLanguageCode1.value; + // 语音识别结果显示框 + // Container( + // width: double.infinity, + // padding: EdgeInsets.all(12.w), + // decoration: BoxDecoration( + // color: isDarkMode ? Colors.grey[800] : Colors.grey[100], + // borderRadius: BorderRadius.circular(8.r), + // border: Border.all( + // color: isDarkMode ? Colors.grey[600]! : Colors.grey[300]!, + // width: 1, + // ), + // ), + // child: Obx(() { + // // 显示最新的语音识别结果 + // final recognitionText = controller.latestRecognitionText.value; + // final detectedLanguage = controller.detectedLanguageCode1.value; - // return Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // // 标题 - // Text( - // '麦头语音识别结果', - // style: TextStyle( - // fontSize: 12.sp, - // color: isDarkMode ? Colors.grey[400] : Colors.grey[600], - // fontWeight: FontWeight.w500, - // ), - // ), - // SizedBox(height: 4.h), - // // 识别文本 - 这里显示 finalResult1 的内容 - // Text( - // recognitionText.isEmpty ? '等待语音输入...' : recognitionText, - // style: TextStyle( - // fontSize: 14.sp, - // color: isDarkMode ? Colors.white : Colors.black87, - // height: 1.3, - // ), - // ), - // // 检测到的语言(如果有) - // if (detectedLanguage.isNotEmpty) ...[ - // SizedBox(height: 4.h), - // Text( - // '检测语言: $detectedLanguage', - // style: TextStyle( - // fontSize: 10.sp, - // color: - // isDarkMode ? Colors.grey[500] : Colors.grey[500], - // ), - // ), - // ], - // ], - // ); - // }), - // ), - ], - ); - }), + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // // 标题 + // Text( + // '麦头语音识别结果', + // style: TextStyle( + // fontSize: 12.sp, + // color: isDarkMode ? Colors.grey[400] : Colors.grey[600], + // fontWeight: FontWeight.w500, + // ), + // ), + // SizedBox(height: 4.h), + // // 识别文本 - 这里显示 finalResult1 的内容 + // Text( + // recognitionText.isEmpty ? '等待语音输入...' : recognitionText, + // style: TextStyle( + // fontSize: 14.sp, + // color: isDarkMode ? Colors.white : Colors.black87, + // height: 1.3, + // ), + // ), + // // 检测到的语言(如果有) + // if (detectedLanguage.isNotEmpty) ...[ + // SizedBox(height: 4.h), + // Text( + // '检测语言: $detectedLanguage', + // style: TextStyle( + // fontSize: 10.sp, + // color: + // isDarkMode ? Colors.grey[500] : Colors.grey[500], + // ), + // ), + // ], + // ], + // ); + // }), + // ), + ], + ), ); }