added voice pipiline
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
__pycache__/
|
||||||
|
.env
|
||||||
|
venv/
|
||||||
|
|
||||||
Binary file not shown.
+132
@@ -0,0 +1,132 @@
|
|||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import pyaudio
|
||||||
|
from openwakeword.model import Model
|
||||||
|
from faster_whisper import WhisperModel
|
||||||
|
from edge_tts import Communicate
|
||||||
|
import wave
|
||||||
|
import webrtcvad # Für bessere Sprach-Erkennung
|
||||||
|
|
||||||
|
from agent import Agent
|
||||||
|
from util.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger("VOICE")
|
||||||
|
|
||||||
|
|
||||||
|
class VoiceNora:
|
||||||
|
def __init__(self):
|
||||||
|
self.agent = Agent()
|
||||||
|
self.running = True
|
||||||
|
self.vad = webrtcvad.Vad(2) # Aggressivität 0-3
|
||||||
|
|
||||||
|
# Wake Word
|
||||||
|
self.ww_model = Model(wakeword="nora")
|
||||||
|
|
||||||
|
# STT
|
||||||
|
self.stt_model = WhisperModel("tiny", device="cpu", compute_type="int8")
|
||||||
|
|
||||||
|
print("\n🎤 Voice N.O.R.A gestartet")
|
||||||
|
print(" Wakeword: 'Nora'")
|
||||||
|
print(" Weibliche Stimme aktiviert\n")
|
||||||
|
|
||||||
|
async def listen_for_wakeword(self):
|
||||||
|
pa = pyaudio.PyAudio()
|
||||||
|
stream = pa.open(format=pyaudio.paInt16, channels=1, rate=16000,
|
||||||
|
input=True, frames_per_buffer=1024)
|
||||||
|
|
||||||
|
print("⏳ Lausche auf 'Nora'...")
|
||||||
|
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
audio_chunk = stream.read(1024, exception_on_overflow=False)
|
||||||
|
audio_np = np.frombuffer(audio_chunk, dtype=np.int16)
|
||||||
|
|
||||||
|
if self.ww_model.predict(audio_np).get("nora", 0) > 0.58:
|
||||||
|
print("\n✅ 'Nora' erkannt!")
|
||||||
|
await self.process_voice_command()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
pa.terminate()
|
||||||
|
|
||||||
|
async def process_voice_command(self):
|
||||||
|
print("🎤 Ich höre zu... (sprich natürlich)")
|
||||||
|
|
||||||
|
audio_data = await self.record_with_vad(max_duration=12)
|
||||||
|
|
||||||
|
if len(audio_data) < 8000: # zu kurz
|
||||||
|
print("❌ Zu kurz.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# STT
|
||||||
|
segments, _ = self.stt_model.transcribe(
|
||||||
|
audio_data, language="de", beam_size=5, vad_filter=True
|
||||||
|
)
|
||||||
|
text = " ".join(segment.text for segment in segments).strip()
|
||||||
|
|
||||||
|
if not text:
|
||||||
|
print("❌ Konnte nichts verstehen.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"👤 Du: {text}")
|
||||||
|
|
||||||
|
response = await self.agent.run(text)
|
||||||
|
print(f"🗣️ N.O.R.A: {response}")
|
||||||
|
|
||||||
|
await self.speak(response)
|
||||||
|
|
||||||
|
async def record_with_vad(self, max_duration=12):
|
||||||
|
"""Aufnahme mit Sprach-Erkennung (endet wenn du aufhörst zu sprechen)"""
|
||||||
|
pa = pyaudio.PyAudio()
|
||||||
|
stream = pa.open(format=pyaudio.paInt16, channels=1, rate=16000,
|
||||||
|
input=True, frames_per_buffer=320)
|
||||||
|
|
||||||
|
frames = []
|
||||||
|
silence_count = 0
|
||||||
|
max_silence = 25 # ca. 1,5 Sekunden Stille
|
||||||
|
|
||||||
|
for _ in range(int(16000 / 320 * max_duration)):
|
||||||
|
chunk = stream.read(320, exception_on_overflow=False)
|
||||||
|
frames.append(chunk)
|
||||||
|
|
||||||
|
# VAD prüfen
|
||||||
|
is_speech = self.vad.is_speech(chunk, 16000)
|
||||||
|
if not is_speech:
|
||||||
|
silence_count += 1
|
||||||
|
else:
|
||||||
|
silence_count = 0
|
||||||
|
|
||||||
|
if silence_count > max_silence and len(frames) > 30:
|
||||||
|
break # aufhören wenn lange Stille
|
||||||
|
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
pa.terminate()
|
||||||
|
|
||||||
|
return b''.join(frames)
|
||||||
|
|
||||||
|
async def speak(self, text: str):
|
||||||
|
"""Edge TTS - weibliche Stimme"""
|
||||||
|
try:
|
||||||
|
communicate = Communicate(text, voice="de-DE-AmalaNeural") # Weiblich, natürlich
|
||||||
|
|
||||||
|
await communicate.save("response.mp3")
|
||||||
|
os.system("mpg123 -q response.mp3")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"TTS Fehler: {e}")
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
voice = VoiceNora()
|
||||||
|
try:
|
||||||
|
await voice.listen_for_wakeword()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n\n👋 N.O.R.A wird beendet.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(str(e))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
Reference in New Issue
Block a user