feat: 静态页面开发完成
This commit is contained in:
111
lib/services/vosk_voice_service.dart
Normal file
111
lib/services/vosk_voice_service.dart
Normal file
@@ -0,0 +1,111 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user