From 7bbe0ed96f452187cc5da04f49b5ae7f7d0f7d3a Mon Sep 17 00:00:00 2001 From: imonlinux <39863321+imonlinux@users.noreply.github.com> Date: Tue, 23 Dec 2025 14:03:18 -0800 Subject: [PATCH] Add test file that captures microphone audio uses the same code that the LVA uses to capture microphone audio. Defaults to downmix and accepts left and right as variable for --channel. --- tests/lva_mic_capture.py | 73 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 tests/lva_mic_capture.py diff --git a/tests/lva_mic_capture.py b/tests/lva_mic_capture.py new file mode 100644 index 0000000..f7dce80 --- /dev/null +++ b/tests/lva_mic_capture.py @@ -0,0 +1,73 @@ +import argparse +import soundcard as sc +import numpy as np +import wave + +DEVICE = "reSpeaker XVF3800 4-Mic Array Analog Stereo" +SECONDS = 5 +SR = 16000 +BLOCK = 1024 + +def main(): + parser = argparse.ArgumentParser(description="Record from soundcard mic to WAV.") + parser.add_argument( + "--channel", + choices=["mix", "left", "right"], + default="mix", + help="mix=record 1ch (downmix/default). left/right=record 2ch and select.", + ) + parser.add_argument("--seconds", type=int, default=SECONDS) + parser.add_argument("--device", type=str, default=DEVICE) + parser.add_argument("--out", type=str, default="/tmp/lva_soundcard.wav") + args = parser.parse_args() + + if args.channel == "mix": + in_ch = 1 + out_ch = 1 + out_path = args.out + else: + in_ch = 2 + out_ch = 1 + # If user didn't override --out, make filename reflect selection + if args.out == "/tmp/lva_soundcard.wav": + out_path = f"/tmp/lva_soundcard_{args.channel}.wav" + else: + out_path = args.out + + mic = sc.get_microphone(args.device, include_loopback=False) + print("Using mic:", mic.name) + print(f"Mode: {args.channel} (request channels={in_ch} -> write channels={out_ch})") + + frames = [] + with mic.recorder(samplerate=SR, channels=in_ch, blocksize=BLOCK) as r: + for _ in range(int(SR * args.seconds / BLOCK)): + buf = r.record(BLOCK) + + if in_ch == 1: + # buf can be (BLOCK,) or (BLOCK, 1) depending on backend + mono = buf.reshape(-1) + sample = mono + else: + # buf should be (BLOCK, 2) + if buf.ndim != 2 or buf.shape[1] < 2: + raise RuntimeError(f"Expected stereo buffer, got shape {buf.shape}") + idx = 0 if args.channel == "left" else 1 + sample = buf[:, idx] + + rms = float(np.sqrt(np.mean(np.square(sample)))) if sample.size else 0.0 + print(f"rms={rms:.6f}") + + pcm16 = (np.clip(sample, -1.0, 1.0) * 32767.0).astype("