import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:mymuseum_visitapp/Services/Glasses/engines/wake_word_engine.dart'; /// Wake word via OpenWakeWord — service Android natif (foreground service). /// Supporte plusieurs modèles en parallèle : un seul AudioRecord + pipeline /// mel→embedding partagée, N classifieurs TFLite tournant simultanément. /// /// Événements reçus : "detected:hey_marco", "detected:hey_alba", etc. /// → onDetectedWithCommand("hey_marco") si fourni, sinon onDetected() /// /// iOS non supporté — fallback sur SpeechToTextWakeWordEngine. class OpenWakeWordEngine implements WakeWordEngine { static const _methodChannel = MethodChannel('be.unov.mymuseum/wake_word'); static const _eventChannel = EventChannel('be.unov.mymuseum/wake_word_events'); /// Un ou plusieurs modèles .tflite dans assets/files (sans extension). /// Ex: ['hey_marco'] ou ['hey_alba', 'hey_marco', 'hey_vasco'] final List modelNames; StreamSubscription? _subscription; void Function()? _onDetected; void Function(String modelName)? _onModelDetected; bool _running = false; OpenWakeWordEngine({ List? modelNames, String? modelName, }) : modelNames = modelNames ?? (modelName != null ? [modelName] : ['hey_visit']); @override Future start({ required void Function() onDetected, void Function(String command)? onDetectedWithCommand, }) async { if (_running) return; if (!_isAndroid) { debugPrint('[OpenWakeWordEngine] Android only — no-op on this platform'); return; } _onDetected = onDetected; _onModelDetected = onDetectedWithCommand; _running = true; _subscription = _eventChannel.receiveBroadcastStream().listen( (event) { if (!_running || event is! String) return; if (event.startsWith('detected:')) { final name = event.substring('detected:'.length); debugPrint('[OpenWakeWordEngine] Wake word detected: $name'); if (_onModelDetected != null) { _onModelDetected!(name); } else { _onDetected?.call(); } } }, onError: (e) => debugPrint('[OpenWakeWordEngine] Event error: $e'), ); await _methodChannel.invokeMethod('start', { 'modelNames': modelNames.join(','), }); debugPrint('[OpenWakeWordEngine] Service started (models: $modelNames)'); } @override Future stop() async { if (!_running) return; _running = false; await _subscription?.cancel(); _subscription = null; if (_isAndroid) await _methodChannel.invokeMethod('stop'); debugPrint('[OpenWakeWordEngine] Service stopped'); } bool get _isAndroid => defaultTargetPlatform == TargetPlatform.android; }