fix: add portaudio to LD_LIBRARY_PATH and add flake lockfile

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>
This commit is contained in:
2026-05-30 00:42:36 +08:00
parent 843ec534d1
commit cbea62b2a9
3 changed files with 119 additions and 5 deletions
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1779786838,
"narHash": "sha256-0geHoGiR5f8qiXg+gO4rSF6Up6Var+kKqiOv9AO/uUc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f44f7788c891fbe5542177df78374f8cdab10e8f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+4 -5
View File
@@ -21,11 +21,10 @@
cudaPackages.cudatoolkit
];
env = {
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.cudaPackages.cudatoolkit
];
};
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.portaudio
pkgs.cudaPackages.cudatoolkit
];
};
};
}
+88
View File
@@ -0,0 +1,88 @@
"""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!")