feat: mvp viode

This commit is contained in:
liqupan
2026-02-03 21:41:25 +08:00
parent dec5748cca
commit 8f19377517
13 changed files with 701 additions and 31 deletions

View File

@@ -4,3 +4,5 @@ library services;
export 'supabase_service.dart';
export 'chat_service.dart';
export 'chat_storage_service.dart';
export 'stt_service.dart';
export 'tts_service.dart';

View File

@@ -0,0 +1,73 @@
import 'package:speech_to_text/speech_to_text.dart';
import 'package:flutter/foundation.dart';
class STTService {
static final STTService _instance = STTService._internal();
factory STTService() => _instance;
STTService._internal();
final SpeechToText _speech = SpeechToText();
bool _isInitialized = false;
bool _isListening = false;
bool get isListening => _isListening;
Future<bool> init() async {
if (_isInitialized) return true;
try {
_isInitialized = await _speech.initialize(
onError: (error) => debugPrint('❌ STT Error: $error'),
onStatus: (status) {
debugPrint('🎤 STT Status: $status');
if (status == 'listening') _isListening = true;
if (status == 'notListening') _isListening = false;
},
);
debugPrint('✅ STT Initialized: $_isInitialized');
return _isInitialized;
} catch (e) {
debugPrint('❌ STT Init failed: $e');
return false;
}
}
Future<void> listen({
required Function(String text) onResult,
required Function(String text) onFinalResult,
String localeId = 'zh-CN',
}) async {
if (!_isInitialized) {
bool success = await init();
if (!success) return;
}
if (_isListening) await stop();
await _speech.listen(
onResult: (result) {
if (result.finalResult) {
onFinalResult(result.recognizedWords);
} else {
onResult(result.recognizedWords);
}
},
localeId: localeId,
listenFor: const Duration(seconds: 30),
pauseFor: const Duration(seconds: 3), // Wait 3s of silence to consider "done"
partialResults: true,
cancelOnError: true,
listenMode: ListenMode.dictation,
);
}
Future<void> stop() async {
await _speech.stop();
_isListening = false;
}
Future<void> cancel() async {
await _speech.cancel();
_isListening = false;
}
}

View File

@@ -0,0 +1,65 @@
import 'package:flutter_tts/flutter_tts.dart';
import 'package:flutter/foundation.dart';
class TTSService {
static final TTSService _instance = TTSService._internal();
factory TTSService() => _instance;
TTSService._internal();
final FlutterTts _flutterTts = FlutterTts();
bool _isInitialized = false;
Future<void> init() async {
if (_isInitialized) return;
try {
if (!kIsWeb) {
if (defaultTargetPlatform == TargetPlatform.iOS) {
await _flutterTts.setSharedInstance(true);
await _flutterTts.setIosAudioCategory(
IosTextToSpeechAudioCategory.playAndRecord,
[
IosTextToSpeechAudioCategoryOptions.allowBluetooth,
IosTextToSpeechAudioCategoryOptions.allowBluetoothA2DP,
IosTextToSpeechAudioCategoryOptions.mixWithOthers,
IosTextToSpeechAudioCategoryOptions.defaultToSpeaker
],
IosTextToSpeechAudioMode.defaultMode);
}
}
await _flutterTts.setLanguage("zh-CN"); // Default to Chinese
await _flutterTts.setPitch(1.0);
await _flutterTts.setSpeechRate(0.5); // Normal rate
_isInitialized = true;
debugPrint('✅ TTSService initialized');
} catch (e) {
debugPrint('❌ TTSService init error: $e');
}
}
Future<void> speak(String text) async {
if (!_isInitialized) await init();
if (text.isEmpty) return;
debugPrint('🗣️ TTS Speaking: $text');
await _flutterTts.speak(text);
}
Future<void> stop() async {
await _flutterTts.stop();
}
void setCompletionHandler(VoidCallback handler) {
_flutterTts.setCompletionHandler(handler);
}
void setStartHandler(VoidCallback handler) {
_flutterTts.setStartHandler(handler);
}
void setErrorHandler(Function(dynamic) handler) {
_flutterTts.setErrorHandler(handler);
}
}