Browse Source

优化 项目日志 代码 减少卡顿问题

weicu
liwei1dao 6 months ago
parent
commit
dd8de4cf5b
  1. 3
      lib/core/services/floating_window_manager.dart
  2. 2
      lib/core/utils/app_lifecycle_manager.dart
  3. 270
      lib/core/utils/logger.dart
  4. 327
      lib/data/services/ble_manager.dart
  5. 23
      lib/data/services/speech_impl/azure_asr_service.dart
  6. 4
      lib/data/services/speech_impl/azure_ast_service.dart
  7. 10
      lib/data/services/usage_stats_service.dart
  8. 5
      lib/data/services/volcano_translation_service.dart
  9. 36
      lib/modules/home/controllers/home_controller.dart
  10. 4
      lib/modules/home/views/home_view.dart
  11. 72
      lib/modules/translation/controllers/translation_controller.dart
  12. 13
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  13. 12
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleAgent.kt
  14. 63
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrToAsr.kt
  15. 25
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift
  16. 3
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift
  17. 22
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/OpusAudioManager.kt
  18. 8
      local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift
  19. 63
      local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftOpusAudioProcessor.swift
  20. 35
      local_plugins/volcano_speech/android/src/main/kotlin/com/yunqiinnovation/volcano_speech/VolcanoAsrHelper.kt

3
lib/core/services/floating_window_manager.dart

@ -165,9 +165,8 @@ class FloatingWindowManager extends GetxController {
try { try {
await _api.updateContent(data.toMap()); await _api.updateContent(data.toMap());
Logger.info('悬浮窗内容已更新');
} catch (e) { } catch (e) {
Logger.error('更新悬浮窗内容失败: $e'); Logger.e('FloatingWindow', '更新悬浮窗内容失败: $e');
} }
} }

2
lib/core/utils/app_lifecycle_manager.dart

@ -79,7 +79,7 @@ class AppLifecycleManager extends GetxService with WidgetsBindingObserver {
_lastCanWakeUp = canWakeUp; _lastCanWakeUp = canWakeUp;
try { try {
Logger.info('lxm---设置是否可以ble唤醒agent: $canWakeUp'); Logger.i('AppLifecycle', '设置是否可以ble唤醒agent: $canWakeUp');
await AgentService.setCanWakeUp(canWakeUp); await AgentService.setCanWakeUp(canWakeUp);
} catch (e) { } catch (e) {
Logger.warning('setCanWakeUp失败: $e'); Logger.warning('setCanWakeUp失败: $e');

270
lib/core/utils/logger.dart

@ -1,6 +1,7 @@
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'dart:io'; import 'dart:io';
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
@ -14,10 +15,19 @@ enum LogLevel {
/// 日志工具类 /// 日志工具类
/// ///
/// 提供统一的日志记录接口,支持控制台输出和文件存储 /// 特性:
/// - 写日志只入内存队列,由后台 Timer 定时批量刷盘,避免主 Isolate 同步 I/O。
/// - Release 包默认 [LogLevel.warning],Debug 包默认 [LogLevel.debug]。
/// - `error` 级别写入后强制立即 flush,崩溃前关键错误不丢。
/// - 支持 `dLazy/iLazy/wLazy` 闭包式 API,级别被过滤时完全不执行字符串插值。
/// - 时间戳格式化按秒缓存,避免每条日志重复 `DateFormat`。
class Logger { class Logger {
/// 当前日志级别,低于此级别的日志不会被输出 /// 当前日志级别
static LogLevel _currentLevel = LogLevel.debug; static LogLevel _currentLevel =
kReleaseMode ? LogLevel.warning : LogLevel.debug;
/// 控制台输出开关(Release 默认关闭以减少 developer.log 开销)
static bool _consoleEnabled = !kReleaseMode;
/// 默认标签 /// 默认标签
static const String _defaultTag = 'App'; static const String _defaultTag = 'App';
@ -27,30 +37,44 @@ class Logger {
static File? _currentLogFile; static File? _currentLogFile;
static bool _fileLogEnabled = false; static bool _fileLogEnabled = false;
static String? _logDirPath; static String? _logDirPath;
static final DateFormat _dateFormat = DateFormat('yyyy-MM-dd HH:mm:ss.SSS');
static final DateFormat _fileDateFormat = DateFormat('yyyy-MM-dd'); static final DateFormat _fileDateFormat = DateFormat('yyyy-MM-dd');
/// 单行最大字符数,超过则截断(避免日志被过长字符串拖慢)
static const int _maxLineLength = 4000;
/// 最大单个日志文件大小 (2MB) /// 最大单个日志文件大小 (2MB)
static const int _maxFileSize = 2 * 1024 * 1024; static const int _maxFileSize = 2 * 1024 * 1024;
/// 最多保留日志文件数量 /// 最多保留日志文件数量
static const int _maxLogFiles = 7; static const int _maxLogFiles = 7;
/// 写入计数器,用于节流轮转检查和刷盘 /// 内存队列:写日志只入队,由定时器批量落盘
static int _writeCount = 0; static final List<String> _queue = <String>[];
/// 队列上限。超过后丢弃最旧条目(保留最新),防止内存无限增长。
static const int _maxQueueSize = 5000;
/// 轮转检查间隔(每 100 次写入检查一次) /// 定时刷盘间隔
static const int _rotationCheckInterval = 100; static const Duration _flushInterval = Duration(milliseconds: 500);
static Timer? _flushTimer;
/// 刷盘间隔(每 50 次写入刷一次) /// 轮转检查间隔
static const int _flushInterval = 50; static const Duration _rotationInterval = Duration(seconds: 30);
static Timer? _rotationTimer;
/// 轮转锁,防止并发轮转 /// 轮转锁,防止并发轮转
static bool _isRotating = false; static bool _isRotating = false;
/// 当前日志文件的日期标识,用于快速判断是否需要换天 /// 正在刷盘标记,防止定时器重入
static bool _isFlushing = false;
/// 当前日志文件的日期标识
static String _currentFileDate = ''; static String _currentFileDate = '';
/// 时间戳缓存:同一秒内复用
static int _cachedTsEpochSecond = 0;
static String _cachedTsString = '';
/// 初始化文件日志 /// 初始化文件日志
static Future<void> initFileLog() async { static Future<void> initFileLog() async {
try { try {
@ -62,7 +86,13 @@ class Logger {
} }
await _openLogFile(); await _openLogFile();
_fileLogEnabled = true; _fileLogEnabled = true;
Logger.i('Logger', '文件日志初始化成功: $_logDirPath');
// 启动后台刷盘与轮转定时器
_flushTimer ??= Timer.periodic(_flushInterval, (_) => _drainQueue());
_rotationTimer ??=
Timer.periodic(_rotationInterval, (_) => _checkRotation());
Logger.i('Logger', '文件日志初始化成功: $_logDirPath level=$_currentLevel');
} catch (e) { } catch (e) {
developer.log('[Logger] 文件日志初始化失败: $e'); developer.log('[Logger] 文件日志初始化失败: $e');
} }
@ -73,24 +103,47 @@ class Logger {
final today = _fileDateFormat.format(DateTime.now()); final today = _fileDateFormat.format(DateTime.now());
final filePath = '$_logDirPath/app_$today.log'; final filePath = '$_logDirPath/app_$today.log';
_currentLogFile = File(filePath); _currentLogFile = File(filePath);
// 先关闭旧 sink,必须 await 确保关闭完成
await _logSink?.flush(); await _logSink?.flush();
await _logSink?.close(); await _logSink?.close();
_logSink = _currentLogFile!.openWrite(mode: FileMode.append); _logSink = _currentLogFile!.openWrite(mode: FileMode.append);
_currentFileDate = today; _currentFileDate = today;
} }
/// 检查日志文件是否需要轮转(受节流和锁保护) /// 把队列里的所有条目一次性写到 sink
static Future<void> _drainQueue() async {
if (_isFlushing) return;
if (!_fileLogEnabled || _logSink == null) return;
if (_queue.isEmpty) return;
_isFlushing = true;
try {
// O(1) 取出全部
final batch = List<String>.unmodifiable(_queue);
_queue.clear();
// 单次 write 减少 syscall 次数
_logSink!.write(batch.join('\n'));
_logSink!.write('\n');
await _logSink!.flush();
} catch (e) {
developer.log('[Logger] drain 失败: $e');
} finally {
_isFlushing = false;
}
}
/// 检查日志文件是否需要轮转(后台定时调度,不在写路径执行)
static Future<void> _checkRotation() async { static Future<void> _checkRotation() async {
if (_isRotating || _currentLogFile == null) return; if (_isRotating || _currentLogFile == null) return;
_isRotating = true; _isRotating = true;
try { try {
// 检查日期是否变更 // 先把队列里的内容落盘,以免轮转后丢
await _drainQueue();
// 日期变更 → 换新文件
final today = _fileDateFormat.format(DateTime.now()); final today = _fileDateFormat.format(DateTime.now());
if (_currentFileDate != today) { if (_currentFileDate != today) {
await _openLogFile(); await _openLogFile();
} }
// 检查文件大小 // 文件过大 → 切分
if (await _currentLogFile!.exists()) { if (await _currentLogFile!.exists()) {
final size = await _currentLogFile!.length(); final size = await _currentLogFile!.length();
if (size > _maxFileSize) { if (size > _maxFileSize) {
@ -104,9 +157,9 @@ class Logger {
_logSink = _currentLogFile!.openWrite(mode: FileMode.append); _logSink = _currentLogFile!.openWrite(mode: FileMode.append);
} }
} }
// 清理旧日志文件
await _cleanOldLogs(); await _cleanOldLogs();
} catch (_) {} finally { } catch (_) {
} finally {
_isRotating = false; _isRotating = false;
} }
} }
@ -131,40 +184,38 @@ class Logger {
} catch (_) {} } catch (_) {}
} }
/// 单行最大字符数,超过则分批写入 /// 快速时间戳(到秒级缓存 + 毫秒拼接)
static const int _maxLineLength = 4000; static String _formatTimestamp(DateTime now) {
final epochSec = now.millisecondsSinceEpoch ~/ 1000;
/// 写入日志到文件 if (epochSec != _cachedTsEpochSecond) {
static void _writeToFile(String level, String tag, String message) { _cachedTsEpochSecond = epochSec;
if (!_fileLogEnabled || _logSink == null) return; // yyyy-MM-dd HH:mm:ss
try { final y = now.year.toString().padLeft(4, '0');
final timestamp = _dateFormat.format(DateTime.now()); final mo = now.month.toString().padLeft(2, '0');
if (message.length <= _maxLineLength) { final d = now.day.toString().padLeft(2, '0');
_logSink!.writeln('$timestamp [$level][$tag] $message'); final h = now.hour.toString().padLeft(2, '0');
} else { final mi = now.minute.toString().padLeft(2, '0');
final totalParts = (message.length + _maxLineLength - 1) ~/ _maxLineLength; final s = now.second.toString().padLeft(2, '0');
for (var i = 0; i < totalParts; i++) { _cachedTsString = '$y-$mo-$d $h:$mi:$s';
final start = i * _maxLineLength; }
final end = start + _maxLineLength > message.length ? message.length : start + _maxLineLength; final ms = now.millisecond.toString().padLeft(3, '0');
final chunk = message.substring(start, end); return '$_cachedTsString.$ms';
_logSink!.writeln('$timestamp [$level][$tag] [${i + 1}/$totalParts] $chunk'); }
}
}
_writeCount++;
// 定期刷盘,确保数据写入磁盘
if (_writeCount % _flushInterval == 0) {
_logSink!.flush();
}
// 定期检查轮转(节流,不是每次写入都检查) /// 入队(不触发 I/O)
if (_writeCount % _rotationCheckInterval == 0) { static void _enqueue(String level, String tag, String message) {
_checkRotation(); if (!_fileLogEnabled) return;
} final ts = _formatTimestamp(DateTime.now());
} catch (e) { String msg = message;
// 文件写入失败不应影响业务逻辑(尤其是 Dio 拦截器链) if (msg.length > _maxLineLength) {
developer.log('[Logger] 文件写入失败: $e'); msg =
'${msg.substring(0, _maxLineLength)}… (truncated ${message.length - _maxLineLength}字符)';
}
// 溢出保护:保留最新
if (_queue.length >= _maxQueueSize) {
_queue.removeRange(0, _queue.length - _maxQueueSize + 1);
} }
_queue.add('$ts [$level][$tag] $msg');
} }
/// 获取日志目录路径 /// 获取日志目录路径
@ -180,12 +231,13 @@ class Logger {
.where((e) => e is File && e.path.endsWith('.log')) .where((e) => e is File && e.path.endsWith('.log'))
.cast<File>() .cast<File>()
.toList(); .toList();
files.sort((a, b) => b.path.compareTo(a.path)); // 最新在前 files.sort((a, b) => b.path.compareTo(a.path));
return files; return files;
} }
/// 刷新缓冲区(导出前调用) /// 强制把队列刷到磁盘(导出前调用)
static Future<void> flush() async { static Future<void> flush() async {
await _drainQueue();
await _logSink?.flush(); await _logSink?.flush();
} }
@ -194,86 +246,114 @@ class Logger {
_currentLevel = level; _currentLevel = level;
} }
/// 输出调试日志 (不带标签版本) /// 开关控制台输出(默认 Release 关闭)
static void setConsoleEnabled(bool enabled) {
_consoleEnabled = enabled;
}
/// 当前级别是否允许某级别输出
static bool isLoggable(LogLevel level) =>
level.index >= _currentLevel.index;
// ========== 普通 API ==========
static void debug(String message) { static void debug(String message) {
if (_currentLevel.index <= LogLevel.debug.index) { if (LogLevel.debug.index >= _currentLevel.index) {
_log('DEBUG', _defaultTag, message); _log('DEBUG', _defaultTag, message, false);
} }
} }
/// 输出调试日志
static void d(String tag, String message) { static void d(String tag, String message) {
if (_currentLevel.index <= LogLevel.debug.index) { if (LogLevel.debug.index >= _currentLevel.index) {
_log('DEBUG', tag, message); _log('DEBUG', tag, message, false);
} }
} }
/// 输出信息日志 (不带标签版本)
static void info(String message) { static void info(String message) {
if (_currentLevel.index <= LogLevel.info.index) { if (LogLevel.info.index >= _currentLevel.index) {
_log('INFO', _defaultTag, message); _log('INFO', _defaultTag, message, false);
} }
} }
/// 输出信息日志
static void i(String tag, String message) { static void i(String tag, String message) {
if (_currentLevel.index <= LogLevel.info.index) { if (LogLevel.info.index >= _currentLevel.index) {
_log('INFO', tag, message); _log('INFO', tag, message, false);
} }
} }
/// 输出警告日志 (不带标签版本)
static void warning(String message) { static void warning(String message) {
if (_currentLevel.index <= LogLevel.warning.index) { if (LogLevel.warning.index >= _currentLevel.index) {
_log('WARNING', _defaultTag, message); _log('WARN', _defaultTag, message, false);
} }
} }
/// 输出警告日志
static void w(String tag, String message) { static void w(String tag, String message) {
if (_currentLevel.index <= LogLevel.warning.index) { if (LogLevel.warning.index >= _currentLevel.index) {
_log('WARNING', tag, message); _log('WARN', tag, message, false);
} }
} }
/// 输出错误日志 (不带标签版本)
static void error(String message, [dynamic error, StackTrace? stackTrace]) { static void error(String message, [dynamic error, StackTrace? stackTrace]) {
if (_currentLevel.index <= LogLevel.error.index) { if (LogLevel.error.index >= _currentLevel.index) {
_log('ERROR', _defaultTag, message); _log('ERROR', _defaultTag, message, true);
if (error != null) { if (error != null) _log('ERROR', _defaultTag, 'Original error: $error', true);
_log('ERROR', _defaultTag, 'Original error: $error');
}
if (stackTrace != null) { if (stackTrace != null) {
_log('ERROR', _defaultTag, 'Stack trace: $stackTrace'); _log('ERROR', _defaultTag, 'Stack trace: $stackTrace', true);
} }
} }
} }
/// 输出错误日志 static void e(String tag, String message,
static void e(String tag, String message, [dynamic error, StackTrace? stackTrace]) { [dynamic error, StackTrace? stackTrace]) {
if (_currentLevel.index <= LogLevel.error.index) { if (LogLevel.error.index >= _currentLevel.index) {
_log('ERROR', tag, message); _log('ERROR', tag, message, true);
if (error != null) { if (error != null) _log('ERROR', tag, 'Original error: $error', true);
_log('ERROR', tag, 'Original error: $error');
}
if (stackTrace != null) { if (stackTrace != null) {
_log('ERROR', tag, 'Stack trace: $stackTrace'); _log('ERROR', tag, 'Stack trace: $stackTrace', true);
} }
} }
} }
// ========== 闭包式 API(高频热点推荐) ==========
/// Debug 级别懒求值:级别未放行时连字符串都不会构造。
static void dLazy(String tag, String Function() messageBuilder) {
if (LogLevel.debug.index >= _currentLevel.index) {
_log('DEBUG', tag, messageBuilder(), false);
}
}
static void iLazy(String tag, String Function() messageBuilder) {
if (LogLevel.info.index >= _currentLevel.index) {
_log('INFO', tag, messageBuilder(), false);
}
}
static void wLazy(String tag, String Function() messageBuilder) {
if (LogLevel.warning.index >= _currentLevel.index) {
_log('WARN', tag, messageBuilder(), false);
}
}
/// 内部日志输出方法 /// 内部日志输出方法
static void _log(String level, String tag, String message) { ///
if (message.length <= _maxLineLength) { /// [forceFlush] = true 时(error 级别)立即触发一次 drain,
developer.log('[$tag]: $message'); /// 保证崩溃前关键错误一定落盘。
} else { static void _log(String level, String tag, String message, bool forceFlush) {
final totalParts = (message.length + _maxLineLength - 1) ~/ _maxLineLength; if (_consoleEnabled) {
for (var i = 0; i < totalParts; i++) { if (message.length <= _maxLineLength) {
final start = i * _maxLineLength; developer.log('[$tag]: $message');
final end = start + _maxLineLength > message.length ? message.length : start + _maxLineLength; } else {
developer.log('[$tag]: [${i + 1}/$totalParts] ${message.substring(start, end)}'); developer.log(
'[$tag]: ${message.substring(0, _maxLineLength)}… (+${message.length - _maxLineLength}字符)');
} }
} }
_writeToFile(level, tag, message); _enqueue(level, tag, message);
if (forceFlush) {
// fire-and-forget;_drainQueue 自带并发保护
// 不 await 以免阻塞调用方
// ignore: discarded_futures
_drainQueue();
}
} }
} }

