Update services/streaming_voice_service.py
Browse files
services/streaming_voice_service.py
CHANGED
|
@@ -4,10 +4,11 @@ import soundfile as sf
|
|
| 4 |
import time
|
| 5 |
import traceback
|
| 6 |
from groq import Groq
|
| 7 |
-
from typing import Optional, Dict, Any
|
| 8 |
from config.settings import settings
|
| 9 |
from core.rag_system import EnhancedRAGSystem
|
| 10 |
from core.tts_service import EnhancedTTSService
|
|
|
|
| 11 |
|
| 12 |
|
| 13 |
class StreamingVoiceService:
|
|
@@ -16,12 +17,93 @@ class StreamingVoiceService:
|
|
| 16 |
self.rag_system = rag_system
|
| 17 |
self.tts_service = tts_service
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
# Conversation context
|
| 20 |
self.conversation_history = []
|
| 21 |
self.current_transcription = ""
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
|
| 24 |
-
"""Xử lý audio streaming
|
| 25 |
if not audio_data:
|
| 26 |
return {
|
| 27 |
'transcription': "❌ Không có dữ liệu âm thanh",
|
|
@@ -61,6 +143,14 @@ class StreamingVoiceService:
|
|
| 61 |
'tts_audio': None
|
| 62 |
}
|
| 63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
# Chuyển đổi thành văn bản
|
| 65 |
transcription = self._transcribe_audio(audio_array, sample_rate)
|
| 66 |
|
|
@@ -269,6 +359,7 @@ Thông tin tham khảo:
|
|
| 269 |
def get_conversation_state(self) -> dict:
|
| 270 |
"""Lấy trạng thái hội thoại"""
|
| 271 |
return {
|
|
|
|
| 272 |
'history_length': len(self.conversation_history),
|
| 273 |
'current_transcription': self.current_transcription,
|
| 274 |
'last_update': time.strftime("%H:%M:%S")
|
|
|
|
| 4 |
import time
|
| 5 |
import traceback
|
| 6 |
from groq import Groq
|
| 7 |
+
from typing import Optional, Dict, Any, Callable
|
| 8 |
from config.settings import settings
|
| 9 |
from core.rag_system import EnhancedRAGSystem
|
| 10 |
from core.tts_service import EnhancedTTSService
|
| 11 |
+
from core.speechbrain_vad import SpeechBrainVAD
|
| 12 |
|
| 13 |
|
| 14 |
class StreamingVoiceService:
|
|
|
|
| 17 |
self.rag_system = rag_system
|
| 18 |
self.tts_service = tts_service
|
| 19 |
|
| 20 |
+
# Khởi tạo VAD
|
| 21 |
+
self.vad_processor = SpeechBrainVAD()
|
| 22 |
+
self.is_listening = False
|
| 23 |
+
self.speech_callback = None
|
| 24 |
+
|
| 25 |
# Conversation context
|
| 26 |
self.conversation_history = []
|
| 27 |
self.current_transcription = ""
|
| 28 |
|
| 29 |
+
def start_listening(self, speech_callback: Callable) -> bool:
|
| 30 |
+
"""Bắt đầu lắng nghe với VAD"""
|
| 31 |
+
if self.is_listening:
|
| 32 |
+
return False
|
| 33 |
+
|
| 34 |
+
self.speech_callback = speech_callback
|
| 35 |
+
success = self.vad_processor.start_stream(self._on_speech_detected)
|
| 36 |
+
if success:
|
| 37 |
+
self.is_listening = True
|
| 38 |
+
print("🎙️ Đã bắt đầu lắng nghe với VAD")
|
| 39 |
+
return success
|
| 40 |
+
|
| 41 |
+
def stop_listening(self):
|
| 42 |
+
"""Dừng lắng nghe"""
|
| 43 |
+
self.vad_processor.stop_stream()
|
| 44 |
+
self.is_listening = False
|
| 45 |
+
self.speech_callback = None
|
| 46 |
+
print("🛑 Đã dừng lắng nghe")
|
| 47 |
+
|
| 48 |
+
def process_audio_chunk(self, audio_data: tuple) -> Dict[str, Any]:
|
| 49 |
+
"""Xử lý audio chunk với VAD (dùng cho real-time streaming)"""
|
| 50 |
+
if not audio_data or not self.is_listening:
|
| 51 |
+
return {
|
| 52 |
+
'transcription': "",
|
| 53 |
+
'response': "",
|
| 54 |
+
'tts_audio': None
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
try:
|
| 58 |
+
sample_rate, audio_array = audio_data
|
| 59 |
+
|
| 60 |
+
# Xử lý với VAD
|
| 61 |
+
self.vad_processor.process_stream(audio_array, sample_rate)
|
| 62 |
+
|
| 63 |
+
return {
|
| 64 |
+
'transcription': "Đang lắng nghe...",
|
| 65 |
+
'response': "",
|
| 66 |
+
'tts_audio': None
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
except Exception as e:
|
| 70 |
+
print(f"❌ Lỗi xử lý audio chunk: {e}")
|
| 71 |
+
return {
|
| 72 |
+
'transcription': "",
|
| 73 |
+
'response': "",
|
| 74 |
+
'tts_audio': None
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
def _on_speech_detected(self, speech_audio: np.ndarray, sample_rate: int):
|
| 78 |
+
"""Callback khi VAD phát hiện speech"""
|
| 79 |
+
print(f"🎯 VAD phát hiện speech segment: {len(speech_audio)/sample_rate:.2f}s")
|
| 80 |
+
|
| 81 |
+
# Chuyển đổi speech thành text
|
| 82 |
+
transcription = self._transcribe_audio(speech_audio, sample_rate)
|
| 83 |
+
|
| 84 |
+
if not transcription or len(transcription.strip()) < 2:
|
| 85 |
+
print("⚠️ Transcription quá ngắn hoặc trống")
|
| 86 |
+
return
|
| 87 |
+
|
| 88 |
+
print(f"📝 VAD Transcription: {transcription}")
|
| 89 |
+
self.current_transcription = transcription
|
| 90 |
+
|
| 91 |
+
# Tạo phản hồi AI
|
| 92 |
+
response = self._generate_ai_response(transcription)
|
| 93 |
+
|
| 94 |
+
# Tạo TTS
|
| 95 |
+
tts_audio_path = self._text_to_speech(response)
|
| 96 |
+
|
| 97 |
+
# Gửi kết quả đến callback
|
| 98 |
+
if self.speech_callback:
|
| 99 |
+
self.speech_callback({
|
| 100 |
+
'transcription': transcription,
|
| 101 |
+
'response': response,
|
| 102 |
+
'tts_audio': tts_audio_path
|
| 103 |
+
})
|
| 104 |
+
|
| 105 |
def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
|
| 106 |
+
"""Xử lý audio streaming (phương thức cũ cho compatibility)"""
|
| 107 |
if not audio_data:
|
| 108 |
return {
|
| 109 |
'transcription': "❌ Không có dữ liệu âm thanh",
|
|
|
|
| 143 |
'tts_audio': None
|
| 144 |
}
|
| 145 |
|
| 146 |
+
# Sử dụng VAD để kiểm tra speech
|
| 147 |
+
if not self.vad_processor.is_speech(audio_array, sample_rate):
|
| 148 |
+
return {
|
| 149 |
+
'transcription': "❌ Không phát hiện giọng nói",
|
| 150 |
+
'response': "Vui lòng nói rõ hơn",
|
| 151 |
+
'tts_audio': None
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
# Chuyển đổi thành văn bản
|
| 155 |
transcription = self._transcribe_audio(audio_array, sample_rate)
|
| 156 |
|
|
|
|
| 359 |
def get_conversation_state(self) -> dict:
|
| 360 |
"""Lấy trạng thái hội thoại"""
|
| 361 |
return {
|
| 362 |
+
'is_listening': self.is_listening,
|
| 363 |
'history_length': len(self.conversation_history),
|
| 364 |
'current_transcription': self.current_transcription,
|
| 365 |
'last_update': time.strftime("%H:%M:%S")
|