From b209394d8df66edcd8436192f34b1304371b7baf Mon Sep 17 00:00:00 2001 From: Till-Immer Date: Sun, 5 Jul 2026 16:17:35 +0200 Subject: [PATCH] added voice pipiline --- .gitignore | 4 + __pycache__/config.cpython-311.pyc | Bin 360 -> 602 bytes voice_nora.py | 132 +++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 .gitignore create mode 100644 voice_nora.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7d4344 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +.env +venv/ + diff --git a/__pycache__/config.cpython-311.pyc b/__pycache__/config.cpython-311.pyc index 65f72c1b02c0478f0f6daecc2ddf50ed38ed1d59..766f5f60727d9bd2a28a515f7063d741dbb84c34 100644 GIT binary patch delta 386 zcmaFCbc=;=IWI340}$*|^vV)voX97^cy6M)Z#@%3Dnk}CNCE^>Se7v|Fsue*2#8`u zlV?SfXG4=`Lz8DmlV?Yl=SX1;X3*rASSRMsF;+-5X5$Lc6JR8j`wzr zjQ4c8B?=M>c6APN4MCE*#p~qi66Ejf9q;S!;_7pY-PbuFJ~YTjlXK#BIguh(pg)S( zKmARsw`X$t2I{fh!xR|K>gcpq>HcPRB} VPVl_QC3l5Ou7Mo{i+F+d004s=RX_j$ delta 163 zcmcb`@`8zPIWI340}$*z>5}yT)V#7=eD1Eko_?P3jsc$W-mZ~V0_mx_nR%JIMtY{YX*r3-8JcX9;~C|6ZgKhg zySVzq`#Ji$PM*l9&&2~YjS+~8-6o%6)Rz~Onjkbq@`AMXMM0e_f;tU+4|wGpxInN7 G 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())