attempt to fix mic audio streaming
This commit is contained in:
Binary file not shown.
+101
@@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- encoding: UTF-8 -*-
|
||||||
|
"""
|
||||||
|
Standalone NAO audio diagnostic - deliberately bypasses naowalk.py entirely.
|
||||||
|
|
||||||
|
Run this FIRST when troubleshooting "I can't hear the robot's mic". It tests
|
||||||
|
each link in the chain independently, so a failure tells you exactly where
|
||||||
|
the problem is instead of "somewhere in 300 lines of PyAudio/threading code":
|
||||||
|
|
||||||
|
TEST 1 - Front mic hardware energy.
|
||||||
|
Uses ALAudioDevice's built-in energy meter. No PyAudio, no
|
||||||
|
network audio streaming, no laptop audio stack involved at all.
|
||||||
|
This is the lowest-level check of "does the mic itself work".
|
||||||
|
|
||||||
|
TEST 2 - Local on-robot recording.
|
||||||
|
Records straight to a .wav file on the robot's own disk, then
|
||||||
|
you pull it off and play it normally. This bypasses the live
|
||||||
|
network stream, the SoundReceiver/queue/threading code, and
|
||||||
|
your laptop's PyAudio output path - all at once.
|
||||||
|
|
||||||
|
TEST 3 - Robot's own speaker.
|
||||||
|
Plays a tone through the ROBOT's speaker (not your laptop), to
|
||||||
|
confirm robot-side audio output is fine, independent of any of
|
||||||
|
the mic-listening code.
|
||||||
|
|
||||||
|
If TEST 1 shows no energy change when you make noise: mic hardware/mute/
|
||||||
|
environment issue, upstream of any code in this project.
|
||||||
|
If TEST 1 works but TEST 2's file is silent: something is wrong with the
|
||||||
|
recording/robot-audio-stack itself, still nothing to do with naowalk.py.
|
||||||
|
If TEST 2's file has clear audio: the mic and robot are fine, and the bug
|
||||||
|
is specifically in naowalk.py's live-streaming path (network delivery,
|
||||||
|
PyAudio device selection, etc.) - go back to the diagnostics already
|
||||||
|
built into naowalk.py (device list / test beep / peak level) to isolate it
|
||||||
|
further.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 nao_audio_diagnostic.py --ip spike.local --port 9559
|
||||||
|
"""
|
||||||
|
|
||||||
|
import qi
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--ip", type=str, default="127.0.0.1")
|
||||||
|
parser.add_argument("--port", type=int, default=9559)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
session = qi.Session()
|
||||||
|
session.connect(f"tcp://{args.ip}:{args.port}")
|
||||||
|
print(f"✅ Connected to {args.ip}")
|
||||||
|
audio = session.service("ALAudioDevice")
|
||||||
|
|
||||||
|
# --- TEST 1: mic hardware energy, all four channels ------------------------
|
||||||
|
print("\n=== TEST 1: Mic hardware energy (all 4 mics) ===")
|
||||||
|
print("Make noise near the robot's head (clap, talk right up close to it) for 10s...")
|
||||||
|
audio.enableEnergyComputation()
|
||||||
|
front_readings, rear_readings, left_readings, right_readings = [], [], [], []
|
||||||
|
for i in range(10):
|
||||||
|
f, r, l, rt = (audio.getFrontMicEnergy(), audio.getRearMicEnergy(),
|
||||||
|
audio.getLeftMicEnergy(), audio.getRightMicEnergy())
|
||||||
|
front_readings.append(f); rear_readings.append(r); left_readings.append(l); right_readings.append(rt)
|
||||||
|
print(f" t={i}s front={f:.2f} rear={r:.2f} left={l:.2f} right={rt:.2f}")
|
||||||
|
time.sleep(1)
|
||||||
|
audio.disableEnergyComputation()
|
||||||
|
|
||||||
|
results = {"front": front_readings, "rear": rear_readings, "left": left_readings, "right": right_readings}
|
||||||
|
flat = [name for name, vals in results.items() if max(vals) - min(vals) < 2]
|
||||||
|
if len(flat) == 4:
|
||||||
|
print("⚠️ ALL FOUR mics are flat. This points to something systemic - e.g. audio "
|
||||||
|
"inputs closed (closeAudioInputs() called somewhere and never reopened), "
|
||||||
|
"or a low-level hardware/driver fault - rather than one damaged mic.")
|
||||||
|
elif flat:
|
||||||
|
print(f"⚠️ Flat mic(s): {', '.join(flat)}. The others responded normally, which "
|
||||||
|
f"points to a fault specific to {', '.join(flat)} rather than the audio "
|
||||||
|
f"pipeline as a whole (that pipeline is clearly working for the others).")
|
||||||
|
else:
|
||||||
|
print("✅ All four mics responded to noise - hardware capture is fine across the board.")
|
||||||
|
|
||||||
|
# --- TEST 2: local on-robot recording ---------------------------------------
|
||||||
|
print("\n=== TEST 2: Local on-robot recording ===")
|
||||||
|
remote_path = "/home/nao/mic_test.wav"
|
||||||
|
print(f"Recording 5s directly to {remote_path} on the robot itself "
|
||||||
|
f"(this bypasses the network stream and your laptop's audio stack entirely)...")
|
||||||
|
audio.startMicrophonesRecording(remote_path)
|
||||||
|
time.sleep(5)
|
||||||
|
audio.stopMicrophonesRecording()
|
||||||
|
print(f"Done. Pull it off and play it with:")
|
||||||
|
print(f" scp nao@{args.ip}:{remote_path} .")
|
||||||
|
print(f" (then open mic_test.wav in any media player - it's 4-channel, so check all channels)")
|
||||||
|
print("If that file has clearly audible sound, the mic and robot are fine and the")
|
||||||
|
print("bug is specifically in naowalk.py's live-streaming path.")
|
||||||
|
|
||||||
|
# --- TEST 3: robot's own speaker, via TTS (version-agnostic) ---------------
|
||||||
|
print("\n=== TEST 3: Robot's own speaker ===")
|
||||||
|
input("Press Enter to have the robot say a test phrase out loud...")
|
||||||
|
session.service("ALTextToSpeech").say("Testing 1 2 3, can you hear me.")
|
||||||
|
print("If you heard the robot speak just now, its speaker output is fine "
|
||||||
|
"(this is unrelated to hearing its mic on your laptop, but rules out a robot-wide audio fault).")
|
||||||
|
|
||||||
|
print("\nDone. Report back which test(s) failed and we'll know exactly where to look.")
|
||||||
+17
-6
@@ -178,7 +178,7 @@ class SoundReceiver:
|
|||||||
# MAIN TELEOP CLASS
|
# MAIN TELEOP CLASS
|
||||||
# ==========================================
|
# ==========================================
|
||||||
class NaoTeleop:
|
class NaoTeleop:
|
||||||
def __init__(self, session, audio_device_index=None):
|
def __init__(self, session, audio_device_index=None, mic_channel=1):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.motion = session.service("ALMotion")
|
self.motion = session.service("ALMotion")
|
||||||
self.posture = session.service("ALRobotPosture")
|
self.posture = session.service("ALRobotPosture")
|
||||||
@@ -196,6 +196,7 @@ class NaoTeleop:
|
|||||||
self.audio_device = None
|
self.audio_device = None
|
||||||
self.audio_service_name = "SoundReceiver"
|
self.audio_service_name = "SoundReceiver"
|
||||||
self.audio_device_index = audio_device_index # which laptop output device to play the mic feed on
|
self.audio_device_index = audio_device_index # which laptop output device to play the mic feed on
|
||||||
|
self.mic_channel = mic_channel # which NAO mic to stream: LEFTCHANNEL(1)/RIGHTCHANNEL(2)/FRONTCHANNEL(3)/REARCHANNEL(4)
|
||||||
|
|
||||||
self.running = True
|
self.running = True
|
||||||
self.head_yaw = 0.0
|
self.head_yaw = 0.0
|
||||||
@@ -261,10 +262,13 @@ class NaoTeleop:
|
|||||||
# 2. Add a tiny delay to let the network handshake complete
|
# 2. Add a tiny delay to let the network handshake complete
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
# 3. Use the IP of the machine running the code
|
# 3. Which physical mic to stream. FRONTCHANNEL(3) was the
|
||||||
# We explicitly tell the robot the IP of your laptop (replace with your laptop IP if still failing)
|
# original default, but on this robot the front and rear mics
|
||||||
# '0' tells it to auto-discover, but sometimes we need to be explicit.
|
# test dead (flat energy readings even up close) while left
|
||||||
self.audio_device.setClientPreferences(self.audio_service_name, 16000, 3, 0)
|
# and right respond normally - see nao_audio_diagnostic.py.
|
||||||
|
# Defaulting to LEFTCHANNEL(1) accordingly; override with
|
||||||
|
# --mic-channel if you want to compare.
|
||||||
|
self.audio_device.setClientPreferences(self.audio_service_name, 16000, self.mic_channel, 0)
|
||||||
self.audio_device.subscribe(self.audio_service_name)
|
self.audio_device.subscribe(self.audio_service_name)
|
||||||
|
|
||||||
print("✅ Live Audio Stream ready")
|
print("✅ Live Audio Stream ready")
|
||||||
@@ -479,6 +483,8 @@ class NaoTeleop:
|
|||||||
pygame.quit()
|
pygame.quit()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
MIC_CHANNELS = {"left": 1, "right": 2, "front": 3, "rear": 4}
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--ip", type=str, default="127.0.0.1")
|
parser.add_argument("--ip", type=str, default="127.0.0.1")
|
||||||
parser.add_argument("--port", type=int, default=9559)
|
parser.add_argument("--port", type=int, default=9559)
|
||||||
@@ -486,6 +492,10 @@ if __name__ == "__main__":
|
|||||||
help="Force a specific PyAudio output device index for the robot's "
|
help="Force a specific PyAudio output device index for the robot's "
|
||||||
"mic feed (see the '🔈 Available output devices' list printed "
|
"mic feed (see the '🔈 Available output devices' list printed "
|
||||||
"at startup) if the system default isn't your speakers.")
|
"at startup) if the system default isn't your speakers.")
|
||||||
|
parser.add_argument("--mic-channel", choices=MIC_CHANNELS.keys(), default="left",
|
||||||
|
help="Which NAO mic to stream. Defaults to 'left' since "
|
||||||
|
"nao_audio_diagnostic.py confirmed front/rear are dead on "
|
||||||
|
"this unit while left/right respond normally.")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
session = qi.Session()
|
session = qi.Session()
|
||||||
@@ -496,4 +506,5 @@ if __name__ == "__main__":
|
|||||||
print(f"❌ Connection failed: {e}")
|
print(f"❌ Connection failed: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
NaoTeleop(session, audio_device_index=args.audio_device_index).run()
|
NaoTeleop(session, audio_device_index=args.audio_device_index,
|
||||||
|
mic_channel=MIC_CHANNELS[args.mic_channel]).run()
|
||||||
|
|||||||
Reference in New Issue
Block a user