import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_tts/flutter_tts.dart'; import 'package:mymuseum_visitapp/Services/Glasses/engines/tts_engine.dart'; /// TTS on-device via Google (Android) / AVSpeechSynthesizer (iOS). /// Gratuit, pas de latence réseau, supporte FR/NL/EN/DE. class FlutterTtsEngine implements TtsEngine { final FlutterTts _tts = FlutterTts(); bool _speaking = false; String? _lastText; String? _lastLang; Completer? _completer; FlutterTtsEngine() { _tts.setStartHandler(() { _speaking = true; }); _tts.setCompletionHandler(() { _speaking = false; _completer?.complete(); _completer = null; }); _tts.setCancelHandler(() { _speaking = false; _completer?.complete(); _completer = null; }); _tts.setErrorHandler((msg) { _speaking = false; _completer?.completeError(msg); _completer = null; }); } @override bool get isSpeaking => _speaking; @override Future speak(String text, {String languageCode = 'fr-FR'}) async { if (text.isEmpty) return; _lastText = text; _lastLang = languageCode; try { await _tts.stop(); await _tts.setLanguage(languageCode); await _tts.setSpeechRate(0.45); await _tts.setVolume(1.0); await _tts.setPitch(1.0); _completer = Completer(); await _tts.speak(text); // Attend la vraie fin de la synthèse await _completer!.future.timeout( const Duration(seconds: 30), onTimeout: () {}, ); } catch (e) { debugPrint('[FlutterTtsEngine] speak error: $e'); } finally { _speaking = false; } } @override Future stop() async { await _tts.stop(); _speaking = false; _completer?.complete(); _completer = null; } @override Future replay() async { if (_lastText != null) { await speak(_lastText!, languageCode: _lastLang ?? 'fr-FR'); } } }