You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
5.1 KiB

// voice_recognition_service.dart
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/foundation.dart';
import 'volcano_asr_service.dart';
import 'package:get/get.dart';
class VoiceRecognitionService extends GetxService {
VolcanoAsrService? _asrService;
bool _isInitialized = false;
bool _isStoppingRecognition = false;
final isRecognizing = false.obs;
Function(String)? onRecognitionResult;
Function(String)? onError;
Function()? onRecognitionComplete;
Function(String)? onSentenceComplete;
Function(String)? onConnectionStatusChanged;
VoiceRecognitionService();
void _logError(String message, [dynamic error, StackTrace? stackTrace]) {
debugPrint('🔴 VoiceRecognitionService Error: $message');
if (error != null) debugPrint('Error details: $error');
if (stackTrace != null) debugPrint('Stack trace: $stackTrace');
}
@override
Future<void> onInit() async {
super.onInit();
if (!_isInitialized) {
try {
await _initializeServices();
_isInitialized = true;
debugPrint('✅ VoiceRecognitionService initialized successfully');
} catch (e, stackTrace) {
_logError('Failed to initialize services', e, stackTrace);
rethrow;
}
}
}
Future<void> _initializeServices() async {
try {
final appKey = dotenv.env['VOLCANO_APP_KEY'];
final accessKey = dotenv.env['VOLCANO_ACCESS_KEY'];
if (appKey == null || accessKey == null) {
throw Exception('Volcano ASR service configuration is missing. Please check your .env file.');
}
_asrService = VolcanoAsrService(
appKey: appKey,
accessKey: accessKey,
onResult: _handleRecognitionResult,
onError: _handleError,
onConnectionStatusChanged: _handleConnectionStatusChanged,
);
await _setupServices();
} catch (e, stackTrace) {
_logError('Error during service initialization', e, stackTrace);
rethrow;
}
}
Future<void> _setupServices() async {
try {
_asrService?.onResult = (result) {
debugPrint('ASR Result: $result');
onRecognitionResult?.call(result);
onSentenceComplete?.call(result);
};
_asrService?.onError = (error) {
_logError('ASR service error', error);
onError?.call('ASR error: $error');
};
_asrService?.onConnectionStatusChanged = (status) {
final isConnected = status == 'connected';
debugPrint('ASR Connection status: $status');
if (!isConnected && isRecognizing.value && !_isStoppingRecognition) {
stopRecognition();
onRecognitionComplete?.call();
}
onConnectionStatusChanged?.call(status);
};
} catch (e, stackTrace) {
_logError('Error during services setup', e, stackTrace);
rethrow;
}
}
Future<void> startRecognition() async {
if (isRecognizing.value || _asrService == null) {
debugPrint('⚠️ Cannot start recognition: ${isRecognizing.value ? 'Already recognizing' : 'ASR service not initialized'}');
return;
}
try {
_isStoppingRecognition = false;
debugPrint('Starting voice recognition...');
await _asrService?.startRecognition();
isRecognizing.value = true;
} catch (e, stackTrace) {
_logError('Failed to start recognition', e, stackTrace);
onError?.call('Failed to start recognition: $e');
await stopRecognition();
}
}
Future<void> stopRecognition() async {
if (!isRecognizing.value || _asrService == null || _isStoppingRecognition) return;
try {
_isStoppingRecognition = true;
debugPrint('Stopping voice recognition...');
isRecognizing.value = false;
await _asrService?.stopRecognition().timeout(
const Duration(seconds: 3),
onTimeout: () {
debugPrint('ASR service stop timeout, forcing cleanup');
_cleanupResources();
},
);
final complete = onRecognitionComplete;
if (complete != null) {
Future.microtask(complete);
}
} catch (e, stackTrace) {
_logError('Failed to stop recognition', e, stackTrace);
onError?.call('Failed to stop recognition: $e');
_cleanupResources();
} finally {
_isStoppingRecognition = false;
}
}
void _cleanupResources() {
if (_asrService != null) {
_asrService!.onResult = null;
_asrService!.onError = null;
_asrService!.onConnectionStatusChanged = null;
}
isRecognizing.value = false;
_isStoppingRecognition = false;
}
void _handleRecognitionResult(String result) {
onRecognitionResult?.call(result);
}
void _handleError(String error) {
onError?.call(error);
}
void _handleConnectionStatusChanged(String status) {
onConnectionStatusChanged?.call(status);
}
@override
void onClose() {
debugPrint('Cleaning up VoiceRecognitionService...');
stopRecognition().then((_) {
debugPrint('VoiceRecognitionService cleanup completed');
super.onClose();
}).catchError((e) {
debugPrint('Error during VoiceRecognitionService cleanup: $e');
super.onClose();
});
}
}