|
|
|
@ -65,6 +65,8 @@ class TranslationController extends GetxController { |
|
|
|
Timer? _recordTimer; |
|
|
|
//秒 |
|
|
|
int _seconds = 0; |
|
|
|
//暂停状态记录计时器是否暂停 |
|
|
|
bool _isTimerPaused = false; |
|
|
|
// 存储相关配置 |
|
|
|
static const String _historyKey = 'translation_history'; |
|
|
|
static const String _sourceLanguageKey = 'translation_source_language'; |
|
|
|
@ -92,6 +94,8 @@ class TranslationController extends GetxController { |
|
|
|
final isTranslating = false.obs; |
|
|
|
final isTtsEnabled = true.obs; |
|
|
|
final isRecording = false.obs; |
|
|
|
final lasyIsRecording = false.obs; |
|
|
|
final hasRecordPermission = false.obs; |
|
|
|
// 语言相关 |
|
|
|
final sourceLanguage = '中文(简体)'.obs; |
|
|
|
final targetLanguage = '英语'.obs; |
|
|
|
@ -119,7 +123,6 @@ class TranslationController extends GetxController { |
|
|
|
Future<void> changeTranslationMode(String mode) async { |
|
|
|
currentMode.value = mode; |
|
|
|
currentModeTitle.value = _getModeTitle(mode); |
|
|
|
|
|
|
|
stopAll(); |
|
|
|
} |
|
|
|
|
|
|
|
@ -137,12 +140,57 @@ class TranslationController extends GetxController { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 暂停计时器 |
|
|
|
void pauseRecordTimer() { |
|
|
|
if (_recordTimer != null && !_isTimerPaused) { |
|
|
|
_recordTimer?.cancel(); |
|
|
|
_recordTimer = null; |
|
|
|
_isTimerPaused = true; |
|
|
|
Logger.info('录音计时器已暂停'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 恢复计时器 |
|
|
|
void resumeRecordTimer() { |
|
|
|
if (_isTimerPaused) { |
|
|
|
_recordTimer = Timer.periodic(Duration(seconds: 1), (_) { |
|
|
|
_seconds++; |
|
|
|
final minutes = (_seconds ~/ 60).toString().padLeft(2, '0'); |
|
|
|
final seconds = (_seconds % 60).toString().padLeft(2, '0'); |
|
|
|
recordDurationText.value = '$minutes:$seconds'; |
|
|
|
}); |
|
|
|
_isTimerPaused = false; |
|
|
|
Logger.info('录音计时器已恢复'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 重置计时器 |
|
|
|
void resetRecordTimer() { |
|
|
|
_recordTimer?.cancel(); |
|
|
|
_recordTimer = null; |
|
|
|
_seconds = 0; |
|
|
|
_isTimerPaused = false; |
|
|
|
recordDurationText.value = '00:00'; |
|
|
|
Logger.info('录音计时器已重置'); |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> setActiveSpeaker(int id) async { |
|
|
|
activeSpeaker.value = id; |
|
|
|
await _asrService.startContinuousRecognition(_audioSourceType); |
|
|
|
// 监听识别结果 |
|
|
|
if (activeSpeaker.value != 0) { |
|
|
|
isPlayback = false; |
|
|
|
await _asrService.startContinuousRecognition(_audioSourceType); |
|
|
|
isRecognizing.value = true; |
|
|
|
if (isRecording.value && isRecognizing.value) { |
|
|
|
if (lasyIsRecording.value != isRecording.value) { |
|
|
|
lasyIsRecording.value = isRecording.value; |
|
|
|
await startRecording(); |
|
|
|
} else { |
|
|
|
await _asrService.resumeRecord(); |
|
|
|
// 恢复计时器 |
|
|
|
resumeRecordTimer(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 开始ASR活跃时长跟踪(面对面模式) |
|
|
|
_startAsrActiveTracking(); |
|
|
|
@ -151,7 +199,11 @@ class TranslationController extends GetxController { |
|
|
|
} else { |
|
|
|
// 停止ASR活跃时长跟踪(面对面模式) |
|
|
|
_stopAsrActiveTracking(); |
|
|
|
|
|
|
|
if (isRecording.value && isRecognizing.value) { |
|
|
|
// 暂停计时器 |
|
|
|
pauseRecordTimer(); |
|
|
|
await _asrService.pauseRecord(); |
|
|
|
} |
|
|
|
if (fTFTranslationResult != '' && !isPlayback) { |
|
|
|
isPlayback = true; |
|
|
|
await _ttsService.setAudioOutputDevice(lastActiveSpeaker.value); |
|
|
|
@ -198,6 +250,9 @@ class TranslationController extends GetxController { |
|
|
|
super.onInit(); |
|
|
|
|
|
|
|
//_setupEventListeners(); |
|
|
|
// 预先检查权限,避免在用户交互时阻塞 |
|
|
|
_preCheckPermissions(); |
|
|
|
|
|
|
|
// 初始化统计服务 |
|
|
|
_initUsageService(); |
|
|
|
|
|
|
|
@ -228,6 +283,15 @@ class TranslationController extends GetxController { |
|
|
|
// 参考Agent模块的实现模式 |
|
|
|
} |
|
|
|
|
|
|
|
/// 预先检查权限 |
|
|
|
Future<void> _preCheckPermissions() async { |
|
|
|
try { |
|
|
|
await requestRecordPermission(); |
|
|
|
} catch (e) { |
|
|
|
Logger.e("Permission", "预检查权限失败: $e"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 初始化统计服务 |
|
|
|
void _initUsageService() { |
|
|
|
try { |
|
|
|
@ -264,16 +328,17 @@ class TranslationController extends GetxController { |
|
|
|
_storage.write(_targetLanguageKey, targetLanguage.value); |
|
|
|
} |
|
|
|
|
|
|
|
Future<bool> _requestRecordPermission() async { |
|
|
|
Future<bool> requestRecordPermission() async { |
|
|
|
try { |
|
|
|
final bool granted = await PermissionUtil.instance.requestPermission( |
|
|
|
hasRecordPermission.value = |
|
|
|
await PermissionUtil.instance.requestPermission( |
|
|
|
permissionType: Permission.microphone, |
|
|
|
permissionName: 'microphonePermission'.tr, |
|
|
|
explanationText: 'microphonePermissionExplanation'.tr, |
|
|
|
permanentDenialText: 'microphonePermissionPermanentDenial'.tr, |
|
|
|
); |
|
|
|
|
|
|
|
if (granted) { |
|
|
|
if (hasRecordPermission.value) { |
|
|
|
Logger.d("Permission", "用户授予了录音权限"); |
|
|
|
return true; |
|
|
|
} else { |
|
|
|
@ -539,11 +604,6 @@ class TranslationController extends GetxController { |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> startRecording() async { |
|
|
|
final bool storage = await _requestStoragePermission(); |
|
|
|
if (!storage) { |
|
|
|
Logger.d("Permission", "未授予存储权限,无法提供录音功能"); |
|
|
|
return; |
|
|
|
} |
|
|
|
//isRecording.value = true; |
|
|
|
_seconds = 0; |
|
|
|
recordDurationText.value = '00:00'; |
|
|
|
@ -559,25 +619,29 @@ class TranslationController extends GetxController { |
|
|
|
final formattedTime = DateFormat('yyyyMMdd_HHmmss').format(DateTime.now()); |
|
|
|
await _asrService.enableRecord( |
|
|
|
"${dir.path}/${currentModeTitle.value.tr}_$formattedTime.wav"); |
|
|
|
await _astService.enableRecord( |
|
|
|
"${dir.path}/${currentModeTitle.value.tr}_${formattedTime}_mic.wav"); |
|
|
|
// await _astService.enableRecord( |
|
|
|
// "${dir.path}/${currentModeTitle.value.tr}_${formattedTime}_mic.wav"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> stopRecording() async { |
|
|
|
_recordTimer?.cancel(); |
|
|
|
_recordTimer = null; |
|
|
|
isRecording.value = false; |
|
|
|
_isTimerPaused = false; |
|
|
|
recordDurationText.value = '00:00'; |
|
|
|
await _asrService.stopRecord(true); |
|
|
|
// 停止实际录音逻辑 |
|
|
|
// ... |
|
|
|
_seconds = 0; |
|
|
|
|
|
|
|
_asrService.stopRecord(true); |
|
|
|
lasyIsRecording.value = false; |
|
|
|
Logger.info('录音已停止,计时器已重置'); |
|
|
|
} |
|
|
|
|
|
|
|
// 开始语音识别 |
|
|
|
Future<void> startRecognition() async { |
|
|
|
Logger.info('开始语音识别'); |
|
|
|
// 等待权限请求完成并获取结果 |
|
|
|
final bool record = await _requestRecordPermission(); |
|
|
|
final bool record = await requestRecordPermission(); |
|
|
|
|
|
|
|
// 如果权限被拒绝,直接返回 |
|
|
|
if (!record) { |
|
|
|
@ -634,18 +698,14 @@ class TranslationController extends GetxController { |
|
|
|
isTtsEnabled.value = true; |
|
|
|
} |
|
|
|
|
|
|
|
if (currentMode.value == 'faceToFace') { |
|
|
|
_asrService.stopContinuousRecognition(); |
|
|
|
if (_bluetoothManager.currentDeviceRx.value != null) { |
|
|
|
restoreOriginalAudioState(); |
|
|
|
} else { |
|
|
|
if (_bluetoothManager.currentDeviceRx.value != null) { |
|
|
|
restoreOriginalAudioState(); |
|
|
|
} else { |
|
|
|
disableBluetoothAudio(); |
|
|
|
} |
|
|
|
await _asrService.startContinuousRecognition(_audioSourceType); |
|
|
|
// 开始ASR活跃时长计时 |
|
|
|
_startAsrActiveTracking(); |
|
|
|
disableBluetoothAudio(); |
|
|
|
} |
|
|
|
await _asrService.startContinuousRecognition(_audioSourceType); |
|
|
|
// 开始ASR活跃时长计时 |
|
|
|
_startAsrActiveTracking(); |
|
|
|
isRecognizing.value = true; |
|
|
|
if (isRecording.value && isRecognizing.value) { |
|
|
|
startRecording(); |
|
|
|
@ -691,6 +751,7 @@ class TranslationController extends GetxController { |
|
|
|
if (!isRecognizing.value) return; |
|
|
|
|
|
|
|
try { |
|
|
|
await stopRecording(); |
|
|
|
await _asrService.stopContinuousRecognition(); |
|
|
|
await _astService.stopContinuousTranslation(); |
|
|
|
// 停止ASR活跃时长计时 |
|
|
|
@ -715,7 +776,7 @@ class TranslationController extends GetxController { |
|
|
|
|
|
|
|
saveTranslationHistory(); |
|
|
|
} |
|
|
|
stopRecording(); |
|
|
|
|
|
|
|
currentSessionId = null; |
|
|
|
} catch (e) { |
|
|
|
Logger.error('停止语音识别失败: ${e.toString()}'); |
|
|
|
@ -993,7 +1054,6 @@ class TranslationController extends GetxController { |
|
|
|
} |
|
|
|
// 确保停止ASR活跃时长跟踪 |
|
|
|
_stopAsrActiveTracking(); |
|
|
|
stopRecording(); |
|
|
|
_translationDebounceTimer?.cancel(); |
|
|
|
await _ttsService.stop(); |
|
|
|
} catch (e) { |
|
|
|
|