327
lib/data/services/ble_manager.dart

@ -259,180 +259,119 @@ class BleManager extends GetxService {
/// 设置BLE事件监听 /// 设置BLE事件监听
void _setupBleListener() { void _setupBleListener() {
//Logger.info('lxm--_setupBleListener被调用,开始设置BLE事件监听');
_statusSubscription?.cancel(); _statusSubscription?.cancel();
_dataSubscription?.cancel(); _dataSubscription?.cancel();
try { try {
Logger.info('lxm--开始监听BLE状态更新流');
// 监听BLE状态更新
_statusSubscription = _bleService.onStatusUpdated.listen( _statusSubscription = _bleService.onStatusUpdated.listen(
(event) { (event) {
Logger.info('lxm--收到BLE状态更新事件: $event'); if (event is! Map) return;
if (event is Map) {
Logger.info('lxm--事件是Map类型,开始处理'); if (event['type'] == 'connectionStateChanged') {
// 处理连接状态更新 final state = event['state'] as int;
if (event['type'] == 'connectionStateChanged') { final oldState = _connectionState.value;
Logger.info('lxm--处理连接状态变化事件'); _connectionState.value = state;
final state = event['state'] as int; Logger.i('BleManager', 'BLE状态更新: $oldState → $state');
final oldState = _connectionState.value;
_connectionState.value = state; if (state == BleConnectionState.connected) {
_isConnected.value = true;
Logger.d('BleManager', '🔄 BLE状态更新: $oldState → $state'); } else if (state == BleConnectionState.disconnected ||
Logger.d('BleManager', '📡 通知监听器状态变化: ${_connectionState.value}'); state == BleConnectionState.error) {
_isConnected.value = false;
// 连接状态转换 _awaitingCodecOpen = false;
if (state == BleConnectionState.connected) { _awaitingCodecClose = false;
Logger.info('lxm--BLE设备已连接'); _isCodecActive.value = false;
_isConnected.value = true; _voiceWakeupSupported.value = false;
_voiceWakeupEnabled.value = true;
// 设备信息(含唤醒状态)由 HomeController.fetchDeviceInfo() 统一查询 Logger.i('BleManager',
} else if (state == BleConnectionState.disconnected || '[VoiceWakeup] 断开连接,重置 supported=false, enabled=true');
state == BleConnectionState.error) { _updateDeviceInfoIfNeeded(
Logger.info('lxm--BLE设备已断开或发生错误'); batteryLevel: 0,
_isConnected.value = false; leftEarBattery: 0,
_awaitingCodecOpen = false; rightEarBattery: 0,
_awaitingCodecClose = false; caseBattery: 0,
_isCodecActive.value = false; leftEarCharging: false,
_voiceWakeupSupported.value = false; rightEarCharging: false,
_voiceWakeupEnabled.value = true; caseCharging: false,
Logger.info('[VoiceWakeup] 断开连接,重置 supported=false, enabled=true'); firmwareVersion: '',
Logger.info('lxm--断开连接时重置设备信息为0'); firmwareProductId: '',
_updateDeviceInfoIfNeeded( firmwareProductColor: '',
batteryLevel: 0, wakeupLeftVersion: '',
leftEarBattery: 0, wakeupRightVersion: '',
rightEarBattery: 0, );
caseBattery: 0,
leftEarCharging: false,
rightEarCharging: false,
caseCharging: false,
firmwareVersion: '',
firmwareProductId: '',
firmwareProductColor: '',
wakeupLeftVersion: '',
wakeupRightVersion: '',
);
// 断开连接时不清除设备信息,保留最后的电量信息
// 只有在需要完全清除设备时才重置信息
}
}
if (event['type'] == 'wakeupVoiceUpgradeProgress') {
// 唤醒语音升级进度
Logger.info('lxm--处理唤醒语音升级进度事件: $event');
_wakeupVoiceUpgradeProgress.value =
event['progress'] as int? ?? 0;
_wakeupVoiceUpgradeStatus.value = 1; // 升级中
Logger.info(
'lxm--唤醒语音升级进度: ${_wakeupVoiceUpgradeProgress.value}%');
}
if (event['type'] == 'wakeupVoiceUpgradeSuccess') {
// 唤醒语音升级状态
Logger.info('lxm--处理唤醒语音升级状态事件: $event');
_wakeupVoiceUpgradeStatus.value = 2; // 升级成功
Logger.info('lxm--唤醒语音升级状态: ${_wakeupVoiceUpgradeStatus.value}');
} }
}
if (event['type'] == 'wakeupVoiceUpgradeFailure') { if (event['type'] == 'wakeupVoiceUpgradeProgress') {
// 唤醒语音升级状态 _wakeupVoiceUpgradeProgress.value = event['progress'] as int? ?? 0;
Logger.info('lxm--处理唤醒语音升级状态事件: $event'); _wakeupVoiceUpgradeStatus.value = 1;
_wakeupVoiceUpgradeStatus.value = 3; // 升级失败 }
Logger.info('lxm--唤醒语音升级状态: ${_wakeupVoiceUpgradeStatus.value}'); if (event['type'] == 'wakeupVoiceUpgradeSuccess') {
} _wakeupVoiceUpgradeStatus.value = 2;
Logger.i('BleManager', '唤醒语音升级成功');
}
Logger.info('lxm--准备调用_processDeviceInfo处理设备信息'); if (event['type'] == 'wakeupVoiceUpgradeFailure') {
// 处理设备信息 _wakeupVoiceUpgradeStatus.value = 3;
_processDeviceInfo(event); Logger.w('BleManager', '唤醒语音升级失败');
} else {
Logger.info('lxm--事件不是Map类型: ${event.runtimeType}');
} }
_processDeviceInfo(event);
}, },
onError: (error) { onError: (error) {
Logger.error('lxm--BLE状态事件流错误: $error'); Logger.e('BleManager', 'BLE状态事件流错误: $error');
}, },
); );
Logger.info('lxm--BLE状态监听器设置完成');
// 监听BLE数据流
} catch (e) { } catch (e) {
Logger.error('lxm--设置BLE事件监听器失败: ${e.toString()}'); Logger.e('BleManager', '设置BLE事件监听器失败: ${e.toString()}');
} }
} }
/// 处理来自原生层的设备信息 /// 处理来自原生层的设备信息
void _processDeviceInfo(Map<dynamic, dynamic> event) { void _processDeviceInfo(Map<dynamic, dynamic> event) {
try { try {
Logger.info('lxm--_processDeviceInfo被调用'); if (!event.containsKey('command') || !event.containsKey('success')) {
Logger.info('lxm--接收到的事件数据: $event'); return;
}
// 检查是否是设备信息响应
if (event.containsKey('command') && event.containsKey('success')) {
Logger.info('lxm--事件包含command和success字段');
final bool success = event['success'] as bool;
Logger.info('lxm--命令执行成功状态: $success');
if (!success) {
Logger.warning('lxm--设备命令执行失败: ${event['command']}');
return;
}
// 根据命令类型处理不同信息 final bool success = event['success'] as bool;
final command = event['command']; if (!success) {
Logger.info('lxm--命令类型: $command (${command.runtimeType})'); Logger.w('BleManager', '设备命令执行失败: ${event['command']}');
return;
if (command is int) { }
Logger.info('lxm--基于命令ID处理: $command');
// 基于命令ID处理 final command = event['command'];
_processDeviceInfoByCommandId(command, event); if (command is int) {
} else if (command is String) { _processDeviceInfoByCommandId(command, event);
Logger.info('lxm--基于命令名称处理: $command'); } else if (command is String) {
// 基于命令名称处理 _processDeviceInfoByCommandName(command, event);
_processDeviceInfoByCommandName(command, event);
} else {
Logger.warning('lxm--未知的命令类型: ${command.runtimeType}');
}
} else { } else {
Logger.info('lxm--事件不包含command或success字段,跳过处理'); Logger.w('BleManager', '未知的命令类型: ${command.runtimeType}');
Logger.info('lxm--事件包含的键: ${event.keys.toList()}');
} }
} catch (e) { } catch (e, s) {
Logger.error('lxm--处理设备信息异常: ${e.toString()}'); Logger.e('BleManager', '处理设备信息异常: $e', e, s);
Logger.error('lxm--异常堆栈: ${StackTrace.current}');
} }
} }
/// 根据命令ID处理设备信息 /// 根据命令ID处理设备信息
void _processDeviceInfoByCommandId( void _processDeviceInfoByCommandId(
int commandId, Map<dynamic, dynamic> data) { int commandId, Map<dynamic, dynamic> data) {
Logger.info('lxm--_processDeviceInfoByCommandId被调用,命令ID: $commandId');
Logger.info('lxm--命令数据: $data');
switch (commandId) { switch (commandId) {
case BleCommands.GET_VERSION: case BleCommands.GET_VERSION:
Logger.info('lxm--处理版本信息命令');
_updateVersionInfoFromData(data); _updateVersionInfoFromData(data);
break; break;
case BleCommands.GET_PRODUCT_ID: case BleCommands.GET_PRODUCT_ID:
Logger.info('lxm--处理产品ID命令');
_updateProductIdFromData(data); _updateProductIdFromData(data);
break; break;
case BleCommands.GET_PRODUCT_COLOR: case BleCommands.GET_PRODUCT_COLOR:
Logger.info('lxm--处理产品颜色命令(暂未实现)');
break; break;
case BleCommands.GET_BATTERY_INFO: case BleCommands.GET_BATTERY_INFO:
Logger.info('lxm--处理电池信息命令,准备调用_updateBatteryInfoFromData');
_updateBatteryInfoFromData(data); _updateBatteryInfoFromData(data);
Logger.info('lxm--电池信息处理完成');
break; break;
case BleCommands.CONTROL_CODEC: case BleCommands.CONTROL_CODEC:
Logger.info('lxm--处理编解码控制命令');
// 处理通话状态
print(" CONTROL_CODEC: $commandId,$data");
final bool codecSuccess = _isCodecControlSuccess(data); final bool codecSuccess = _isCodecControlSuccess(data);
if (_awaitingCodecOpen) { if (_awaitingCodecOpen) {
//如果正在等待打开编解码器 _isCodecActive.value = codecSuccess;
_isCodecActive.value = codecSuccess; // 只在等待打开时更新状态
_awaitingCodecOpen = false; _awaitingCodecOpen = false;
} else if (_awaitingCodecClose) { } else if (_awaitingCodecClose) {
if (codecSuccess) { if (codecSuccess) {
@ -440,31 +379,21 @@ class BleManager extends GetxService {
} }
_awaitingCodecClose = false; _awaitingCodecClose = false;
} }
if (!codecSuccess) {
if (codecSuccess) { Logger.w('BleManager', '编解码控制失败');
print("lxm--编解码控制成功 (rawData 第二字节非 0)");
Logger.info('lxm--编解码控制成功 (rawData 第二字节非 0)');
} else {
print("lxm--编解码控制失败 (rawData 为 [162, 0] 或无法解析)");
Logger.warning('lxm--编解码控制失败 (rawData 为 [162, 0] 或无法解析)');
} }
break; break;
case BleCommands.GET_WAKEUP_VERSION: case BleCommands.GET_WAKEUP_VERSION:
Logger.info('lxm--处理唤醒语音版本命令');
_updateWakeupVersionFromData(data); _updateWakeupVersionFromData(data);
break; break;
case BleCommands.GET_VOICE_WAKEUP_STATUS: case BleCommands.GET_VOICE_WAKEUP_STATUS:
Logger.info('[VoiceWakeup] 收到查询响应(0x14), data: $data');
_updateVoiceWakeupStatusFromData(data); _updateVoiceWakeupStatusFromData(data);
break; break;
case BleCommands.SET_VOICE_WAKEUP_STATUS: case BleCommands.SET_VOICE_WAKEUP_STATUS:
Logger.info('[VoiceWakeup] 收到设置响应(0x15), data: $data');
_updateVoiceWakeupSetResultFromData(data); _updateVoiceWakeupSetResultFromData(data);
break; break;
default: default:
Logger.warning('lxm--未处理的命令ID: $commandId,数据: $data'); Logger.w('BleManager', '未处理的命令ID: $commandId');
print(" 未处理的命令ID: $commandId,$data");
} }
} }
@ -484,82 +413,46 @@ class BleManager extends GetxService {
case 'productColor': case 'productColor':
break; break;
case 'Error': case 'Error':
print(" Error: $commandName,$data"); Logger.w('BleManager', '收到 Error 命令');
break; break;
default: default:
print(" 未处理的命令名称: $commandName,$data"); Logger.w('BleManager', '未处理的命令名称: $commandName');
Logger.info('未处理的命令名称: $commandName');
} }
} }
/// 更新电池信息 /// 更新电池信息
void _updateBatteryInfoFromData(Map<dynamic, dynamic> data) { void _updateBatteryInfoFromData(Map<dynamic, dynamic> data) {
try { try {
Logger.info('lxm--_updateBatteryInfoFromData被调用');
Logger.info('lxm--接收到的原始数据: $data');
// 确保数据中包含电池信息
if (!data.containsKey('leftEarBattery') || if (!data.containsKey('leftEarBattery') ||
!data.containsKey('rightEarBattery') || !data.containsKey('rightEarBattery') ||
!data.containsKey('caseBattery')) { !data.containsKey('caseBattery')) {
Logger.warning('lxm--电池信息数据不完整,缺少必要字段'); Logger.w('BleManager', '电池信息数据不完整');
Logger.warning('lxm--数据包含的键: ${data.keys.toList()}');
return; return;
} }
Logger.info('lxm--原始电池数据解析:');
Logger.info(
'lxm-- leftEarBattery原始值: ${data['leftEarBattery']} (类型: ${data['leftEarBattery'].runtimeType})');
Logger.info(
'lxm-- rightEarBattery原始值: ${data['rightEarBattery']} (类型: ${data['rightEarBattery'].runtimeType})');
Logger.info(
'lxm-- caseBattery原始值: ${data['caseBattery']} (类型: ${data['caseBattery'].runtimeType})');
Logger.info(
'lxm-- leftEarCharging原始值: ${data['leftEarCharging']} (类型: ${data['leftEarCharging'].runtimeType})');
Logger.info(
'lxm-- rightEarCharging原始值: ${data['rightEarCharging']} (类型: ${data['rightEarCharging'].runtimeType})');
Logger.info(
'lxm-- caseCharging原始值: ${data['caseCharging']} (类型: ${data['caseCharging'].runtimeType})');
Logger.info(
'lxm-- batteryLevel原始值: ${data['batteryLevel']} (类型: ${data['batteryLevel']?.runtimeType})');
// 提取电池信息
final int leftBattery = (data['leftEarBattery'] as int? ?? 0) == 127 final int leftBattery = (data['leftEarBattery'] as int? ?? 0) == 127
? -1 // 特殊值127转换为-1 ? -1
: (data['leftEarBattery'] as int? ?? 0) > 100 : (data['leftEarBattery'] as int? ?? 0) > 100
? 0 // 大于100的值重置为0 ? 0
: (data['leftEarBattery'] as int? ?? 0); // 正常值 : (data['leftEarBattery'] as int? ?? 0);
final int rightBattery = (data['rightEarBattery'] as int? ?? 0) == 127 final int rightBattery = (data['rightEarBattery'] as int? ?? 0) == 127
? -1 // 特殊值127转换为-1 ? -1
: (data['rightEarBattery'] as int? ?? 0) > 100 : (data['rightEarBattery'] as int? ?? 0) > 100
? 0 // 大于100的值重置为0 ? 0
: (data['rightEarBattery'] as int? ?? 0); // 正常值 : (data['rightEarBattery'] as int? ?? 0);
final int caseBattery = (data['caseBattery'] as int? ?? 0) == 127 final int caseBattery = (data['caseBattery'] as int? ?? 0) == 127
? -1 // 特殊值127转换为-1 ? -1
: (data['caseBattery'] as int? ?? 0) > 100 : (data['caseBattery'] as int? ?? 0) > 100
? 0 // 大于100的值重置为0 ? 0
: (data['caseBattery'] as int? ?? 0); // 正常值 : (data['caseBattery'] as int? ?? 0);
final bool leftCharging = data['leftEarCharging'] as bool? ?? false; final bool leftCharging = data['leftEarCharging'] as bool? ?? false;
final bool rightCharging = data['rightEarCharging'] as bool? ?? false; final bool rightCharging = data['rightEarCharging'] as bool? ?? false;
final bool caseCharging = data['caseCharging'] as bool? ?? false; final bool caseCharging = data['caseCharging'] as bool? ?? false;
// 获取整体电量(原生层使用左耳电量作为主要指示)
final int overallBattery = data['batteryLevel'] as int? ?? leftBattery; final int overallBattery = data['batteryLevel'] as int? ?? leftBattery;
Logger.info('lxm--处理后的电池数据:');
Logger.info('lxm-- leftBattery: $leftBattery');
Logger.info('lxm-- rightBattery: $rightBattery');
Logger.info('lxm-- caseBattery: $caseBattery');
Logger.info('lxm-- leftCharging: $leftCharging');
Logger.info('lxm-- rightCharging: $rightCharging');
Logger.info('lxm-- caseCharging: $caseCharging');
Logger.info('lxm-- overallBattery: $overallBattery');
Logger.info('lxm--准备调用_updateDeviceInfoIfNeeded');
// 更新设备信息
_updateDeviceInfoIfNeeded( _updateDeviceInfoIfNeeded(
batteryLevel: overallBattery, batteryLevel: overallBattery,
leftEarBattery: leftBattery, leftEarBattery: leftBattery,
@ -569,12 +462,8 @@ class BleManager extends GetxService {
rightEarCharging: rightCharging, rightEarCharging: rightCharging,
caseCharging: caseCharging, caseCharging: caseCharging,
); );
} catch (e, s) {
Logger.info( Logger.e('BleManager', '更新电池信息失败: $e', e, s);
'lxm--设备电池信息已更新: 电量=$overallBattery%, 左耳=$leftBattery%, 右耳=$rightBattery%, 充电仓=$caseBattery%');
} catch (e) {
Logger.error('lxm--更新电池信息失败: ${e.toString()}');
Logger.error('lxm--错误堆栈: ${StackTrace.current}');
} }
} }
@ -718,18 +607,8 @@ class BleManager extends GetxService {
String? wakeupLeftVersion, String? wakeupLeftVersion,
String? wakeupRightVersion, String? wakeupRightVersion,
}) { }) {
Logger.info('lxm--_updateDeviceInfoIfNeeded被调用');
Logger.info(
'lxm--传入参数: batteryLevel=$batteryLevel, leftEarBattery=$leftEarBattery, rightEarBattery=$rightEarBattery, caseBattery=$caseBattery');
Logger.info(
'lxm--充电状态: leftEarCharging=$leftEarCharging, rightEarCharging=$rightEarCharging, caseCharging=$caseCharging');
Logger.info('lxm--当前deviceInfo.value是否为null: ${deviceInfo.value == null}');
// 如果设备信息已存在,则更新 // 如果设备信息已存在,则更新
if (deviceInfo.value != null) { if (deviceInfo.value != null) {
Logger.info('lxm--设备信息已存在,进行更新操作');
Logger.info('lxm--更新前的设备信息: ${deviceInfo.value}');
deviceInfo.value = deviceInfo.value!.copyWith( deviceInfo.value = deviceInfo.value!.copyWith(
batteryLevel: batteryLevel ?? deviceInfo.value?.batteryLevel, batteryLevel: batteryLevel ?? deviceInfo.value?.batteryLevel,
leftEarBattery: _getValidBatteryValue( leftEarBattery: _getValidBatteryValue(
@ -751,20 +630,17 @@ class BleManager extends GetxService {
wakeupRightVersion: wakeupRightVersion:
wakeupRightVersion ?? deviceInfo.value!.wakeupRightVersion, wakeupRightVersion ?? deviceInfo.value!.wakeupRightVersion,
); );
Logger.info('lxm--更新后的设备信息: ${deviceInfo.value}');
} }
// 创建设备信息 // 创建设备信息
// 如果设备信息不存在,但有地址和名称,则创建新的设备信息 // 如果设备信息不存在,但有地址和名称,则创建新的设备信息
else if (deviceInfo.value == null && else if (deviceInfo.value == null &&
_isConnected.value && _isConnected.value &&
(firmwareVersion != null || batteryLevel != null)) { (firmwareVersion != null || batteryLevel != null)) {
Logger.info('lxm--设备信息不存在,创建新的设备信息对象');
// 使用保存的设备信息创建新的DeviceInfo对象
String deviceAddress = currentDeviceAddress ?? ""; String deviceAddress = currentDeviceAddress ?? "";
String deviceName = _currentDeviceName ?? ""; String deviceName = _currentDeviceName ?? "";
Logger.info('lxm--创建新的设备信息对象: address=$deviceAddress, name=$deviceName'); Logger.i('BleManager',
'创建新的设备信息对象: address=$deviceAddress, name=$deviceName');
deviceInfo.value = DeviceInfo( deviceInfo.value = DeviceInfo(
address: deviceAddress, address: deviceAddress,
name: deviceName, name: deviceName,
@ -781,11 +657,6 @@ class BleManager extends GetxService {
wakeupLeftVersion: wakeupLeftVersion ?? '', wakeupLeftVersion: wakeupLeftVersion ?? '',
wakeupRightVersion: wakeupRightVersion ?? '', wakeupRightVersion: wakeupRightVersion ?? '',
); );
Logger.info('lxm--新创建的设备信息: ${deviceInfo.value}');
} else {
Logger.info(
'lxm--不满足更新条件: deviceInfo.value == null: ${deviceInfo.value == null}, _isConnected.value: ${_isConnected.value}, firmwareVersion: $firmwareVersion, batteryLevel: $batteryLevel');
} }
} }
@ -1053,7 +924,7 @@ class BleManager extends GetxService {
); );
stsCmei = matchedDevice.cmei; stsCmei = matchedDevice.cmei;
} catch (_) {} } catch (_) {}
Logger.info('lxm--当前设备 CMEI: $stsCmei (MAC=$mac)'); Logger.i('BleManager', '当前设备 CMEI: $stsCmei (MAC=$mac)');
final config = <String, dynamic>{ final config = <String, dynamic>{
// STS 模式路由 // STS 模式路由
@ -1139,35 +1010,17 @@ class BleManager extends GetxService {
}); });
} }
Logger.info('lxm--准备调用AgentService.startAgentService启动BLE服务');
final result = await AgentService.startAgentService(config); final result = await AgentService.startAgentService(config);
Logger.info('lxm--AgentService.startAgentService调用结果: $result'); Logger.i('BleManager', 'AgentService.startAgentService 结果: $result');
if (result) { if (result) {
Logger.info('lxm--BLE服务启动成功');
// 添加延迟以等待设备连接
Logger.info('lxm--等待2秒让设备连接');
//await Future.delayed(const Duration(seconds: 2));
// 检查连接状态
Logger.info('lxm--检查设备连接状态');
final isConnected = await _bleService.isConnected(); final isConnected = await _bleService.isConnected();
Logger.info('lxm--设备连接状态检查结果: $isConnected');
if (isConnected) { if (isConnected) {
Logger.info('lxm--设备已连接,更新连接状态');
_isConnected.value = true; _isConnected.value = true;
_connectionState.value = BleConnectionState.connected; _connectionState.value = BleConnectionState.connected;
// 如果设备信息存在,则获取详细信息
Logger.info('lxm--准备获取设备详细信息');
if (deviceInfo.value != null) {
// 获取详细设备信息
// fetchDeviceInfo();
}
} }
} else { } else {
Logger.error('BLE服务启动失败'); Logger.e('BleManager', 'BLE服务启动失败');
} }
return result; return result;

