cbea62b2a9
Move LD_LIBRARY_PATH out of env block and include portaudio so audio devices are discoverable at runtime. Add flake.lock and a quick microphone test script. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
89 lines
3.0 KiB
Python
89 lines
3.0 KiB
Python
"""Quick microphone tests. Run: uv run python test_mic.py"""
|
|
|
|
import numpy as np
|
|
import sounddevice as sd
|
|
import sys
|
|
import time
|
|
|
|
SAMPLE_RATE = 16000
|
|
|
|
|
|
def test_device_info():
|
|
"""Show which device will be used for recording."""
|
|
default_input = sd.default.device[0]
|
|
info = sd.query_devices(default_input)
|
|
print(f"Default input device [{default_input}]: {info['name']}")
|
|
print(f" Max input channels: {info['max_input_channels']}")
|
|
print(f" Default sample rate: {info['default_samplerate']}")
|
|
assert info["max_input_channels"] > 0, "Default device has no input channels!"
|
|
print(" PASS\n")
|
|
|
|
|
|
def test_record_1s():
|
|
"""Record 1 second and check we got non-silent audio."""
|
|
print("Recording 1 second... (speak or make noise!)")
|
|
audio = sd.rec(SAMPLE_RATE, samplerate=SAMPLE_RATE, channels=1, dtype="float32")
|
|
sd.wait()
|
|
audio = audio.flatten()
|
|
|
|
peak = np.max(np.abs(audio))
|
|
rms = np.sqrt(np.mean(audio ** 2))
|
|
print(f" Samples: {len(audio)}")
|
|
print(f" Peak amplitude: {peak:.4f}")
|
|
print(f" RMS: {rms:.6f}")
|
|
|
|
assert len(audio) == SAMPLE_RATE, f"Expected {SAMPLE_RATE} samples, got {len(audio)}"
|
|
assert peak > 0, "All zeros — mic not capturing anything"
|
|
if peak < 0.001:
|
|
print(" WARNING: Very low signal — mic might be muted or too far away")
|
|
else:
|
|
print(" Signal level looks good")
|
|
print(" PASS\n")
|
|
|
|
|
|
def test_record_levels():
|
|
"""Record 3 seconds in 1-second chunks, show live levels."""
|
|
print("Recording 3 seconds — speak during seconds 2-3 for comparison...")
|
|
for i in range(3):
|
|
audio = sd.rec(SAMPLE_RATE, samplerate=SAMPLE_RATE, channels=1, dtype="float32")
|
|
sd.wait()
|
|
audio = audio.flatten()
|
|
rms = np.sqrt(np.mean(audio ** 2))
|
|
peak = np.max(np.abs(audio))
|
|
bar = "#" * int(min(peak * 200, 50))
|
|
print(f" Second {i+1}: peak={peak:.4f} rms={rms:.6f} |{bar}")
|
|
print(" PASS\n")
|
|
|
|
|
|
def test_stream_callback():
|
|
"""Test that InputStream callback fires correctly."""
|
|
frames_received = []
|
|
|
|
def callback(indata, frames, time_info, status):
|
|
if status:
|
|
print(f" Status: {status}")
|
|
frames_received.append(len(indata))
|
|
|
|
print("Testing InputStream callback for 1 second...")
|
|
with sd.InputStream(samplerate=SAMPLE_RATE, channels=1, dtype="float32",
|
|
callback=callback, blocksize=800):
|
|
time.sleep(1)
|
|
|
|
total_frames = sum(frames_received)
|
|
expected = SAMPLE_RATE
|
|
print(f" Callbacks fired: {len(frames_received)}")
|
|
print(f" Total frames: {total_frames} (expected ~{expected})")
|
|
print(f" Blocksize per callback: {frames_received[0] if frames_received else 'N/A'}")
|
|
assert len(frames_received) > 0, "No callbacks received!"
|
|
assert abs(total_frames - expected) < expected * 0.2, f"Frame count off by >20%"
|
|
print(" PASS\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("=== Microphone Tests ===\n")
|
|
test_device_info()
|
|
test_record_1s()
|
|
test_record_levels()
|
|
test_stream_callback()
|
|
print("All tests passed!")
|