81 lines
2.8 KiB
Dart
81 lines
2.8 KiB
Dart
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<String> modelNames;
|
|
|
|
StreamSubscription<dynamic>? _subscription;
|
|
void Function()? _onDetected;
|
|
void Function(String modelName)? _onModelDetected;
|
|
bool _running = false;
|
|
|
|
OpenWakeWordEngine({
|
|
List<String>? modelNames,
|
|
String? modelName,
|
|
}) : modelNames = modelNames ?? (modelName != null ? [modelName] : ['hey_visit']);
|
|
|
|
@override
|
|
Future<void> 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<void> 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;
|
|
}
|