23
lib/data/services/speech_impl/azure_asr_service.dart

@ -307,44 +307,25 @@ class AzureAsrService extends GetxService implements AsrService {
switch (eventType) { switch (eventType) {
case 'audioData': case 'audioData':
final Uint8List data = eventMap['data'] as Uint8List? ?? Uint8List(0); final Uint8List data = eventMap['data'] as Uint8List? ?? Uint8List(0);
final double timestamp =
(eventMap['timestamp'] as num?)?.toDouble() ?? 0.0;
final int size = eventMap['size'] as int? ?? 0;
print('接收到音频数据: 大小=${size}字节, 时间戳=${timestamp}');
Logger.debug('接收到音频数据: 大小=${size}字节, 时间戳=${timestamp}');
// 发送到音频数据流
_audioDataStreamController?.add(data); _audioDataStreamController?.add(data);
break; break;
} }
} }
/// 获取音频数据流 /// 获取音频数据流
Stream<Uint8List>? getAudioDataStream() { Stream<Uint8List>? getAudioDataStream() {
print("getAudioDataStream");
_audioDataStreamController ??= StreamController<Uint8List>.broadcast(); _audioDataStreamController ??= StreamController<Uint8List>.broadcast();
return _audioDataStreamController?.stream; return _audioDataStreamController?.stream;
} }
/// 处理来自原生端的识别事件 /// 处理来自原生端的识别事件
void _handleRecognitionEvent(dynamic event) { void _handleRecognitionEvent(dynamic event) {
if (event is! Map) { if (event is! Map) return;
print('🔴[ASR-DBG] 非 Map 事件被丢弃: ${event.runtimeType}'); if (_eventStreamController == null) return;
return;
}
if (_eventStreamController == null) {
print('🔴[ASR-DBG] _eventStreamController 为 null,事件被丢弃: $event');
return;
}
final Map<dynamic, dynamic> eventMap = event; final Map<dynamic, dynamic> eventMap = event;
final String eventType = eventMap['type'] as String? ?? ''; final String eventType = eventMap['type'] as String? ?? '';
final String provider = eventMap['provider'] as String? ?? ''; final String provider = eventMap['provider'] as String? ?? '';
if (eventType != 'nativeLog' && eventType != 'audioData') {
print(
'🔴[ASR-DBG] 原生事件抵达 azure_asr_service: type=$eventType, provider=$provider, text=${eventMap['text']}');
}
if (provider.isNotEmpty) { if (provider.isNotEmpty) {
if (provider != _latestAsrProvider) { if (provider != _latestAsrProvider) {
_latestAsrProvider = provider; _latestAsrProvider = provider;

4
lib/data/services/speech_impl/azure_ast_service.dart

@ -122,7 +122,7 @@ class AzureAstService extends GetxService implements AstService {
final String utteranceId = eventMap['utteranceId'] as String? ?? ''; final String utteranceId = eventMap['utteranceId'] as String? ?? '';
final String text = eventMap['text'] as String? ?? ''; final String text = eventMap['text'] as String? ?? '';
final String detectedLanguage = eventMap['language'] as String? ?? ''; final String detectedLanguage = eventMap['language'] as String? ?? '';
Logger.info('[STS] 识别中 [$serviceId]: text=$text'); Logger.d('AzureAST', '[STS] 识别中 [$serviceId]: text=$text');
_eventStreamController?.add(ASTEvent( _eventStreamController?.add(ASTEvent(
type: ASTEventType.intermediateResult, type: ASTEventType.intermediateResult,
serviceId: serviceId, serviceId: serviceId,
@ -137,7 +137,7 @@ class AzureAstService extends GetxService implements AstService {
final String text = eventMap['translatedText'] as String? ?? ''; final String text = eventMap['translatedText'] as String? ?? '';
final String detectedLanguage = final String detectedLanguage =
eventMap['targetLanguage'] as String? ?? ''; eventMap['targetLanguage'] as String? ?? '';
Logger.info('[STS] 翻译中 [$serviceId]: text=$text'); Logger.d('AzureAST', '[STS] 翻译中 [$serviceId]: text=$text');
_eventStreamController?.add(ASTEvent( _eventStreamController?.add(ASTEvent(
type: ASTEventType.translationInterim, type: ASTEventType.translationInterim,
serviceId: serviceId, serviceId: serviceId,

10
lib/data/services/usage_stats_service.dart

@ -33,7 +33,7 @@ class UsageStatsService extends GetxService {
/// 初始化服务 /// 初始化服务
void _initializeService() { void _initializeService() {
_loadTodayTranslationStats(); _loadTodayTranslationStats();
Logger.info('lxm--使用量统计服务初始化完成'); Logger.i('UsageStats', '使用量统计服务初始化完成');
} }
/// 加载当日翻译统计数据 /// 加载当日翻译统计数据
@ -243,11 +243,11 @@ class UsageStatsService extends GetxService {
}, },
}; };
Logger.info('lxm---MEET用量上报参数: $params'); Logger.d('UsageStats', 'MEET用量上报参数: $params');
final response = await Api.usagesResPoints(params); final response = await Api.usagesResPoints(params);
Logger.info('lxm---MEET用量上报响应: $response'); Logger.d('UsageStats', 'MEET用量上报响应: $response');
_applyUsageResponse(response); _applyUsageResponse(response);
} catch (e) { } catch (e) {
@ -420,11 +420,11 @@ class UsageStatsService extends GetxService {
}, },
}; };
Logger.info('lxm---ASR用量上报参数: $params'); Logger.d('UsageStats', 'ASR用量上报参数: $params');
final response = await Api.usagesResPoints(params); final response = await Api.usagesResPoints(params);
Logger.info('lxm---ASR用量上报响应: $response'); Logger.d('UsageStats', 'ASR用量上报响应: $response');
_applyUsageResponse(response); _applyUsageResponse(response);
} catch (e) { } catch (e) {

5
lib/data/services/volcano_translation_service.dart

@ -76,11 +76,10 @@ class VolcanoTranslationService extends GetxService {
} }
_isInitialized = true; _isInitialized = true;
Logger.info( Logger.i('VolcanoTrans', 'Volcano Translation Service initialized 成功');
'lxm---Volcano Translation Service initialized成功, accessKey: $_accessKey, region: $_region, service: $_service,secretKey: $_secretKey');
return true; return true;
} catch (e) { } catch (e) {
Logger.error('lxm---Volcano Translation Service initialized失败,$e'); Logger.e('VolcanoTrans', 'Volcano Translation Service 初始化失败: $e');
return false; return false;
} }
} }

36
lib/modules/home/controllers/home_controller.dart

@ -55,22 +55,6 @@ class HomeController extends GetxController {
final firmwareVersion = ''.obs; final firmwareVersion = ''.obs;
void updateDeviceInfo(DeviceInfo? newDeviceInfo) { void updateDeviceInfo(DeviceInfo? newDeviceInfo) {
Logger.d(_tag, 'lxm--updateDeviceInfo被调用');
Logger.d(_tag, 'lxm--接收到的DeviceInfo: $newDeviceInfo');
if (newDeviceInfo != null) {
Logger.d(_tag, 'lxm--原始数据 - 左耳电量: ${newDeviceInfo.leftEarBattery}');
Logger.d(_tag, 'lxm--原始数据 - 右耳电量: ${newDeviceInfo.rightEarBattery}');
Logger.d(_tag, 'lxm--原始数据 - 充电仓电量: ${newDeviceInfo.caseBattery}');
Logger.d(_tag, 'lxm--原始数据 - 左耳充电: ${newDeviceInfo.leftEarCharging}');
Logger.d(_tag, 'lxm--原始数据 - 右耳充电: ${newDeviceInfo.rightEarCharging}');
Logger.d(_tag, 'lxm--原始数据 - 充电仓充电: ${newDeviceInfo.caseCharging}');
Logger.d(_tag, 'lxm--原始数据 - 固件版本: ${newDeviceInfo.firmwareVersion}');
} else {
Logger.d(_tag, 'lxm--接收到的DeviceInfo为null');
}
// 只有当新数据不为0时才更新电池值,否则保持原值
leftEarBattery.value = newDeviceInfo?.leftEarBattery ?? 0; leftEarBattery.value = newDeviceInfo?.leftEarBattery ?? 0;
rightEarBattery.value = newDeviceInfo?.rightEarBattery ?? 0; rightEarBattery.value = newDeviceInfo?.rightEarBattery ?? 0;
caseBattery.value = newDeviceInfo?.caseBattery ?? 0; caseBattery.value = newDeviceInfo?.caseBattery ?? 0;
@ -78,16 +62,6 @@ class HomeController extends GetxController {
rightEarCharging.value = newDeviceInfo?.rightEarCharging ?? false; rightEarCharging.value = newDeviceInfo?.rightEarCharging ?? false;
caseCharging.value = newDeviceInfo?.caseCharging ?? false; caseCharging.value = newDeviceInfo?.caseCharging ?? false;
firmwareVersion.value = newDeviceInfo?.firmwareVersion ?? ''; firmwareVersion.value = newDeviceInfo?.firmwareVersion ?? '';
Logger.d(_tag, 'lxm--更新后的值 - 左耳电量: ${leftEarBattery.value}');
Logger.d(_tag, 'lxm--更新后的值 - 右耳电量: ${rightEarBattery.value}');
Logger.d(_tag, 'lxm--更新后的值 - 充电仓电量: ${caseBattery.value}');
Logger.d(_tag, 'lxm--更新后的值 - 左耳充电: ${leftEarCharging.value}');
Logger.d(_tag, 'lxm--更新后的值 - 右耳充电: ${rightEarCharging.value}');
Logger.d(_tag, 'lxm--更新后的值 - 充电仓充电: ${caseCharging.value}');
Logger.d(_tag, 'lxm--更新后的值 - 固件版本: ${firmwareVersion.value}');
// rightEarCharging.refresh();
//update();
} }
Worker? _deviceInfoWorker; Worker? _deviceInfoWorker;
@ -138,15 +112,7 @@ class HomeController extends GetxController {
void _setupStateListeners() { void _setupStateListeners() {
_deviceInfoWorker = ever(_bleManager.deviceInfo, (DeviceInfo? info) { _deviceInfoWorker = ever(_bleManager.deviceInfo, (DeviceInfo? info) {
Logger.d(_tag, 'lxm--_deviceInfoWorker监听器被触发'); if (info != null) updateDeviceInfo(info);
Logger.d(_tag, 'lxm--从BleManager接收到的DeviceInfo: $info');
if (info != null) {
Logger.d(_tag, 'lxm--DeviceInfo不为null,调用updateDeviceInfo');
updateDeviceInfo(info);
} else {
Logger.d(_tag, 'lxm--DeviceInfo为null,跳过更新');
}
//update();
}); });
_classicBluetoothWorker = _classicBluetoothWorker =

4
lib/modules/home/views/home_view.dart

@ -519,10 +519,6 @@ class HomeView extends GetView<HomeController> {
final caseCharging = controller.caseCharging.value; final caseCharging = controller.caseCharging.value;
final isConnected = controller.isFullyConnectedRx.value; final isConnected = controller.isFullyConnectedRx.value;
print(
'lxm--电池信息UI - 左耳电量: $leftBattery, 右耳电量: $rightBattery, 充电仓电量: $caseBattery, '
'充电(L/R/C): $leftEarCharging/$rightEarCharging/$caseCharging, 已连接: $isConnected');
// 判断是否显示充电盒:充电盒电量大于0且设备已连接 // 判断是否显示充电盒:充电盒电量大于0且设备已连接
final shouldShowCase = caseBattery > 0 && isConnected; final shouldShowCase = caseBattery > 0 && isConnected;

72
lib/modules/translation/controllers/translation_controller.dart

@ -1040,16 +1040,16 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
return; return;
} }
if (currentMode.value == 'simultaneous') { if (currentMode.value == 'simultaneous') {
Logger.info('liwei-------- 开始语音识别,模式:同声传译,ASR phone_call'); Logger.i('Translation', '开始语音识别,模式:同声传译,ASR phone_call');
await _startAsrService('phone_call'); await _startAsrService('phone_call');
} else if (currentMode.value == 'faceToFace') { } else if (currentMode.value == 'faceToFace') {
Logger.info('iwei-------- 开始语音识别,模式:面对面,ASR push_to_talk'); Logger.i('Translation', '开始语音识别,模式:面对面,ASR push_to_talk');
await _startAsrService('push_to_talk'); await _startAsrService('push_to_talk');
} else if (currentMode.value == 'call') { } else if (currentMode.value == 'call') {
Logger.info('iwei-------- 通话模式:AST端到端服务已启动,跳过Azure ASR'); Logger.i('Translation', '通话模式:AST端到端服务已启动,跳过Azure ASR');
} else { } else {
Logger.info( Logger.i('Translation',
'iwei-------- 开始语音识别,模式:${currentMode.value},ASR 模式:normal'); '开始语音识别,模式:${currentMode.value},ASR 模式:normal');
await _startAsrService('normal'); await _startAsrService('normal');
} }
await _finalizeRecognitionStart(); await _finalizeRecognitionStart();
@ -1174,10 +1174,10 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
/// 处理语音识别事件 /// 处理语音识别事件
/// [event] 识别事件 /// [event] 识别事件
void _handleRecognitionEvent(RecognitionEvent event) { void _handleRecognitionEvent(RecognitionEvent event) {
print(
'🟣[TRANS-DBG] _handleRecognitionEvent: type=${event.type}, text=${event.text}, provider=${event.provider}, detectedLang=${event.detectedLanguage}, currentMode=${currentMode.value}');
final String asrProvider = event.provider; final String asrProvider = event.provider;
if (asrProvider.isNotEmpty) Logger.info('ASR provider: $asrProvider'); if (asrProvider.isNotEmpty) {
Logger.d('Translation', 'ASR provider: $asrProvider');
}
switch (event.type) { switch (event.type) {
case RecognitionEventType.finalResult: case RecognitionEventType.finalResult:
if (event.text.isEmpty) break; if (event.text.isEmpty) break;
@ -1185,12 +1185,11 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
detectedLanguageCode = currentMode.value == 'call' detectedLanguageCode = currentMode.value == 'call'
? targetLanguageCode.value ? targetLanguageCode.value
: event.detectedLanguage; : event.detectedLanguage;
Logger.info("处理识别完成:${event.text}"); Logger.d('Translation', '处理识别完成: ${event.text}');
if (currentMode.value != 'faceToFace') { if (currentMode.value != 'faceToFace') {
handleFinalResult(event.text); handleFinalResult(event.text);
} else { } else {
faceToFaceRecognitionText.value += event.text; faceToFaceRecognitionText.value += event.text;
Logger.info('lxm-面对面翻译识别文本更新: ${faceToFaceRecognitionText.value}');
} }
if (currentMode.value == 'faceToFace' && activeSpeaker.value == 0) { if (currentMode.value == 'faceToFace' && activeSpeaker.value == 0) {
handleFinalResult(faceToFaceRecognitionText.value); handleFinalResult(faceToFaceRecognitionText.value);
@ -1203,7 +1202,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
if (event.text.isEmpty) break; if (event.text.isEmpty) break;
//通话模式下:己方语言即为源语言,无需判断 //通话模式下:己方语言即为源语言,无需判断
detectedLanguageCode = sourceLanguageCode.value; detectedLanguageCode = sourceLanguageCode.value;
Logger.info("lxm---处理己方识别完成:${event.text}"); Logger.d('Translation', '处理己方识别完成: ${event.text}');
handleFinalResult(event.text); handleFinalResult(event.text);
break; break;
@ -1213,7 +1212,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
detectedLanguageCode = currentMode.value == 'call' detectedLanguageCode = currentMode.value == 'call'
? targetLanguageCode.value ? targetLanguageCode.value
: event.detectedLanguage; : event.detectedLanguage;
Logger.info("处理识别中事件:${event.text}"); Logger.d('Translation', '处理识别中: ${event.text}');
handleIntermediateResult(event.text); handleIntermediateResult(event.text);
break; break;
@ -1247,7 +1246,8 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
/// 处理 AST(语音识别+翻译一体)事件 /// 处理 AST(语音识别+翻译一体)事件
/// 按 serviceId 区分双路(A=己方, B=对方),用 utteranceId 匹配同一句话的事件 /// 按 serviceId 区分双路(A=己方, B=对方),用 utteranceId 匹配同一句话的事件
void _handleAstEvent(ASTEvent event) { void _handleAstEvent(ASTEvent event) {
Logger.info('[STS] _handleAstEvent 收到: type=${event.type}, serviceId=${event.serviceId}, text=${event.text}'); Logger.d('Translation',
'[STS] _handleAstEvent: type=${event.type}, serviceId=${event.serviceId}');
// 根据 serviceId 确定语言方向 // 根据 serviceId 确定语言方向
final String srcCode = event.serviceId == "A" final String srcCode = event.serviceId == "A"
? sourceLanguageCode.value ? sourceLanguageCode.value
@ -1259,7 +1259,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
switch (event.type) { switch (event.type) {
case ASTEventType.intermediateResult: case ASTEventType.intermediateResult:
if (event.text.isEmpty) break; if (event.text.isEmpty) break;
Logger.info('AST 识别中 [${event.serviceId}]: ${event.text}'); Logger.d('Translation', 'AST 识别中 [${event.serviceId}]: ${event.text}');
currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString(); currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString();
// 按 utteranceId 查找已有项 // 按 utteranceId 查找已有项
@ -1301,7 +1301,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
case ASTEventType.finalResult: case ASTEventType.finalResult:
if (event.text.isEmpty) break; if (event.text.isEmpty) break;
Logger.info('AST 识别完成 [${event.serviceId}]: ${event.text}'); Logger.i('Translation', 'AST 识别完成 [${event.serviceId}]: ${event.text}');
currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString(); currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString();
TranslationItem? target; TranslationItem? target;
@ -1352,7 +1352,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
case ASTEventType.translationInterim: case ASTEventType.translationInterim:
if (event.text.isEmpty) break; if (event.text.isEmpty) break;
Logger.info('AST 翻译中 [${event.serviceId}]: ${event.text}'); Logger.d('Translation', 'AST 翻译中 [${event.serviceId}]: ${event.text}');
currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString(); currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString();
TranslationItem? target; TranslationItem? target;
@ -1390,7 +1390,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
case ASTEventType.translationResult: case ASTEventType.translationResult:
if (event.text.isEmpty) break; if (event.text.isEmpty) break;
Logger.info('AST 翻译完成 [${event.serviceId}]: ${event.text}'); Logger.i('Translation', 'AST 翻译完成 [${event.serviceId}]: ${event.text}');
currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString(); currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString();
TranslationItem? target; TranslationItem? target;
@ -1534,17 +1534,12 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
// 防抖处理翻译请求 // 防抖处理翻译请求
_translationDebounceTimer?.cancel(); _translationDebounceTimer?.cancel();
_translationDebounceTimer = Timer(const Duration(milliseconds: 300), () { _translationDebounceTimer = Timer(const Duration(milliseconds: 300), () {
final lastIsInter = translationHistory.isNotEmpty ? translationHistory.last.isIntermediate : null;
final lastSrc = translationHistory.isNotEmpty ? translationHistory.last.sourceText : '';
print('🟣[TRANS-DBG] 中间结果防抖触发: histEmpty=${translationHistory.isEmpty}, lastIsInter=$lastIsInter, lastSrcLen=${lastSrc.length}');
if (translationHistory.isNotEmpty && if (translationHistory.isNotEmpty &&
translationHistory.last.isIntermediate && translationHistory.last.isIntermediate &&
translationHistory.last.sourceText.isNotEmpty) { translationHistory.last.sourceText.isNotEmpty) {
translateText(translationHistory.last.sourceText, translateText(translationHistory.last.sourceText,
translationHistory.last.timestamp, translationHistory.last.timestamp,
isFinal: false); isFinal: false);
} else {
print('🟣[TRANS-DBG] 中间结果防抖未调用 translateText(条件不满足)');
} }
}); });
} }
@ -1552,11 +1547,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
/// 处理最终识别结果 /// 处理最终识别结果
/// [text] 识别的文本 /// [text] 识别的文本
Future<void> handleFinalResult(String text) async { Future<void> handleFinalResult(String text) async {
print('🟣[TRANS-DBG] handleFinalResult 入口: text=$text, mode=${currentMode.value}, historyLen=${translationHistory.length}, lastIntermediate=${translationHistory.isNotEmpty ? translationHistory.last.isIntermediate : "N/A"}'); if (text.isEmpty) return;
if (text.isEmpty) {
print('🟣[TRANS-DBG] handleFinalResult 因 text 为空直接返回');
return;
}
// 更新当前项 // 更新当前项
if (translationHistory.isNotEmpty && if (translationHistory.isNotEmpty &&
@ -1606,30 +1597,22 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
/// 返回翻译结果 /// 返回翻译结果
Future<String?> translateText(String sourceText, DateTime timestamp, Future<String?> translateText(String sourceText, DateTime timestamp,
{bool isFinal = false}) async { {bool isFinal = false}) async {
print('🟣[TRANS-DBG] translateText 入口: text=$sourceText, isFinal=$isFinal, mode=${currentMode.value}, src=${sourceLanguageCode.value}, tgt=${targetLanguageCode.value}, detected=$detectedLanguageCode'); if (sourceText.isEmpty) return null;
if (sourceText.isEmpty) {
print('🟣[TRANS-DBG] translateText 因 sourceText 为空直接返回');
return null;
}
isTranslating.value = true; isTranslating.value = true;
String? translationResult; String? translationResult;
try { try {
final languageCodes = _determineLanguageCodes(); final languageCodes = _determineLanguageCodes();
print('🟣[TRANS-DBG] 准备调用翻译 API: src=${languageCodes['source']} tgt=${languageCodes['target']}');
translationResult = await _performTranslation( translationResult = await _performTranslation(
sourceText, languageCodes['source']!, languageCodes['target']!); sourceText, languageCodes['source']!, languageCodes['target']!);
print('🟣[TRANS-DBG] 翻译 API 返回: result=$translationResult');
if (translationResult != null) { if (translationResult != null) {
await _updateTranslationHistory( await _updateTranslationHistory(
sourceText, translationResult, timestamp, languageCodes, isFinal); sourceText, translationResult, timestamp, languageCodes, isFinal);
} else {
print('🟣[TRANS-DBG] 翻译 API 返回 null,不更新历史也不播报');
} }
} catch (e, st) { } catch (e, st) {
print('🟣[TRANS-DBG] 翻译失败: ${e.toString()}\n$st'); Logger.e('Translation', '翻译失败: $e', e, st);
} finally { } finally {
isTranslating.value = false; isTranslating.value = false;
} }
@ -1743,18 +1726,10 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
await _updateFloatingWindowWithTranslation(translationHistory[index]); await _updateFloatingWindowWithTranslation(translationHistory[index]);
} }
// 添加TranslationItem日志打印
if (isFinal) { if (isFinal) {
final item = translationHistory[index]; final item = translationHistory[index];
Logger.info('lxm-翻译完成 - TranslationItem详情: ' Logger.i('Translation',
'源文本: ${item.sourceText}, ' '翻译完成: ${item.sourceLanguageCode}→${item.targetLanguageCode} ${item.sourceText.length}字符');
'翻译文本: ${item.translatedText}, '
'源语言: ${item.sourceLanguageCode}, '
'目标语言: ${item.targetLanguageCode}, '
'时间戳: ${item.timestamp.toIso8601String()}, '
'会话ID: ${item.sessionId}, '
'是否会话首项: ${item.isFirstInSession}, '
'是否中间结果: ${item.isIntermediate}');
} }
if (isFinal) { if (isFinal) {
@ -1805,11 +1780,8 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
/// 处理TTS播放 /// 处理TTS播放
/// [translationResult] 翻译结果 /// [translationResult] 翻译结果
Future<void> _handleTtsPlayback(String translationResult) async { Future<void> _handleTtsPlayback(String translationResult) async {
print('🟣[TRANS-DBG] _handleTtsPlayback 入口: isTtsEnabled=${isTtsEnabled.value}, text=$translationResult');
if (isTtsEnabled.value) { if (isTtsEnabled.value) {
await playTranslatedText(translationResult); await playTranslatedText(translationResult);
} else {
print('🟣[TRANS-DBG] TTS 已禁用,跳过播报');
} }
} }

13
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -857,16 +857,12 @@ object AgentService : CoroutineScope {
stopIdleCheck() // 先停止现有的检查 stopIdleCheck() // 先停止现有的检查
if (!isRecognitionActive) return if (!isRecognitionActive) return
Log.d(TAG, "启动空闲检测,超时时间: ${idleSeconds}秒,模式: $currentRecognitionMode")
idleCheckJob = launch { idleCheckJob = launch {
try { try {
delay(idleSeconds * 1000L) delay(idleSeconds * 1000L)
Log.d(TAG, "空闲检测触发,isActive: $isActive, isRecognitionActive: $isRecognitionActive, isTtsSpeaking: $isTtsSpeaking, isAiStreaming: $isAiStreaming, mode: $mode")
if (isActive && isRecognitionActive) { if (isActive && isRecognitionActive) {
if (!isTtsSpeaking && !isAiStreaming) { if (!isTtsSpeaking && !isAiStreaming) {
// 条件满足:TTS未播放且AI未流式输出,执行自动停止 Log.i(TAG, "空闲检测触发自动停止,模式: $currentRecognitionMode")
Log.d(TAG, "空闲检测触发自动停止,模式: $currentRecognitionMode")
BleService.closeCodec() BleService.closeCodec()
if (currentRecognitionMode != "phone_call"){ if (currentRecognitionMode != "phone_call"){
stopRecognition() stopRecognition()
@ -878,8 +874,6 @@ object AgentService : CoroutineScope {
) )
) )
} else { } else {
// 条件不满足:TTS或AI仍在工作中,重新调度计时器等待下一轮检查
Log.d(TAG, "空闲检测条件不满足(tts=$isTtsSpeaking, ai=$isAiStreaming),重新调度")
startIdleCheck(mode, idleSeconds) startIdleCheck(mode, idleSeconds)
} }
} }
@ -897,7 +891,6 @@ object AgentService : CoroutineScope {
private fun stopIdleCheck() { private fun stopIdleCheck() {
idleCheckJob?.cancel() idleCheckJob?.cancel()
idleCheckJob = null idleCheckJob = null
Log.d(TAG, "空闲检测已停止")
} }
/** /**
@ -1009,16 +1002,13 @@ object AgentService : CoroutineScope {
fun recognizeCallback(): Boolean { fun recognizeCallback(): Boolean {
try { try {
Log.d(TAG, "recognizeCallback")
val success = azureAsrHelper?.setupEventListeners(object : val success = azureAsrHelper?.setupEventListeners(object :
AzureAsrHelper.ContinuousRecognizeCallback { AzureAsrHelper.ContinuousRecognizeCallback {
override fun onRecognizing(sessionid:String, recognizing: String, detectedLanguage: String) { override fun onRecognizing(sessionid:String, recognizing: String, detectedLanguage: String) {
Log.d(TAG, "liwei-------- onRecognizing: $sessionid")
pendingProcessJob?.cancel() pendingProcessJob?.cancel()
if (recognizing.isNotEmpty()&&isKeepResult==true) { if (recognizing.isNotEmpty()&&isKeepResult==true) {
// 检测到语音,更新状态 // 检测到语音,更新状态
_hasSpeechDetected.set(true) _hasSpeechDetected.set(true)
Log.d(TAG, "ai识别中重置定时器")
restartIdleCheck() restartIdleCheck()
if(currsessionAsrId.isEmpty()) { if(currsessionAsrId.isEmpty()) {
currsessionAsrId = sessionid currsessionAsrId = sessionid
@ -1147,7 +1137,6 @@ object AgentService : CoroutineScope {
if (currentRecognitionMode == "push_to_talk") { //按住说话 if (currentRecognitionMode == "push_to_talk") { //按住说话
if (recognitionResult.isEmpty()){ if (recognitionResult.isEmpty()){
Log.d(TAG, "liwei-------- onSessionStopped:currentRecognitionResult $currentRecognitionResult")
recognitionResult+= currentRecognitionResult recognitionResult+= currentRecognitionResult
currentRecognitionResult = "" currentRecognitionResult = ""
} }

12
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleAgent.kt

@ -64,7 +64,7 @@ object BleAgent : BleService.Callback, AgentServiceListener {
* 设置是否可以唤醒 * 设置是否可以唤醒
*/ */
fun setCanWakeUp(canWakeUp: Boolean) { fun setCanWakeUp(canWakeUp: Boolean) {
Log.d(TAG, "liwei--------------设置是否可以唤醒: $canWakeUp") Log.i(TAG, "设置是否可以唤醒: $canWakeUp")
isCanWakeUp = canWakeUp isCanWakeUp = canWakeUp
} }
@ -153,19 +153,17 @@ object BleAgent : BleService.Callback, AgentServiceListener {
} }
override fun onAudioDataReceived(data: ByteArray, channel: Int) { override fun onAudioDataReceived(data: ByteArray, channel: Int) {
// Log.d(TAG, "onAudioDataReceived, data: ${data.size}")
AgentService.pushAudioData(data) AgentService.pushAudioData(data)
// 可选:处理音频数据
} }
/** /**
* 处理唤醒信号 * 处理唤醒信号
* 在收到唤醒信号时启动语音识别 * 在收到唤醒信号时启动语音识别
*/ */
override fun onWakeupSignalReceived() { override fun onWakeupSignalReceived() {
Log.d(TAG, "liwei-------------- 收到唤醒信号,启动语音识别${AgentService.isRecognitionActive}") Log.i(TAG, "收到唤醒信号,启动语音识别 active=${AgentService.isRecognitionActive}")
if (!isCanWakeUp) { if (!isCanWakeUp) {
Log.d(TAG, "liwei-------------- 当前不可以唤醒") Log.i(TAG, "当前不可以唤醒")
return return
} }
// 停止当前TTS,避免冲突 // 停止当前TTS,避免冲突
@ -184,12 +182,12 @@ object BleAgent : BleService.Callback, AgentServiceListener {
* 处理开始 AI 单次对话 * 处理开始 AI 单次对话
*/ */
override fun onStartSingleDialogReceived() { override fun onStartSingleDialogReceived() {
Log.d(TAG, "liwei-------------- 收到开始 AI 单次对话指令") Log.i(TAG, "收到开始 AI 单次对话指令")
startSingleDialog() startSingleDialog()
} }
override fun onEndSingleDialogReceived() { override fun onEndSingleDialogReceived() {
Log.d(TAG, "liwei-------------- 收到结束 AI 单次对话指令") Log.i(TAG, "收到结束 AI 单次对话指令")
// 停止语音识别 // 停止语音识别
stopListening() stopListening()
} }

63
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrToAsr.kt

@ -121,11 +121,8 @@ class IntegratedSpeechTranslationService(
val sendLen = (ttsdata.size / multiple) * multiple val sendLen = (ttsdata.size / multiple) * multiple
if (sendLen > 0) { if (sendLen > 0) {
val chunk = ttsdata.copyOfRange(0, sendLen) val chunk = ttsdata.copyOfRange(0, sendLen)
Log.d(TAG, "按${multiple}字节对齐发送音频: ${chunk.size} 字节,缓存剩余: ${ttsdata.size - sendLen}")
eventCallback?.onSynthesisAudioGenerated(utteranceId, text, chunk) eventCallback?.onSynthesisAudioGenerated(utteranceId, text, chunk)
ttsdata = ttsdata.copyOfRange(sendLen, ttsdata.size) ttsdata = ttsdata.copyOfRange(sendLen, ttsdata.size)
} else {
Log.d(TAG, "暂未够${multiple}字节,当前缓存: ${ttsdata.size}")
} }
} }
/** /**
@ -397,10 +394,6 @@ class IntegratedSpeechTranslationService(
isInUtterance.set(true) isInUtterance.set(true)
} }
val uttId = currentUtteranceId ?: nextUtteranceId() val uttId = currentUtteranceId ?: nextUtteranceId()
Log.d(
TAG,
"识别中事件[${uttId}]: ${event.result.text},serviceConfig.sourceLanguage:${serviceConfig.sourceLanguage}"
)
eventCallback?.onRecognizing( eventCallback?.onRecognizing(
uttId, uttId,
event.result.text, event.result.text,
@ -413,7 +406,6 @@ class IntegratedSpeechTranslationService(
// 取消上一次“识别中翻译”任务,避免旧翻译结果覆盖新结果 // 取消上一次“识别中翻译”任务,避免旧翻译结果覆盖新结果
recognizingTranslationJob?.cancel() recognizingTranslationJob?.cancel()
recognizingTranslationJob = launch { recognizingTranslationJob = launch {
Log.d(TAG, "识别中翻译触发: ${event.result.text}")
processTranslationOnly(uttId, event.result.text) processTranslationOnly(uttId, event.result.text)
} }
} }
@ -433,23 +425,15 @@ class IntegratedSpeechTranslationService(
serviceConfig.sourceLanguage, serviceConfig.sourceLanguage,
confidence confidence
) )
Log.d( Log.i(TAG, "识别完成[${uttId}]: ${event.result.text}")
TAG,
"识别完成事件[${uttId}]: ${event.result.text},serviceConfig.sourceLanguage:${serviceConfig.sourceLanguage}"
)
// 只有在服务仍在运行时才触发翻译流程 // 只有在服务仍在运行时才触发翻译流程
if (serviceState.isRecognizing.get()) { if (serviceState.isRecognizing.get()) {
isInUtterance.set(false) isInUtterance.set(false)
currentUtteranceId = null currentUtteranceId = null
launch { launch {
// 识别完成时,马上打断上一次“识别中”的翻译,但不要在主线程 join
cancelRecognizingTranslationJobSafely() cancelRecognizingTranslationJobSafely()
Log.d(TAG, "触发最终翻译流程: ${event.result.text}")
processTranslationAndSynthesis(uttId, event.result.text) processTranslationAndSynthesis(uttId, event.result.text)
} }
} else {
Log.d(TAG, "服务已停止,跳过翻译流程: ${event.result.text}")
} }
} }
} }
@ -524,8 +508,7 @@ class IntegratedSpeechTranslationService(
// 合成开始事件 // 合成开始事件
SynthesisStarted.addEventListener { _, _ -> SynthesisStarted.addEventListener { _, _ ->
Log.d(TAG, "合成开始事件") ttsdata = ByteArray(0)
ttsdata = ByteArray(0)
serviceState.isSynthesizing.set(true) serviceState.isSynthesizing.set(true)
val uttId = currentSynthesisId ?: "" val uttId = currentSynthesisId ?: ""
val text = currentSynthesisText ?: "" val text = currentSynthesisText ?: ""
@ -533,11 +516,9 @@ class IntegratedSpeechTranslationService(
eventCallback?.onStateChanged("Synthesis", true) eventCallback?.onStateChanged("Synthesis", true)
} }
// 合成进行中事件 // 合成进行中事件(高频:每音频包触发)
Synthesizing.addEventListener { _, event -> Synthesizing.addEventListener { _, event ->
Log.d(TAG, "合成进行中事件") val progress = 0.5f
// 计算进度(简化版)
val progress = 0.5f // 实际应用中可以根据音频数据计算
val uttId = currentSynthesisId ?: "" val uttId = currentSynthesisId ?: ""
val text = currentSynthesisText ?: "" val text = currentSynthesisText ?: ""
eventCallback?.onSynthesisProgress(uttId, text, progress) eventCallback?.onSynthesisProgress(uttId, text, progress)
@ -548,7 +529,6 @@ class IntegratedSpeechTranslationService(
// 合成完成事件 // 合成完成事件
SynthesisCompleted.addEventListener { _, event -> SynthesisCompleted.addEventListener { _, event ->
Log.d(TAG, "合成完成事件")
val uttId = currentSynthesisId ?: "" val uttId = currentSynthesisId ?: ""
val text = currentSynthesisText ?: "" val text = currentSynthesisText ?: ""
eventCallback?.onSynthesisCompleted(uttId, text) eventCallback?.onSynthesisCompleted(uttId, text)
@ -559,7 +539,7 @@ class IntegratedSpeechTranslationService(
// 合成取消事件 // 合成取消事件
SynthesisCanceled.addEventListener { _, event -> SynthesisCanceled.addEventListener { _, event ->
Log.d(TAG, "合成取消事件") Log.w(TAG, "合成取消事件")
ttsdata = ByteArray(0) ttsdata = ByteArray(0)
serviceState.isSynthesizing.set(false) serviceState.isSynthesizing.set(false)
val reason = event.result.reason val reason = event.result.reason
@ -744,7 +724,6 @@ class IntegratedSpeechTranslationService(
serviceState.isTranslating.set(true) serviceState.isTranslating.set(true)
eventCallback?.onTranslationStarted(targetUtteranceId, targetText) eventCallback?.onTranslationStarted(targetUtteranceId, targetText)
eventCallback?.onStateChanged("Translation", true) eventCallback?.onStateChanged("Translation", true)
Log.d(TAG, "翻译开始${targetText}")
try { try {
val srcLang = if (serviceConfig.translationSourceLanguage.isNotEmpty()) serviceConfig.translationSourceLanguage else serviceConfig.sourceLanguage val srcLang = if (serviceConfig.translationSourceLanguage.isNotEmpty()) serviceConfig.translationSourceLanguage else serviceConfig.sourceLanguage
val tgtLang = if (serviceConfig.translationTargetLanguage.isNotEmpty()) serviceConfig.translationTargetLanguage else serviceConfig.targetLanguage val tgtLang = if (serviceConfig.translationTargetLanguage.isNotEmpty()) serviceConfig.translationTargetLanguage else serviceConfig.targetLanguage
@ -767,7 +746,6 @@ class IntegratedSpeechTranslationService(
when { when {
translationResult?.success == true && !translationResult.translatedText.isNullOrEmpty() -> { translationResult?.success == true && !translationResult.translatedText.isNullOrEmpty() -> {
Log.d(TAG, "翻译成功${translationResult.translatedText}")
eventCallback?.onTranslated( eventCallback?.onTranslated(
targetUtteranceId, targetUtteranceId,
targetText, targetText,
@ -816,7 +794,6 @@ class IntegratedSpeechTranslationService(
} }
try { try {
Log.d(TAG, "开始语音合成: $text")
serviceState.isSynthesizing.set(true) serviceState.isSynthesizing.set(true)
// 在开始时记录当前合成上下文,并抛出开始事件 // 在开始时记录当前合成上下文,并抛出开始事件
@ -830,16 +807,9 @@ class IntegratedSpeechTranslationService(
val result = synthesizer?.SpeakSsmlAsync(ssml)?.get() val result = synthesizer?.SpeakSsmlAsync(ssml)?.get()
if (result?.reason == ResultReason.SynthesizingAudioCompleted) { if (result?.reason == ResultReason.SynthesizingAudioCompleted) {
val playbackStatus =
if (serviceConfig.enableAudioPlayback) "音频已播放" else "音频已合成但未播放"
Log.d(TAG, "语音合成成功,$playbackStatus")
// 获取原始音频数据
val rawAudioData = result.audioData val rawAudioData = result.audioData
eventCallback?.onSynthesisAudioGenerated(utteranceId, text, ttsdata)
Log.d(TAG, "语音合成成功,发送音频: ${ttsdata.size} 字节,缓存剩余: ${ttsdata.size}") ttsdata = ByteArray(0)
eventCallback?.onSynthesisAudioGenerated(utteranceId, text, ttsdata)
ttsdata = ByteArray(0)
return rawAudioData return rawAudioData
} else { } else {
Log.e(TAG, "语音合成失败: ${result?.reason}") Log.e(TAG, "语音合成失败: ${result?.reason}")
@ -875,22 +845,12 @@ class IntegratedSpeechTranslationService(
* - text: 待翻译的源文本 * - text: 待翻译的源文本
*/ */
private suspend fun processTranslationOnly(utteranceId: String, text: String) { private suspend fun processTranslationOnly(utteranceId: String, text: String) {
// 若服务已停止,直接跳过 if (!serviceState.isRecognizing.get()) return
if (!serviceState.isRecognizing.get()) { if (serviceState.isTranslating.get()) return
Log.d(TAG, "服务已停止,取消识别中翻译流程: $text")
return
}
// 若已有翻译在进行,直接退出(避免并发)
if (serviceState.isTranslating.get()) {
Log.d(TAG, "已有翻译进行中,识别中翻译跳过: $text")
return
}
serviceState.isTranslating.set(true) serviceState.isTranslating.set(true)
eventCallback?.onTranslationStarted(utteranceId, text) eventCallback?.onTranslationStarted(utteranceId, text)
eventCallback?.onStateChanged("Translation", true) eventCallback?.onStateChanged("Translation", true)
Log.d(TAG, "识别中翻译开始: $text")
try { try {
val srcLang = if (serviceConfig.translationSourceLanguage.isNotEmpty()) serviceConfig.translationSourceLanguage else serviceConfig.sourceLanguage val srcLang = if (serviceConfig.translationSourceLanguage.isNotEmpty()) serviceConfig.translationSourceLanguage else serviceConfig.sourceLanguage
@ -905,8 +865,6 @@ class IntegratedSpeechTranslationService(
when { when {
translationResult?.success == true && !translationResult.translatedText.isNullOrEmpty() -> { translationResult?.success == true && !translationResult.translatedText.isNullOrEmpty() -> {
Log.d(TAG, "识别中翻译成功: ${translationResult.translatedText}")
// 使用“识别中”专用的临时翻译回调,区分最终翻译
eventCallback?.onInterimTranslated( eventCallback?.onInterimTranslated(
utteranceId, utteranceId,
text, text,
@ -916,18 +874,15 @@ class IntegratedSpeechTranslationService(
} }
translationResult?.error != null -> { translationResult?.error != null -> {
Log.w(TAG, "识别中翻译失败: ${translationResult.error}")
eventCallback?.onTranslationFailed(utteranceId, text, translationResult.error) eventCallback?.onTranslationFailed(utteranceId, text, translationResult.error)
} }
else -> { else -> {
Log.w(TAG, "识别中翻译返回空结果")
eventCallback?.onTranslationFailed(utteranceId, text, "翻译服务返回空结果") eventCallback?.onTranslationFailed(utteranceId, text, "翻译服务返回空结果")
} }
} }
} catch (e: CancellationException) { } catch (e: CancellationException) {
// 被识别完成事件或新识别中事件取消,不做失败提示 // 被识别完成事件或新识别中事件取消,不做失败提示
Log.d(TAG, "识别中翻译任务已取消: $text")
} catch (e: TimeoutCancellationException) { } catch (e: TimeoutCancellationException) {
Log.w(TAG, "识别中翻译超时: $text") Log.w(TAG, "识别中翻译超时: $text")
eventCallback?.onTranslationFailed(utteranceId, text, "翻译超时") eventCallback?.onTranslationFailed(utteranceId, text, "翻译超时")

25
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift

@ -849,15 +849,12 @@ public class AzureAsrHelper: NSObject {
return currentLanguage return currentLanguage
} }
print("result: \(result)")
// 备用方案:从属性中获取语言 // 备用方案:从属性中获取语言
if let properties = result.properties, if let properties = result.properties,
let language = properties.getPropertyByName( let language = properties.getPropertyByName(
"speechServiceResponse_RecognitionLanguage"), "speechServiceResponse_RecognitionLanguage"),
!language.isEmpty !language.isEmpty
{ {
print("从属性中检测到语言: \(language)")
return language return language
} }
@ -865,13 +862,12 @@ public class AzureAsrHelper: NSObject {
do { do {
let langResult = try SPXAutoDetectSourceLanguageResult(result) let langResult = try SPXAutoDetectSourceLanguageResult(result)
if let language = langResult.language, !language.isEmpty { if let language = langResult.language, !language.isEmpty {
print("SDK方法检测到语言: \(language)")
return language return language
} }
} catch { } catch {
print("SDK语言检测失败: \(error.localizedDescription)") os_log("SDK语言检测失败: %{public}@", log: log, type: .error,
error.localizedDescription)
} }
print("无法检测语言,使用默认语言: \(currentLanguage)")
return currentLanguage return currentLanguage
} }
@ -916,8 +912,6 @@ public class AzureAsrHelper: NSObject {
* 设置事件监听器 * 设置事件监听器
*/ */
public func setupEventListeners(callback: ContinuousRecognizeCallback) -> Bool { public func setupEventListeners(callback: ContinuousRecognizeCallback) -> Bool {
print("设置ssssss监听器:${speechConfig}")
// 保存回调引用用于网络事件 // 保存回调引用用于网络事件
self.continuousCallback = callback self.continuousCallback = callback
@ -935,9 +929,6 @@ public class AzureAsrHelper: NSObject {
// 使用现有的 getDetectedLanguage 方法替代 result.language // 使用现有的 getDetectedLanguage 方法替代 result.language
let detectedLanguage = self.getDetectedLanguage(from: result) let detectedLanguage = self.getDetectedLanguage(from: result)
let text = result.text
print("正在识别事件=检测到语言: \(detectedLanguage), 识别中: \(text)")
DispatchQueue.main.async { DispatchQueue.main.async {
callback.onRecognizing( callback.onRecognizing(
@ -953,42 +944,34 @@ public class AzureAsrHelper: NSObject {
!(result.text?.isEmpty ?? true) !(result.text?.isEmpty ?? true)
else { return } else { return }
// 使用现有的 getDetectedLanguage 方法替代 result.language
let detectedLanguage = self.getDetectedLanguage(from: result) let detectedLanguage = self.getDetectedLanguage(from: result)
let text = result.text let text = result.text
print("识别完成事件=检测到语言: \(detectedLanguage), 识别中: \(text)") os_log("识别完成: lang=%{public}@ text=%{public}@", log: self.log, type: .info,
detectedLanguage, text ?? "")
// 在主线程回调结果
DispatchQueue.main.async { DispatchQueue.main.async {
callback.onResult( callback.onResult(
sessionid: self.currsessionId, result.text ?? "", detectedLanguage) sessionid: self.currsessionId, result.text ?? "", detectedLanguage)
self.currsessionId = UUID().uuidString self.currsessionId = UUID().uuidString
} }
} }
// 会话开始事件 // 会话开始事件
recognizer.addSessionStartedEventHandler { (sender, event) in recognizer.addSessionStartedEventHandler { (sender, event) in
// 直接在当前线程调用回调
self.currsessionId = UUID().uuidString self.currsessionId = UUID().uuidString
callback.onSessionStarted(sessionid: self.currsessionId) callback.onSessionStarted(sessionid: self.currsessionId)
print("会话开始事件:")
} }
// 会话结束事件 // 会话结束事件
recognizer.addSessionStoppedEventHandler { [weak self] (sender, event) in recognizer.addSessionStoppedEventHandler { [weak self] (sender, event) in
guard let self = self else { return } guard let self = self else { return }
print("会话结束事件:")
// 直接在当前线程调用回调
callback.onSessionStopped(sessionid: self.currsessionId) callback.onSessionStopped(sessionid: self.currsessionId)
self.audioStream?.isContinuousRecognitionActive = false self.audioStream?.isContinuousRecognitionActive = false
//self.stopAudioProcessing()
} }
// 取消事件 // 取消事件
recognizer.addCanceledEventHandler { [weak self] (_, event) in recognizer.addCanceledEventHandler { [weak self] (_, event) in
guard let self = self else { return } guard let self = self else { return }
print("取消事件:")
let errorDetails = event.errorDetails ?? "未知错误" let errorDetails = event.errorDetails ?? "未知错误"
let reason = String(describing: event.reason.rawValue) let reason = String(describing: event.reason.rawValue)

3
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift

@ -168,8 +168,7 @@ public class AzureTtsHelper: NSObject, ITtsService, AVAudioPlayerDelegate {
*/ */
public func startspeak(sessionid: String) -> Bool { public func startspeak(sessionid: String) -> Bool {
self.sessionid = sessionid self.sessionid = sessionid
print("startspeak sessionid \(sessionid)") os_log("startspeak session=%{public}@", log: log, type: .info, sessionid)
// os_log("调用链: startspeak 设置session=%{public}@", log: log, type: .info, sessionid)
// 重置当前会话的文本计数 // 重置当前会话的文本计数
self.currentSessionTextCount = 0 self.currentSessionTextCount = 0
self.pendingTextCount = 0 self.pendingTextCount = 0

22
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/OpusAudioManager.kt

@ -300,16 +300,10 @@ class OpusAudioManager {
* @param data 音频数据 (Opus编码) * @param data 音频数据 (Opus编码)
*/ */
fun processAudioData(data: ByteArray) { fun processAudioData(data: ByteArray) {
if (!isInitialized) { if (!isInitialized) return
Log.w(TAG, "OpusAudioManager未初始化,忽略音频数据")
return
}
if (decodeOpusManager?.isDecodeStream == true) { if (decodeOpusManager?.isDecodeStream == true) {
// 将数据加入队列进行异步处理 // 将数据加入队列进行异步处理
audioDataQueue.offer(data) audioDataQueue.offer(data)
} else {
Log.d(TAG, "Opus解码流未启动,忽略音频数据")
} }
} }
@ -344,17 +338,8 @@ class OpusAudioManager {
Log.d(TAG, "准备开始Opus数据流编码") Log.d(TAG, "准备开始Opus数据流编码")
encodeOpusManager?.startEncodeStream(object : OnEncodeStreamCallback { encodeOpusManager?.startEncodeStream(object : OnEncodeStreamCallback {
override fun onEncodeStream(data: ByteArray?) { override fun onEncodeStream(data: ByteArray?) {
Log.d(TAG, "Opus编码回调收到数据,大小: ${data?.size ?: 0} bytes")
if (data != null) { if (data != null) {
// 编码完成的数据处理:
// 1. 通知回调
callback?.onAudioDataEncoded(data) callback?.onAudioDataEncoded(data)
// // 2. 将编码后的数据加入发送队列进行分块发送
// addAudioDataToSendQueue(data)
} else {
Log.w(TAG, "编码回调收到空数据")
} }
} }
@ -411,13 +396,8 @@ class OpusAudioManager {
return return
} }
// 如果已经在编码流中,直接写入数据
if (encodeOpusManager?.isEncodeStream == true) { if (encodeOpusManager?.isEncodeStream == true) {
Log.d(TAG, "正在进行Opus编码流,写入音频数据")
// 将外部音频数据写入编码流
encodeOpusManager?.writeEncodeStream(data) encodeOpusManager?.writeEncodeStream(data)
} else {
Log.w(TAG, "Opus编码流未启动,无法写入音频数据")
} }
} }

8
local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift

@ -680,12 +680,8 @@ public class BleService: NSObject {
// MARK: - 编解码控制 // MARK: - 编解码控制
public func writeExternalAudioData(data: Data) { public func writeExternalAudioData(data: Data) {
print("pcm音频进入编码", data.count) // 将外部音频数据写入编码流
// 确保Opus编码器处于活跃状态(如果不是则启动) opusProcessor?.encodePCMData(data)
// 将外部音频数据写入编码流,每次处理1280字节
opusProcessor?.encodePCMData(data)
} }
/// 打开编码器 /// 打开编码器

63
local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftOpusAudioProcessor.swift

@ -443,26 +443,14 @@ class SwiftOpusAudioProcessor: NSObject {
/// 编码PCM音频数据 /// 编码PCM音频数据
/// - Parameter pcmData: PCM音频数据 /// - Parameter pcmData: PCM音频数据
func encodePCMData(_ pcmData: Data) { func encodePCMData(_ pcmData: Data) {
guard isEncodingActive, let encoder = jlOpusEncoder else { guard isEncodingActive, let encoder = jlOpusEncoder else { return }
print("编码器未启动或不可用") guard !pcmData.isEmpty else { return }
return
}
guard !pcmData.isEmpty else {
print("PCM数据为空")
return
}
print("接收PCM数据 - 数据大小: \(pcmData.count) bytes")
// 将新数据加入队列 // 将新数据加入队列
queueLock.lock() queueLock.lock()
pcmDataQueue.append(pcmData) pcmDataQueue.append(pcmData)
let currentQueueSize = pcmDataQueue.count
queueLock.unlock() queueLock.unlock()
print("PCM数据已加入队列 - 当前队列大小: \(currentQueueSize) bytes")
// 异步处理队列中的数据 // 异步处理队列中的数据
processQueuedData() processQueuedData()
} }
@ -479,57 +467,33 @@ class SwiftOpusAudioProcessor: NSObject {
} }
processingQueue?.async { [weak self, weak encoder] in processingQueue?.async { [weak self, weak encoder] in
guard let self = self, let encoder = encoder else { guard let self = self, let encoder = encoder else { return }
print("编码器或处理器已释放")
return
}
while self.isEncodingActive { while self.isEncodingActive {
var chunkData: Data? var chunkData: Data?
// 从队列中取出数据
self.queueLock.lock() self.queueLock.lock()
if self.pcmDataQueue.count >= self.chunkSize { if self.pcmDataQueue.count >= self.chunkSize {
// 取出640字节数据
chunkData = self.pcmDataQueue.subdata(in: 0..<self.chunkSize) chunkData = self.pcmDataQueue.subdata(in: 0..<self.chunkSize)
self.pcmDataQueue.removeSubrange(0..<self.chunkSize) self.pcmDataQueue.removeSubrange(0..<self.chunkSize)
} else if self.pcmDataQueue.count > 0 { } else if self.pcmDataQueue.count > 0 {
// 不足640字节,补齐静音数据
let availableBytes = self.pcmDataQueue.count let availableBytes = self.pcmDataQueue.count
let silentBytesNeeded = self.chunkSize - availableBytes let silentBytesNeeded = self.chunkSize - availableBytes
// 创建静音数据(16位PCM静音为0)
let silentData = Data(repeating: 0, count: silentBytesNeeded) let silentData = Data(repeating: 0, count: silentBytesNeeded)
// 组合实际数据和静音数据
chunkData = Data() chunkData = Data()
chunkData?.append(self.pcmDataQueue) chunkData?.append(self.pcmDataQueue)
chunkData?.append(silentData) chunkData?.append(silentData)
// 清空队列
self.pcmDataQueue.removeAll() self.pcmDataQueue.removeAll()
print("数据不足640字节,已补齐静音数据 - 实际数据: \(availableBytes) bytes, 静音数据: \(silentBytesNeeded) bytes")
} }
self.queueLock.unlock() self.queueLock.unlock()
// 如果有数据需要编码
if let dataToEncode = chunkData { if let dataToEncode = chunkData {
guard self.isEncodingActive else { guard self.isEncodingActive else { break }
print("编码已停止,退出处理循环")
break
}
// 编码数据块
encoder.opusEncode(dataToEncode) encoder.opusEncode(dataToEncode)
print("已编码音频块 - 数据大小: \(dataToEncode.count) bytes")
} else { } else {
// 队列为空,退出循环
break break
} }
} }
print("队列处理完成")
} }
} }
@ -563,13 +527,8 @@ extension SwiftOpusAudioProcessor: JLOpusEncoderDelegate {
print("Opus编码错误: \(error.localizedDescription)") print("Opus编码错误: \(error.localizedDescription)")
return return
} }
guard let opusData = data, !opusData.isEmpty else { guard let opusData = data, !opusData.isEmpty else { return }
print("编码后的Opus数据为空")
return
}
print("Opus编码成功 - 数据大小: \(opusData.count) bytes")
// 回调编码后的数据到主线程 // 回调编码后的数据到主线程
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in

35
local_plugins/volcano_speech/android/src/main/kotlin/com/yunqiinnovation/volcano_speech/VolcanoAsrHelper.kt

@ -636,27 +636,17 @@ class VolcanoAsrHelper(private val context: Context) {
} }
SpeechEngineDefines.MESSAGE_TYPE_PARTIAL_RESULT -> { SpeechEngineDefines.MESSAGE_TYPE_PARTIAL_RESULT -> {
// 中间识别结果 // 中间识别结果(高频,降级为 debug)
Log.i(TAG, "中间识别结果: $data")
try { try {
val json = JSONObject(data) val json = JSONObject(data)
if (!json.has("result")) { if (!json.has("result")) {
Log.w(TAG, "中间结果没有 result 字段: $data")
return return
} }
// if (json.has("definite")) {
// Log.i(TAG, "utterances: ${json.getString("utterances")}")
// }
// 从多种可能的格式中获取文本
val text = extractTextFromResult(json) val text = extractTextFromResult(json)
// 获取语言信息,如果没有则使用默认语言
val detectedLanguage = json.optString("lang", language) val detectedLanguage = json.optString("lang", language)
if (text.isNotEmpty()) { if (text.isNotEmpty()) {
mainHandler.post { mainHandler.post {
Log.i(TAG, "提取到中间文本: $text")
// 中间结果
continuousCallback?.onRecognizing(text, detectedLanguage) continuousCallback?.onRecognizing(text, detectedLanguage)
} }
} }
@ -664,34 +654,25 @@ class VolcanoAsrHelper(private val context: Context) {
Log.e(TAG, "解析中间识别结果异常: ${e}") Log.e(TAG, "解析中间识别结果异常: ${e}")
} }
} }
SpeechEngineDefines.MESSAGE_TYPE_FINAL_RESULT -> { SpeechEngineDefines.MESSAGE_TYPE_FINAL_RESULT -> {
Log.i(TAG, "最终识别结果: $data")
// 最终识别结果
try { try {
val json = JSONObject(data) val json = JSONObject(data)
if (!json.has("result")) { if (!json.has("result")) {
Log.w(TAG, "最终结果没有 result 字段: $data") Log.w(TAG, "最终结果没有 result 字段")
return return
} }
// 从多种可能的格式中获取文本
val text = extractTextFromResult(json) val text = extractTextFromResult(json)
// 获取语言信息,如果没有则使用默认语言
val detectedLanguage = json.optString("lang", language) val detectedLanguage = json.optString("lang", language)
if (text.isNotEmpty()) { if (text.isNotEmpty()) {
mainHandler.post { mainHandler.post {
if (isContinuousMode) { if (isContinuousMode) {
Log.i(TAG, "发送最终识别结果: $text (连续模式)") Log.i(TAG, "最终识别结果(连续): $text")
continuousCallback?.onResult(text, detectedLanguage) continuousCallback?.onResult(text, detectedLanguage)
// 在连续模式下,不需要手动触发下一轮识别,引擎会自动继续监听
// 但如果需要明确标记新一轮开始,可以在此处添加代码
} else { } else {
Log.i(TAG, "发送最终识别结果: $text (单次模式)") Log.i(TAG, "最终识别结果(单次): $text")
recognizeCallback?.onResult(text, detectedLanguage) recognizeCallback?.onResult(text, detectedLanguage)
} }
} }

Loading…
Cancel
Save