Files
zhinian_manage/lib/services/vosk_voice_service.dart

112 lines
3.3 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'package:vosk_flutter_service/vosk_flutter.dart';
class VoskVoiceService {
VoskVoiceService._();
static final VoskVoiceService instance = VoskVoiceService._();
static const String _modelAsset = 'assets/models/vosk-model-small-cn-0.22.zip';
static const int _sampleRate = 16000;
final VoskFlutterPlugin _vosk = VoskFlutterPlugin.instance();
Model? _model;
Recognizer? _recognizer;
SpeechService? _speechService;
bool _initializing = false;
bool _listening = false;
final StreamController<String> _partialController = StreamController<String>.broadcast();
final StreamController<String> _resultController = StreamController<String>.broadcast();
final StreamController<String> _errorController = StreamController<String>.broadcast();
StreamSubscription<String>? _partialSub;
StreamSubscription<String>? _resultSub;
Stream<String> get partialStream => _partialController.stream;
Stream<String> get resultStream => _resultController.stream;
Stream<String> get errorStream => _errorController.stream;
bool get isListening => _listening;
bool get isReady => _speechService != null;
Future<bool> ensureReady() async {
if (_speechService != null) return true;
if (_initializing) {
while (_initializing) {
await Future<void>.delayed(const Duration(milliseconds: 80));
}
return _speechService != null;
}
_initializing = true;
try {
final modelPath = await ModelLoader().loadFromAssets(_modelAsset);
_model = await _vosk.createModel(modelPath);
_recognizer = await _vosk.createRecognizer(
model: _model!,
sampleRate: _sampleRate,
);
_speechService = await _vosk.initSpeechService(_recognizer!);
_partialSub = _speechService!.onPartial().listen((raw) {
final text = _extractField(raw, 'partial');
if (text != null) _partialController.add(text);
});
_resultSub = _speechService!.onResult().listen((raw) {
final text = _extractField(raw, 'text');
if (text != null) _resultController.add(text);
});
return true;
} catch (e) {
_errorController.add(e.toString());
return false;
} finally {
_initializing = false;
}
}
Future<bool> start() async {
if (!await ensureReady()) return false;
if (_listening) return true;
final ok = await _speechService!.start(
onRecognitionError: (err) => _errorController.add(err.toString()),
);
_listening = ok ?? false;
return _listening;
}
Future<void> stop() async {
if (!_listening || _speechService == null) return;
await _speechService!.stop();
_listening = false;
}
Future<void> cancel() async {
if (_speechService == null) return;
await _speechService!.cancel();
_listening = false;
}
Future<void> dispose() async {
await _partialSub?.cancel();
await _resultSub?.cancel();
await _speechService?.dispose();
await _recognizer?.dispose();
_speechService = null;
_recognizer = null;
_model = null;
_listening = false;
}
String? _extractField(String raw, String key) {
try {
final map = jsonDecode(raw);
if (map is Map && map[key] is String) {
final v = (map[key] as String).trim();
return v;
}
} catch (_) {}
return null;
}
}