datbkpro commited on
Commit
627fbab
·
verified ·
1 Parent(s): 9c3732f

Update services/streaming_voice_service.py

Browse files
Files changed (1) hide show
  1. services/streaming_voice_service.py +0 -366
services/streaming_voice_service.py CHANGED
@@ -1,370 +1,4 @@
1
- # import io
2
- # import numpy as np
3
- # import soundfile as sf
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
- # from core.silero_vad import SileroVAD
13
 
14
-
15
- # class StreamingVoiceService:
16
- # def __init__(self, groq_client: Groq, rag_system: EnhancedRAGSystem, tts_service: EnhancedTTSService):
17
- # self.client = groq_client
18
- # self.rag_system = rag_system
19
- # self.tts_service = tts_service
20
-
21
- # # Khởi tạo VAD
22
- # self.vad_processor = SileroVAD()
23
- # self.is_listening = False
24
- # self.speech_callback = None
25
-
26
- # # Conversation context
27
- # self.conversation_history = []
28
- # self.current_transcription = ""
29
-
30
- # def start_listening(self, speech_callback: Callable) -> bool:
31
- # """Bắt đầu lắng nghe với VAD"""
32
- # if self.is_listening:
33
- # return False
34
-
35
- # self.speech_callback = speech_callback
36
- # success = self.vad_processor.start_stream(self._on_speech_detected)
37
- # if success:
38
- # self.is_listening = True
39
- # print("🎙️ Đã bắt đầu lắng nghe với VAD")
40
- # return success
41
-
42
- # def stop_listening(self):
43
- # """Dừng lắng nghe"""
44
- # self.vad_processor.stop_stream()
45
- # self.is_listening = False
46
- # self.speech_callback = None
47
- # print("🛑 Đã dừng lắng nghe")
48
-
49
- # def process_audio_chunk(self, audio_data: tuple) -> Dict[str, Any]:
50
- # """Xử lý audio chunk với VAD (dùng cho real-time streaming)"""
51
- # if not audio_data or not self.is_listening:
52
- # return {
53
- # 'transcription': "",
54
- # 'response': "",
55
- # 'tts_audio': None
56
- # }
57
-
58
- # try:
59
- # sample_rate, audio_array = audio_data
60
-
61
- # # Xử lý với VAD
62
- # self.vad_processor.process_stream(audio_array, sample_rate)
63
-
64
- # return {
65
- # 'transcription': "Đang lắng nghe...",
66
- # 'response': "",
67
- # 'tts_audio': None
68
- # }
69
-
70
- # except Exception as e:
71
- # print(f"❌ Lỗi xử lý audio chunk: {e}")
72
- # return {
73
- # 'transcription': "",
74
- # 'response': "",
75
- # 'tts_audio': None
76
- # }
77
-
78
- # def _on_speech_detected(self, speech_audio: np.ndarray, sample_rate: int):
79
- # """Callback khi VAD phát hiện speech"""
80
- # print(f"🎯 VAD phát hiện speech segment: {len(speech_audio)/sample_rate:.2f}s")
81
-
82
- # # Chuyển đổi speech thành text
83
- # transcription = self._transcribe_audio(speech_audio, sample_rate)
84
-
85
- # if not transcription or len(transcription.strip()) < 2:
86
- # print("⚠️ Transcription quá ngắn hoặc trống")
87
- # return
88
-
89
- # print(f"📝 VAD Transcription: {transcription}")
90
- # self.current_transcription = transcription
91
-
92
- # # Tạo phản hồi AI
93
- # response = self._generate_ai_response(transcription)
94
-
95
- # # Tạo TTS
96
- # tts_audio_path = self._text_to_speech(response)
97
-
98
- # # Gửi kết quả đến callback
99
- # if self.speech_callback:
100
- # self.speech_callback({
101
- # 'transcription': transcription,
102
- # 'response': response,
103
- # 'tts_audio': tts_audio_path
104
- # })
105
-
106
- # def process_streaming_audio(self, audio_data: tuple) -> Dict[str, Any]:
107
- # """Xử lý audio streaming (phương thức cũ cho compatibility)"""
108
- # if not audio_data:
109
- # return {
110
- # 'transcription': "❌ Không có dữ liệu âm thanh",
111
- # 'response': "Vui lòng nói lại",
112
- # 'tts_audio': None
113
- # }
114
-
115
- # try:
116
- # # Lấy dữ liệu audio từ Gradio
117
- # sample_rate, audio_array = audio_data
118
-
119
- # print(f"🎯 Nhận audio: {len(audio_array)} samples, SR: {sample_rate}")
120
-
121
- # # Kiểm tra kiểu dữ liệu và chuyển đổi nếu cần
122
- # if isinstance(audio_array, np.ndarray):
123
- # if audio_array.dtype == np.float32 or audio_array.dtype == np.float64:
124
- # # Chuyển từ float sang int16
125
- # audio_array = (audio_array * 32767).astype(np.int16)
126
-
127
- # # Kiểm tra audio có dữ liệu không
128
- # if len(audio_array) == 0:
129
- # return {
130
- # 'transcription': "❌ Âm thanh trống",
131
- # 'response': "Vui lòng nói lại",
132
- # 'tts_audio': None
133
- # }
134
-
135
- # # Tính toán âm lượng
136
- # audio_abs = np.abs(audio_array.astype(np.float32))
137
- # audio_rms = np.sqrt(np.mean(audio_abs**2)) / 32767.0
138
- # print(f"📊 Âm lượng RMS: {audio_rms:.4f}")
139
-
140
- # if audio_rms < 0.005:
141
- # return {
142
- # 'transcription': "❌ Âm thanh quá yếu",
143
- # 'response': "Xin vui lòng nói to hơn",
144
- # 'tts_audio': None
145
- # }
146
-
147
- # # Sử dụng VAD để kiểm tra speech
148
- # if not self.vad_processor.is_speech(audio_array, sample_rate):
149
- # return {
150
- # 'transcription': "❌ Không phát hiện giọng nói",
151
- # 'response': "Vui lòng nói rõ hơn",
152
- # 'tts_audio': None
153
- # }
154
-
155
- # # Chuyển đổi thành văn bản
156
- # transcription = self._transcribe_audio(audio_array, sample_rate)
157
-
158
- # if not transcription or len(transcription.strip()) == 0:
159
- # return {
160
- # 'transcription': "❌ Không nghe rõ",
161
- # 'response': "Xin vui lòng nói lại rõ hơn",
162
- # 'tts_audio': None
163
- # }
164
-
165
- # # Kiểm tra nếu transcription quá ngắn
166
- # if len(transcription.strip()) < 2:
167
- # return {
168
- # 'transcription': "❌ Câu nói quá ngắn",
169
- # 'response': "Xin vui lòng nói câu dài hơn",
170
- # 'tts_audio': None
171
- # }
172
-
173
- # print(f"📝 Đã chuyển đổi: {transcription}")
174
-
175
- # # Cập nhật transcription hiện tại
176
- # self.current_transcription = transcription
177
-
178
- # # Tạo phản hồi AI
179
- # response = self._generate_ai_response(transcription)
180
-
181
- # # Tạo TTS
182
- # tts_audio_path = self._text_to_speech(response)
183
-
184
- # return {
185
- # 'transcription': transcription,
186
- # 'response': response,
187
- # 'tts_audio': tts_audio_path
188
- # }
189
-
190
- # except Exception as e:
191
- # print(f"❌ Lỗi xử lý streaming audio: {e}")
192
- # print(f"Chi tiết lỗi: {traceback.format_exc()}")
193
- # return {
194
- # 'transcription': f"❌ Lỗi: {str(e)}",
195
- # 'response': "Xin lỗi, có lỗi xảy ra trong quá trình xử lý",
196
- # 'tts_audio': None
197
- # }
198
-
199
- # def _transcribe_audio(self, audio_data: np.ndarray, sample_rate: int) -> Optional[str]:
200
- # """Chuyển audio -> text với xử lý sample rate"""
201
- # try:
202
- # # Đảm bảo kiểu dữ liệu là int16
203
- # if audio_data.dtype != np.int16:
204
- # if audio_data.dtype in [np.float32, np.float64]:
205
- # audio_data = (audio_data * 32767).astype(np.int16)
206
- # else:
207
- # audio_data = audio_data.astype(np.int16)
208
-
209
- # # Chuẩn hóa audio data
210
- # if audio_data.ndim > 1:
211
- # audio_data = np.mean(audio_data, axis=1).astype(np.int16) # Chuyển sang mono
212
-
213
- # # Resample nếu sample rate không phải 16000Hz (Whisper yêu cầu)
214
- # target_sample_rate = 16000
215
- # if sample_rate != target_sample_rate:
216
- # audio_data = self._resample_audio(audio_data, sample_rate, target_sample_rate)
217
- # sample_rate = target_sample_rate
218
- # print(f"🔄 Đã resample từ {sample_rate}Hz xuống {target_sample_rate}Hz")
219
-
220
- # # Giới hạn độ dài audio
221
- # max_duration = 10 # giây
222
- # max_samples = sample_rate * max_duration
223
- # if len(audio_data) > max_samples:
224
- # audio_data = audio_data[:max_samples]
225
- # print(f"⚠️ Cắt audio xuống còn {max_duration} giây")
226
-
227
- # # Đảm bảo audio đủ dài
228
- # min_duration = 0.5 # giây
229
- # min_samples = int(sample_rate * min_duration)
230
- # if len(audio_data) < min_samples:
231
- # # Pad audio nếu quá ngắn
232
- # padding = np.zeros(min_samples - len(audio_data), dtype=np.int16)
233
- # audio_data = np.concatenate([audio_data, padding])
234
- # print(f"⚠️ Đã pad audio lên {min_duration} giây")
235
-
236
- # print(f"🔊 Gửi audio đến Whisper: {len(audio_data)} samples, {sample_rate}Hz")
237
-
238
- # buffer = io.BytesIO()
239
- # sf.write(buffer, audio_data, sample_rate, format='wav', subtype='PCM_16')
240
- # buffer.seek(0)
241
-
242
- # # Gọi API Whisper
243
- # transcription = self.client.audio.transcriptions.create(
244
- # model=settings.WHISPER_MODEL,
245
- # file=("speech.wav", buffer.read(), "audio/wav"),
246
- # response_format="text",
247
- # language="vi",
248
- # temperature=0.0,
249
- # )
250
-
251
- # # Xử lý response
252
- # if hasattr(transcription, 'text'):
253
- # result = transcription.text.strip()
254
- # elif isinstance(transcription, str):
255
- # result = transcription.strip()
256
- # else:
257
- # result = str(transcription).strip()
258
-
259
- # print(f"✅ Transcription thành công: '{result}'")
260
- # return result
261
-
262
- # except Exception as e:
263
- # print(f"❌ Lỗi transcription: {e}")
264
- # print(f"Audio details: dtype={audio_data.dtype}, shape={audio_data.shape}, sr={sample_rate}")
265
- # return None
266
-
267
- # def _resample_audio(self, audio_data: np.ndarray, orig_sr: int, target_sr: int) -> np.ndarray:
268
- # """Resample audio sử dụng scipy"""
269
- # try:
270
- # from scipy import signal
271
-
272
- # # Tính số samples mới
273
- # duration = len(audio_data) / orig_sr
274
- # new_length = int(duration * target_sr)
275
-
276
- # # Resample sử dụng scipy.signal.resample
277
- # resampled_audio = signal.resample(audio_data, new_length)
278
-
279
- # # Chuyển lại về int16
280
- # resampled_audio = resampled_audio.astype(np.int16)
281
-
282
- # return resampled_audio
283
-
284
- # except ImportError:
285
- # print("⚠️ Không có scipy, sử dụng simple resampling")
286
- # # Simple resampling bằng interpolation
287
- # orig_length = len(audio_data)
288
- # new_length = int(orig_length * target_sr / orig_sr)
289
-
290
- # # Linear interpolation
291
- # x_old = np.linspace(0, 1, orig_length)
292
- # x_new = np.linspace(0, 1, new_length)
293
- # resampled_audio = np.interp(x_new, x_old, audio_data).astype(np.int16)
294
-
295
- # return resampled_audio
296
- # except Exception as e:
297
- # print(f"❌ Lỗi resample: {e}")
298
- # return audio_data
299
-
300
- # def _generate_ai_response(self, user_input: str) -> str:
301
- # """Sinh phản hồi AI"""
302
- # try:
303
- # # Thêm vào lịch sử
304
- # self.conversation_history.append({"role": "user", "content": user_input})
305
-
306
- # # Tìm kiếm RAG
307
- # rag_results = self.rag_system.semantic_search(user_input, top_k=2)
308
- # context_text = "\n".join([f"- {result.get('text', str(result))}" for result in rag_results]) if rag_results else ""
309
-
310
- # system_prompt = f"""Bạn là trợ lý AI thông minh chuyên về tiếng Việt.
311
- # Hãy trả lời ngắn gọn, tự nhiên và hữu ích (dưới 100 từ).
312
- # Thông tin tham khảo:
313
- # {context_text}
314
- # """
315
-
316
- # messages = [{"role": "system", "content": system_prompt}]
317
- # # Giữ lại 4 tin nhắn gần nhất
318
- # messages.extend(self.conversation_history[-4:])
319
-
320
- # completion = self.client.chat.completions.create(
321
- # model="llama-3.1-8b-instant",
322
- # messages=messages,
323
- # max_tokens=150,
324
- # temperature=0.7
325
- # )
326
-
327
- # response = completion.choices[0].message.content
328
- # self.conversation_history.append({"role": "assistant", "content": response})
329
-
330
- # # Giới hạn lịch sử
331
- # if len(self.conversation_history) > 8:
332
- # self.conversation_history = self.conversation_history[-8:]
333
-
334
- # return response
335
-
336
- # except Exception as e:
337
- # return f"Xin lỗi, tôi gặp lỗi khi tạo phản hồi: {str(e)}"
338
-
339
- # def _text_to_speech(self, text: str) -> Optional[str]:
340
- # """Chuyển văn bản thành giọng nói"""
341
- # try:
342
- # if not text or text.startswith("❌") or text.startswith("Xin lỗi"):
343
- # return None
344
-
345
- # tts_bytes = self.tts_service.text_to_speech(text, 'vi')
346
- # if tts_bytes:
347
- # audio_path = self.tts_service.save_audio_to_file(tts_bytes)
348
- # print(f"✅ Đã tạo TTS: {audio_path}")
349
- # return audio_path
350
- # except Exception as e:
351
- # print(f"❌ Lỗi TTS: {e}")
352
- # return None
353
-
354
- # def clear_conversation(self):
355
- # """Xóa lịch sử hội thoại"""
356
- # self.conversation_history = []
357
- # self.current_transcription = ""
358
- # print("🗑️ Đã xóa lịch sử hội thoại")
359
-
360
- # def get_conversation_state(self) -> dict:
361
- # """Lấy trạng thái hội thoại"""
362
- # return {
363
- # 'is_listening': self.is_listening,
364
- # 'history_length': len(self.conversation_history),
365
- # 'current_transcription': self.current_transcription,
366
- # 'last_update': time.strftime("%H:%M:%S")
367
- # }
368
  import io
369
  import numpy as np
370
  import soundfile as sf
 
 
 
 
 
 
 
 
 
 
 
 
 
1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import io
3
  import numpy as np
4
  import soundfile as sf