added auto mute when holding v so i dont hear myself
This commit is contained in:
+29
-7
@@ -40,7 +40,7 @@ class SoundReceiver:
|
|||||||
PREBUFFER_CHUNKS = 3
|
PREBUFFER_CHUNKS = 3
|
||||||
SILENCE_CHUNK = b"\x00" * 4096
|
SILENCE_CHUNK = b"\x00" * 4096
|
||||||
|
|
||||||
def __init__(self, output_device_index=None, mic_gain=8.0):
|
def __init__(self, output_device_index=None, mic_gain=8.0, pyaudio_instance=None):
|
||||||
self.running = True
|
self.running = True
|
||||||
self.underrun_count = 0
|
self.underrun_count = 0
|
||||||
self.chunks_received = 0
|
self.chunks_received = 0
|
||||||
@@ -48,9 +48,12 @@ class SoundReceiver:
|
|||||||
self._peak_since_print = 0
|
self._peak_since_print = 0
|
||||||
self._last_status_print = 0
|
self._last_status_print = 0
|
||||||
self.mic_gain = float(mic_gain)
|
self.mic_gain = float(mic_gain)
|
||||||
|
self.muted = False
|
||||||
|
self._owns_pyaudio = False
|
||||||
|
|
||||||
if HAS_AUDIO:
|
if HAS_AUDIO:
|
||||||
self.p = pyaudio.PyAudio()
|
self.p = pyaudio_instance if pyaudio_instance is not None else pyaudio.PyAudio()
|
||||||
|
self._owns_pyaudio = pyaudio_instance is None
|
||||||
print("🔈 Available output devices:")
|
print("🔈 Available output devices:")
|
||||||
for i in range(self.p.get_device_count()):
|
for i in range(self.p.get_device_count()):
|
||||||
info = self.p.get_device_info_by_index(i)
|
info = self.p.get_device_info_by_index(i)
|
||||||
@@ -117,6 +120,9 @@ class SoundReceiver:
|
|||||||
|
|
||||||
def _write_chunk(self, raw_bytes):
|
def _write_chunk(self, raw_bytes):
|
||||||
try:
|
try:
|
||||||
|
if self.muted:
|
||||||
|
self.stream.write(b"\x00" * len(raw_bytes))
|
||||||
|
return
|
||||||
samples = np.frombuffer(raw_bytes, dtype=np.int16).astype(np.float32)
|
samples = np.frombuffer(raw_bytes, dtype=np.int16).astype(np.float32)
|
||||||
if samples.size:
|
if samples.size:
|
||||||
amplified = np.clip(samples * self.mic_gain, -32768, 32767)
|
amplified = np.clip(samples * self.mic_gain, -32768, 32767)
|
||||||
@@ -136,7 +142,8 @@ class SoundReceiver:
|
|||||||
try:
|
try:
|
||||||
self.stream.stop_stream()
|
self.stream.stop_stream()
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
self.p.terminate()
|
if self._owns_pyaudio:
|
||||||
|
self.p.terminate()
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
@@ -151,7 +158,7 @@ class LiveMicRelay:
|
|||||||
stop() ends the remote nc process, which ends aplay with it.
|
stop() ends the remote nc process, which ends aplay with it.
|
||||||
"""
|
"""
|
||||||
def __init__(self, nao_ip, ssh_user="nao", ssh_password="nao",
|
def __init__(self, nao_ip, ssh_user="nao", ssh_password="nao",
|
||||||
port=5555, rate=16000, input_device_index=None):
|
port=5555, rate=16000, input_device_index=None, pyaudio_instance=None):
|
||||||
self.nao_ip = nao_ip
|
self.nao_ip = nao_ip
|
||||||
self.ssh_user = ssh_user
|
self.ssh_user = ssh_user
|
||||||
self.ssh_password = ssh_password
|
self.ssh_password = ssh_password
|
||||||
@@ -165,7 +172,10 @@ class LiveMicRelay:
|
|||||||
self._sock = None
|
self._sock = None
|
||||||
self._stream = None
|
self._stream = None
|
||||||
self._thread = None
|
self._thread = None
|
||||||
self._p = pyaudio.PyAudio() if HAS_AUDIO else None
|
# Reuse the app's single PyAudio context - a second independent one
|
||||||
|
# opened from a background thread crashes PortAudio's PulseAudio
|
||||||
|
# backend (pa_atomic_load assertion).
|
||||||
|
self._p = pyaudio_instance if pyaudio_instance is not None else (pyaudio.PyAudio() if HAS_AUDIO else None)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if self.active:
|
if self.active:
|
||||||
@@ -266,8 +276,13 @@ class NaoTeleop:
|
|||||||
self.mic_channel = mic_channel
|
self.mic_channel = mic_channel
|
||||||
self.mic_gain = mic_gain
|
self.mic_gain = mic_gain
|
||||||
|
|
||||||
|
# One shared PyAudio context for the whole app - SoundReceiver (mic
|
||||||
|
# playback) and LiveMicRelay (push-to-talk capture) both use it,
|
||||||
|
# since two separate contexts crash PortAudio's PulseAudio backend.
|
||||||
|
self._pyaudio = pyaudio.PyAudio() if HAS_AUDIO else None
|
||||||
|
|
||||||
self.mic_relay = LiveMicRelay(nao_ip, ssh_user=nao_ssh_user, ssh_password=nao_ssh_password,
|
self.mic_relay = LiveMicRelay(nao_ip, ssh_user=nao_ssh_user, ssh_password=nao_ssh_password,
|
||||||
port=relay_port, rate=relay_rate)
|
port=relay_port, rate=relay_rate, pyaudio_instance=self._pyaudio)
|
||||||
self.ptt_active = False
|
self.ptt_active = False
|
||||||
|
|
||||||
self.running = True
|
self.running = True
|
||||||
@@ -328,7 +343,8 @@ class NaoTeleop:
|
|||||||
def _init_mic_stream(self):
|
def _init_mic_stream(self):
|
||||||
if not self.audio_device: return
|
if not self.audio_device: return
|
||||||
try:
|
try:
|
||||||
self.sound_receiver = SoundReceiver(self.audio_device_index, self.mic_gain)
|
self.sound_receiver = SoundReceiver(self.audio_device_index, self.mic_gain,
|
||||||
|
pyaudio_instance=self._pyaudio)
|
||||||
self.session.registerService(self.audio_service_name, self.sound_receiver)
|
self.session.registerService(self.audio_service_name, self.sound_receiver)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.audio_device.setClientPreferences(self.audio_service_name, 16000, self.mic_channel, 0)
|
self.audio_device.setClientPreferences(self.audio_service_name, 16000, self.mic_channel, 0)
|
||||||
@@ -605,9 +621,13 @@ class NaoTeleop:
|
|||||||
if keys[pygame.K_v] and not self.ptt_active:
|
if keys[pygame.K_v] and not self.ptt_active:
|
||||||
self.ptt_active = True
|
self.ptt_active = True
|
||||||
self.mic_relay.start()
|
self.mic_relay.start()
|
||||||
|
if getattr(self, "sound_receiver", None):
|
||||||
|
self.sound_receiver.muted = True
|
||||||
elif not keys[pygame.K_v] and self.ptt_active:
|
elif not keys[pygame.K_v] and self.ptt_active:
|
||||||
self.ptt_active = False
|
self.ptt_active = False
|
||||||
self.mic_relay.stop()
|
self.mic_relay.stop()
|
||||||
|
if getattr(self, "sound_receiver", None):
|
||||||
|
self.sound_receiver.muted = False
|
||||||
|
|
||||||
speed_changed = False
|
speed_changed = False
|
||||||
if keys[pygame.K_LEFTBRACKET]:
|
if keys[pygame.K_LEFTBRACKET]:
|
||||||
@@ -721,6 +741,8 @@ class NaoTeleop:
|
|||||||
self._safe("sound receiver close", self.sound_receiver.close)
|
self._safe("sound receiver close", self.sound_receiver.close)
|
||||||
if hasattr(self, 'video') and self.video_client:
|
if hasattr(self, 'video') and self.video_client:
|
||||||
self._safe("video unsubscribe", lambda: self.video.unsubscribe(self.video_client))
|
self._safe("video unsubscribe", lambda: self.video.unsubscribe(self.video_client))
|
||||||
|
if getattr(self, "_pyaudio", None):
|
||||||
|
self._safe("pyaudio terminate", self._pyaudio.terminate)
|
||||||
self._safe("rest", self.motion.rest)
|
self._safe("rest", self.motion.rest)
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user