From a4191f3461e684ae89be02c3abbe99e621778267 Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Thu, 27 Jun 2024 08:55:26 -0700 Subject: [PATCH 1/4] tools/capture-test.py: pylint cleanups These warnings snuck through earlier; silence or evade. Signed-off-by: Andy Ross --- tools/capture-test.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/capture-test.py b/tools/capture-test.py index b90efb08..40e45514 100755 --- a/tools/capture-test.py +++ b/tools/capture-test.py @@ -46,7 +46,7 @@ """ def parse_opts(): - global opts + global opts # pylint: disable=global-statement ap = argparse.ArgumentParser(description=HELP_TEXT, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("--disable-rtnr", action="store_true", help="Disable RTNR noise reduction") @@ -103,6 +103,7 @@ def err_wrap(ret): return ret def alloc(self, typ): return (C.c_byte * getattr(self.lib, f"snd_{typ}_sizeof")())() + # pylint: disable=too-few-public-methods class pcm_channel_area_t(C.Structure): _fields_ = [("addr", C.c_ulong), ("first", C.c_int), ("step", C.c_int)] @@ -352,7 +353,8 @@ def echo_test(): # Just slurps in the wav file and chops off the header, assuming # the user got the format and sampling rate correct. WAV_HDR_LEN = 44 - buf = open(opts.noise, "rb").read()[WAV_HDR_LEN:] + with open(opts.noise, "rb") as f: + buf = f.read()[WAV_HDR_LEN:] (rfd, wfd) = os.pipe() pid = os.fork() From e09defecb7ed17f5536a2e57e7eb76ead42a5780 Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Thu, 27 Jun 2024 08:56:29 -0700 Subject: [PATCH 2/4] tools/capture-test.py: Fix ALSA parameter This was a usage goof, the final "dir" argument to snd_pcm_hw_params_set_rate() is not specifying the direction of the stream, it's specifying the "direction" in which the code will err if it has to pick a non-matching value. In this case the parameter had a value of zero ("exact match", what we want) anyway, so it was an invisible noop. Signed-off-by: Andy Ross --- tools/capture-test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/capture-test.py b/tools/capture-test.py index 40e45514..faf9c46a 100755 --- a/tools/capture-test.py +++ b/tools/capture-test.py @@ -112,7 +112,7 @@ def pcm_init_stream(pcm, rate, chans, fmt, access): alsa.snd_pcm_hw_params_any(pcm, hwp) alsa.snd_pcm_hw_params_set_format(pcm, hwp, fmt) alsa.snd_pcm_hw_params_set_channels(pcm, hwp, chans) - alsa.snd_pcm_hw_params_set_rate(pcm, hwp, rate, alsa.PCM_STREAM_PLAYBACK) + alsa.snd_pcm_hw_params_set_rate(pcm, hwp, rate, 0) alsa.snd_pcm_hw_params_set_access(pcm, hwp, access) alsa.snd_pcm_hw_params(pcm, hwp) From 1a864ad669e88ca13160babbace52a4b238f228d Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Tue, 18 Jun 2024 15:07:35 -0700 Subject: [PATCH 3/4] capture-test: Add buffer size control & verbose logging Recent SOF drivers have started populating the buffer size of ALSA hw_params structs with values that produce an -EINVAL if you try to set them (unclear to me if this is a bug or not). Regardless, the capture test wants control over that, so add it. Also hook the ALSA hw_params dump code (for debugging issues like this) and hide it under a "--verbose" flag. Signed-off-by: Andy Ross --- tools/capture-test.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/capture-test.py b/tools/capture-test.py index faf9c46a..a5d50c2f 100755 --- a/tools/capture-test.py +++ b/tools/capture-test.py @@ -49,12 +49,14 @@ def parse_opts(): global opts # pylint: disable=global-statement ap = argparse.ArgumentParser(description=HELP_TEXT, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("-v", "--verbose", action="store_true", help="Verbose output") ap.add_argument("--disable-rtnr", action="store_true", help="Disable RTNR noise reduction") ap.add_argument("-c", "--card", type=int, default=0, help="ALSA card index") ap.add_argument("--pcm", type=int, default=16, help="Output ALSA PCM index") ap.add_argument("--cap", type=int, default=18, help="Capture ALSA PCM index") ap.add_argument("--rate", type=int, default=48000, help="Sample rate") ap.add_argument("--chan", type=int, default=2, help="Output channel count") + ap.add_argument("--bufsz", type=int, default=2048, help="Buffer size in frames") ap.add_argument("--capchan", type=int, help="Capture channel count (if different from output)") ap.add_argument("--capbits", type=int, default=16, help="Capture sample bits (16 or 32)") @@ -114,6 +116,16 @@ def pcm_init_stream(pcm, rate, chans, fmt, access): alsa.snd_pcm_hw_params_set_channels(pcm, hwp, chans) alsa.snd_pcm_hw_params_set_rate(pcm, hwp, rate, 0) alsa.snd_pcm_hw_params_set_access(pcm, hwp, access) + alsa.snd_pcm_hw_params_set_buffer_size(pcm, hwp, opts.bufsz) + if opts.verbose: + print("Set hw_params:") + out = C.c_ulong(0) + alsa.snd_output_buffer_open(C.byref(out)) + alsa.snd_pcm_hw_params_dump(hwp, out) + buf = C.c_ulong(0) + alsa.snd_output_buffer_string(out, C.byref(buf)) + print(C.string_at(buf.value).decode("ascii", errors="ignore")) + alsa.snd_pcm_hw_params(pcm, hwp) def ctl_disable_rtnr(): From f717aa3e373520464a4c3794d61fb4685e5be82e Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Thu, 20 Jun 2024 13:00:08 -0700 Subject: [PATCH 4/4] capture-test.py: Add channel map feature Hardware is awful. Some MTL boards needlessly move their 2-channel mic inputs around in the 4 channel DMIC space, so doing the test in an automated way needs to duplicate the idea of a channel map. Thankfully this is python and not firmware so it's like six lines to add a --capmap argument. It's a string, so e.g. "--capmap 23" will select out the 3rd and 4th capture channels as the 2-channel stream to inspect. Signed-off-by: Andy Ross --- tools/capture-test.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/capture-test.py b/tools/capture-test.py index a5d50c2f..652f4a80 100755 --- a/tools/capture-test.py +++ b/tools/capture-test.py @@ -60,6 +60,8 @@ def parse_opts(): ap.add_argument("--capchan", type=int, help="Capture channel count (if different from output)") ap.add_argument("--capbits", type=int, default=16, help="Capture sample bits (16 or 32)") + ap.add_argument("--capmap", type=str, default="01", + help="Capture channel map (as string, e.g. '23' to select 3rd/4th elems)") ap.add_argument("--noise", default="noise.wav", help="WAV file containing 'noise' for capture") ap.add_argument("--duration", type=int, default=3, help="Capture duration (seconds)") @@ -71,6 +73,7 @@ def parse_opts(): opts = ap.parse_args() if not opts.capchan: opts.capchan = opts.chan + opts.capmap = [int(x) for x in opts.capmap] opts.base_test = not (opts.chirp_test or opts.echo_test) class ALSA: @@ -295,7 +298,9 @@ def cap_to_playback(buf): # treat it, and it can plausibly create false positive chirp signals # loud enough). for i in range(0, len(buf), capsz): - frame = [scale * x for x in struct.unpack(capfmt, buf[i:i+capsz])[0:opts.chan]] + frame = struct.unpack(capfmt, buf[i:i+capsz]) # Decode + frame = [frame[n] for n in opts.capmap] # Select via channel map + frame = [scale * x for x in frame] # Convert to float if last_frame: delta_sum += sum(abs(last_frame[x] - frame[x]) for x in range(opts.chan)) last_frame = frame