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
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();
|
|
});
|
|
}
|
|
}
|