Browse Source

feat(translation): 添加编解码器状态跟踪和准备状态动画

- 在BleManager中添加isCodecActive状态跟踪
- 在TranslationController中添加isPreparing状态
- 为准备状态添加动画效果和视觉反馈
- 增加编解码器激活检查逻辑
newdev_shunjiawei
lxm 11 months ago
parent
commit
d48b43eb50
  1. 6
      lib/data/services/ble_manager.dart
  2. 34
      lib/modules/translation/controllers/translation_controller.dart
  3. 238
      lib/modules/translation/views/translation_view.dart

6
lib/data/services/ble_manager.dart

@ -195,6 +195,11 @@ class BleManager extends GetxService {
// 设备信息缓存 // 设备信息缓存
final deviceInfo = Rx<DeviceInfo?>(null); // 使用 Rx 包装 final deviceInfo = Rx<DeviceInfo?>(null); // 使用 Rx 包装
//添加编码器状态跟踪
final _isCodecActive = false.obs;
bool get isCodecActive => _isCodecActive.value;
RxBool get isCodecActiveRx => _isCodecActive;
// 订阅 // 订阅
StreamSubscription<dynamic>? _statusSubscription; StreamSubscription<dynamic>? _statusSubscription;
StreamSubscription<dynamic>? _dataSubscription; StreamSubscription<dynamic>? _dataSubscription;
@ -368,6 +373,7 @@ class BleManager extends GetxService {
print(" CONTROL_CODEC: $commandId,$data"); print(" CONTROL_CODEC: $commandId,$data");
final bool codecSuccess = _isCodecControlSuccess(data); final bool codecSuccess = _isCodecControlSuccess(data);
_isCodecActive.value = codecSuccess; // 更新编解码器状态
if (codecSuccess) { if (codecSuccess) {
print("lxm--编解码控制成功 (rawData 第二字节非 0)"); print("lxm--编解码控制成功 (rawData 第二字节非 0)");

34
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 activeSpeaker = 0.obs; // 0=无激活, 1=按钮1, 2=按钮2
RxInt lastActiveSpeaker = 0.obs; // 上一次状态 RxInt lastActiveSpeaker = 0.obs; // 上一次状态
final isRecognizing = false.obs; final isRecognizing = false.obs;
final isPreparing = false.obs;
final isTranslating = false.obs; final isTranslating = false.obs;
final isTtsEnabled = true.obs; final isTtsEnabled = true.obs;
final isRecording = false.obs; final isRecording = false.obs;
@ -844,10 +845,16 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
await _initializeSession(); await _initializeSession();
await _configureAudioMode(); await _configureAudioMode();
if (currentMode.value != 'faceToFace') { if (currentMode.value != 'faceToFace') {
if ((currentMode.value == 'audioVideo' ||
currentMode.value == 'call') &&
!bleManager.isCodecActive) {
return;
}
await _startAsrService(); await _startAsrService();
await _finalizeRecognitionStart(); await _finalizeRecognitionStart();
} }
} catch (e) { } catch (e) {
isPreparing.value = false;
isRecognizing.value = false; isRecognizing.value = false;
Logger.error('启动语音识别失败: ${e.toString()}'); Logger.error('启动语音识别失败: ${e.toString()}');
} }
@ -893,7 +900,18 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
/// 配置音视频模式 /// 配置音视频模式
Future<void> _configureAudioVideoMode() async { Future<void> _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; _audioSourceType = true;
isTtsEnabled.value = false; isTtsEnabled.value = false;
Logger.info('发送ble音乐或者通话远端声音'); Logger.info('发送ble音乐或者通话远端声音');
@ -907,7 +925,18 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
]; ];
Logger.info('1初始化AST服务,支持语言: $callModeLanguages'); //ast=asr+translate Logger.info('1初始化AST服务,支持语言: $callModeLanguages'); //ast=asr+translate
await _astService.initialize(supportedLanguages: callModeLanguages); 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; _audioSourceType = true;
isTtsEnabled.value = false; isTtsEnabled.value = false;
Logger.info('发送ble系统mic和dac(音乐或者通话远端)声音'); Logger.info('发送ble系统mic和dac(音乐或者通话远端)声音');
@ -1379,6 +1408,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
/// 停止所有操作 /// 停止所有操作
Future<void> stopAll() async { Future<void> stopAll() async {
try { try {
isPreparing.value = false;
if (currentMode.value == "audioVideo" || currentMode.value == "call") { if (currentMode.value == "audioVideo" || currentMode.value == "call") {
bleManager.closeCodec(); bleManager.closeCodec();
Logger.info('关闭ble编解码器'); Logger.info('关闭ble编解码器');

238
lib/modules/translation/views/translation_view.dart

@ -467,134 +467,122 @@ class TranslationView extends GetView<TranslationController> {
), ),
], ],
), ),
child: Obx(() { child: Column(
return Column( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, children: [
children: [ // 原有的按钮区域
// 原有的按钮区域 Row(
if (controller.currentMode.value == 'faceToFace') mainAxisAlignment: MainAxisAlignment.center,
Row( children: [
mainAxisAlignment: MainAxisAlignment.center, // 主要控制按钮 - 开始/暂停语音识别
children: [ Obx(() {
// 耳机图标按钮 (说话者1)-显示源语言 final isRecognizing = controller.isRecognizing.value;
Obx(() => _buildHoldToTalkButton( final isPreparing = controller.isPreparing.value;
isDarkMode: isDarkMode, return GestureDetector(
speakerId: 1, onTap: () {
activeSpeaker: controller.activeSpeaker.value, if (isRecognizing) {
icon: Icons.mic_none, controller.stopAll();
label: controller.sourceLanguage.value } else {
.split(' ')[0] controller.startRecognition();
.tr, // 源语言名称 }
)), },
SizedBox(width: 16.w), child: AnimatedContainer(
// 手机图标按钮 (说话者2)-显示目标语言 duration: const Duration(milliseconds: 300),
Obx(() => _buildHoldToTalkButton( curve: Curves.easeInOut,
isDarkMode: isDarkMode, width: 60.w,
speakerId: 2, height: 60.w,
activeSpeaker: controller.activeSpeaker.value, transform: isPreparing
icon: Icons.mic_none, ? (Matrix4.identity()..scale(1.08))
label: controller.targetLanguage.value : Matrix4.identity(),
.split(' ')[0] decoration: BoxDecoration(
.tr, // 目标语言名称 color: isRecognizing
)), ? (isDarkMode ? Colors.red[700] : Colors.red)
], : (isDarkMode ? AppColors.primary : Colors.blue),
) shape: BoxShape.circle,
else boxShadow: isPreparing
Row( ? [
mainAxisAlignment: MainAxisAlignment.center, BoxShadow(
children: [ color: (isDarkMode
// 主要控制按钮 - 开始/暂停语音识别 ? AppColors.primary
Obx(() { : Colors.blue)
final isRecognizing = controller.isRecognizing.value; .withOpacity(0.5),
return GestureDetector( blurRadius: 12,
onTap: () { spreadRadius: 2,
if (isRecognizing) { )
controller.stopAll(); ]
} else { : [],
controller.startRecognition(); ),
} child: Icon(
}, isRecognizing ? Icons.pause : Icons.play_arrow,
child: Container( color: Colors.white,
width: 60.w, size: 30.sp,
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,
),
),
);
}),
],
),
// 添加间距 // 添加间距
//SizedBox(height: 16.h), //SizedBox(height: 16.h),
// 语音识别结果显示框 // 语音识别结果显示框
// Container( // Container(
// width: double.infinity, // width: double.infinity,
// padding: EdgeInsets.all(12.w), // padding: EdgeInsets.all(12.w),
// decoration: BoxDecoration( // decoration: BoxDecoration(
// color: isDarkMode ? Colors.grey[800] : Colors.grey[100], // color: isDarkMode ? Colors.grey[800] : Colors.grey[100],
// borderRadius: BorderRadius.circular(8.r), // borderRadius: BorderRadius.circular(8.r),
// border: Border.all( // border: Border.all(
// color: isDarkMode ? Colors.grey[600]! : Colors.grey[300]!, // color: isDarkMode ? Colors.grey[600]! : Colors.grey[300]!,
// width: 1, // width: 1,
// ), // ),
// ), // ),
// child: Obx(() { // child: Obx(() {
// // 显示最新的语音识别结果 // // 显示最新的语音识别结果
// final recognitionText = controller.latestRecognitionText.value; // final recognitionText = controller.latestRecognitionText.value;
// final detectedLanguage = controller.detectedLanguageCode1.value; // final detectedLanguage = controller.detectedLanguageCode1.value;
// return Column( // return Column(
// crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
// children: [ // children: [
// // 标题 // // 标题
// Text( // Text(
// '麦头语音识别结果', // '麦头语音识别结果',
// style: TextStyle( // style: TextStyle(
// fontSize: 12.sp, // fontSize: 12.sp,
// color: isDarkMode ? Colors.grey[400] : Colors.grey[600], // color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
// fontWeight: FontWeight.w500, // fontWeight: FontWeight.w500,
// ), // ),
// ), // ),
// SizedBox(height: 4.h), // SizedBox(height: 4.h),
// // 识别文本 - 这里显示 finalResult1 的内容 // // 识别文本 - 这里显示 finalResult1 的内容
// Text( // Text(
// recognitionText.isEmpty ? '等待语音输入...' : recognitionText, // recognitionText.isEmpty ? '等待语音输入...' : recognitionText,
// style: TextStyle( // style: TextStyle(
// fontSize: 14.sp, // fontSize: 14.sp,
// color: isDarkMode ? Colors.white : Colors.black87, // color: isDarkMode ? Colors.white : Colors.black87,
// height: 1.3, // height: 1.3,
// ), // ),
// ), // ),
// // 检测到的语言(如果有) // // 检测到的语言(如果有)
// if (detectedLanguage.isNotEmpty) ...[ // if (detectedLanguage.isNotEmpty) ...[
// SizedBox(height: 4.h), // SizedBox(height: 4.h),
// Text( // Text(
// '检测语言: $detectedLanguage', // '检测语言: $detectedLanguage',
// style: TextStyle( // style: TextStyle(
// fontSize: 10.sp, // fontSize: 10.sp,
// color: // color:
// isDarkMode ? Colors.grey[500] : Colors.grey[500], // isDarkMode ? Colors.grey[500] : Colors.grey[500],
// ), // ),
// ), // ),
// ], // ],
// ], // ],
// ); // );
// }), // }),
// ), // ),
], ],
); ),
}),
); );
} }

Loading…
Cancel
Save