From 63295374175200332da1cc6162a92b1b340369ec Mon Sep 17 00:00:00 2001 From: Arkadiusz Cholewinski Date: Mon, 5 May 2025 13:34:37 +0200 Subject: [PATCH 1/3] TinyAlsa: Add support for tinyAlsa. Add support for tinyALSA and update env-check and README file. Signed-off-by: Arkadiusz Cholewinski --- README.md | 20 +++++--- case-lib/lib.sh | 119 +++++++++++++++++++++++++++++++++++++++++------- env-check.sh | 3 ++ 3 files changed, 120 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 3a329c3b..c4fe4b47 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,12 @@ expect alsa-utils python3 python3-construct python3-graphviz ``` sudo apt install expect alsa-utils python3 python3-construct python3-graphviz ``` +If you would like to use tinyALSA for testing, install tinyALSA and SoX. +- How to install tinyALSA: https://github.com/tinyalsa/tinyalsa +- To install SoX run below command: +``` +sudo apt-install sox +``` #### user group sudo adm audio @@ -52,12 +58,14 @@ Usage: ./check-playback.sh [OPTION] 2020-03-19 22:13:32 UTC [COMMAND] aplay -q --fatal-errors -Dhw:0,0 -r 48000 -c 2 -f S16_LE -d 4 /dev/zero -v -q ... ``` - -Some tests support SOF_ALSA_OPTS, SOF_APLAY_OPTS and SOF_ARECORD_OPTS, -work in progress. Where supported, optional parameters in SOF_APLAY_OPTS -and SOF_ARECORD_OPTS are passed to all aplay and arecord -invocations. SOF_ALSA_OPTS parameters are passed to both aplay and -arecord. Warning these environments variables do NOT support parameters +Some tests support these environment variables (work in progress): + - SOF_ALSA_TOOL is used to select the audio tool for testing. + Set this variable to 'alsa' (default value) or 'tinyalsa' to choose between the ALSA and TinyALSA toolsets. + - SOF_ALSA_OPTS contains optional parameters passed on both play and record. + - SOF_APLAY_OPTS and SOF_ARECORD_OPTS contain optional parameters passed additionally on play and record respectively. +These options are applied to the selected tool (alsa or tinyalsa) based on the value of SOF_ALSA_TOOL + +Warning, these environment variables do NOT support parameters with whitespace or globbing characters, in other words this does NOT work: diff --git a/case-lib/lib.sh b/case-lib/lib.sh index 02096638..159d4da1 100644 --- a/case-lib/lib.sh +++ b/case-lib/lib.sh @@ -752,17 +752,88 @@ func_lib_check_pa() # However, 1. arrays would complicate the user interface 2. ALSA does not # seem to need arguments with whitespace or globbing characters. +# SOF_ALSA_TOOL: +# This option is used for selecting tool for testing, +# So far, supported tools are 'alsa' and 'tinyalsa' +# To select appropriate tool, set SOF_ALSA_TOOL to one of above +# before using 'aplay_opts' or 'arecord_opts' function. +# Default is SOF_ALSA_TOOL='alsa' + + +# Function to extract the card number and device number from $dev option (e.g., hw:0,10) +parse_audio_device() { + # Extract the card number (e.g., "0" from hw:0,10) + card_nr=$(printf '%s' "$1" | cut -d ':' -f2 | cut -d ',' -f1) + + # Extract the device number (e.g., "10" from hw:0,10) + dev_nr=$(printf '%s' "$1" | cut -d ',' -f2) +} + +# Function to extract the numeric format value from the PCM sample formats +# There is passes PCM sample format while using ALSA tool (arecord) +# While using TinyALSA (tinycap -b) we need to convert PCM sample fomrat to bits. +extract_format_number() { + # (e.g., extracting '16' from 'S16_LE') + printf '%s' "$1" | grep '[0-9]\+' -o +} + +# Initialize the parameters using for audio testing. +# shellcheck disable=SC2034 +initialize_audio_params() +{ + local idx="$1" + + channel=$(func_pipeline_parse_value "$idx" channel) + rate=$(func_pipeline_parse_value "$idx" rate) + fmts=$(func_pipeline_parse_value "$idx" fmt) + dev=$(func_pipeline_parse_value "$idx" dev) + pcm=$(func_pipeline_parse_value "$idx" pcm) + type=$(func_pipeline_parse_value "$idx" type) + snd=$(func_pipeline_parse_value "$idx" snd) + + : "${SOF_ALSA_TOOL:="alsa"}" + if [[ "$SOF_ALSA_TOOL" = "tinyalsa" ]]; then + parse_audio_device "$dev" + fi +} + aplay_opts() { - dlogc "aplay $SOF_ALSA_OPTS $SOF_APLAY_OPTS $*" - # shellcheck disable=SC2086 - aplay $SOF_ALSA_OPTS $SOF_APLAY_OPTS "$@" + if [[ "$SOF_ALSA_TOOL" = "tinyalsa" ]]; then + # shellcheck disable=SC2154 + if ! sox -n -r "$rate" -c "$channel" noise.wav synth "$duration" white; then + printf 'Error: sox command failed.\n' >&2 + return 1 + fi + dlogc "tinyplay $SOF_ALSA_OPTS $SOF_APLAY_OPTS -D $card_nr -d $dev_nr -i wav noise.wav" + # shellcheck disable=SC2086 + tinyplay $SOF_ALSA_OPTS $SOF_APLAY_OPTS -D "$card_nr" -d "$dev_nr" -i wav noise.wav + elif [[ "$SOF_ALSA_TOOL" = "alsa" ]]; then + dlogc "aplay $SOF_ALSA_OPTS $SOF_APLAY_OPTS $*" + # shellcheck disable=SC2086 + aplay $SOF_ALSA_OPTS $SOF_APLAY_OPTS "$@" + else + die "Unknown ALSA tool: $SOF_ALSA_TOOL" + fi } + arecord_opts() { - dlogc "arecord $SOF_ALSA_OPTS $SOF_ARECORD_OPTS $*" - # shellcheck disable=SC2086 - arecord $SOF_ALSA_OPTS $SOF_ARECORD_OPTS "$@" + + if [[ "$SOF_ALSA_TOOL" = "tinyalsa" ]]; then + # shellcheck disable=SC2154 + # Global variable "$fmt_elem" from check_capture.sh test script + format=$(extract_format_number "$fmt_elem") + dlogc "tinycap $SOF_ALSA_OPTS $SOF_ARECORD_OPTS $file -D $card_nr -d $dev_nr -c $channel -t $duration -r $rate -b $format" + # shellcheck disable=SC2086 + tinycap $SOF_ALSA_OPTS $SOF_ARECORD_OPTS "$file" -D "$card_nr" -d "$dev_nr" -c "$channel" -t "$duration" -r "$rate" -b "$format" + elif [[ "$SOF_ALSA_TOOL" = "alsa" ]]; then + dlogc "arecord $SOF_ALSA_OPTS $SOF_ARECORD_OPTS $*" + # shellcheck disable=SC2086 + arecord $SOF_ALSA_OPTS $SOF_ARECORD_OPTS "$@" + else + die "Unknown ALSA tool: $SOF_ALSA_TOOL" + fi } die() @@ -952,7 +1023,7 @@ is_ipc4() logger_disabled() { # Disable logging when available... - if [ ${OPT_VAL['s']} -eq 0 ]; then + if [ "${OPT_VAL['s']}" -eq 0 ]; then return 0 fi @@ -1063,15 +1134,31 @@ set_alsa_settings() reset_sof_volume() { # set all PGA* volume to 0dB - amixer -Dhw:0 scontrols | sed -e "s/^.*'\(.*\)'.*/\1/" |grep -E 'PGA|gain' | - while read -r mixer_name - do - if is_ipc4; then - amixer -Dhw:0 -- sset "$mixer_name" 100% - else - amixer -Dhw:0 -- sset "$mixer_name" 0dB - fi - done + if [[ "$SOF_ALSA_TOOL" = "alsa" ]]; then + amixer -Dhw:0 scontrols | sed -e "s/^.*'\(.*\)'.*/\1/" |grep -E 'PGA|gain' | + + while read -r mixer_name + do + if is_ipc4; then + amixer -Dhw:0 -- sset "$mixer_name" 100% + else + amixer -Dhw:0 -- sset "$mixer_name" 0dB + fi + done + elif [[ "$SOF_ALSA_TOOL" = "tinyalsa" ]]; then + tinymix -D0 controls | sed -e "s/^.*'\(.*\)'.*/\1/" |grep -E 'PGA|gain' | + + while read -r mixer_name + do + if is_ipc4; then + tinymix -D0 set "$mixer_name" 100% + else + tinymix -D0 set "$mixer_name" 0dB + fi + done + else + echo "Unknown alsa tool $SOF_ALSA_TOOL" + fi } DO_PERF_ANALYSIS=0 diff --git a/env-check.sh b/env-check.sh index ba98e372..2b2a75eb 100755 --- a/env-check.sh +++ b/env-check.sh @@ -83,6 +83,9 @@ out_str="" check_res=0 printf "Checking for some OS packages:\t\t" func_check_pkg expect func_check_pkg aplay +func_check_pkg sox +func_check_pkg tinycap +func_check_pkg tinyplay func_check_pkg python3 # jq is command-line json parser func_check_pkg jq From 03afd54991da384072b805f10c212e682907a654 Mon Sep 17 00:00:00 2001 From: Arkadiusz Cholewinski Date: Thu, 8 May 2025 09:21:08 +0200 Subject: [PATCH 2/3] TinyALSA:Refactor check-capture and check-playback Refactor Check-capture and Check-playback tests. Signed-off-by: Arkadiusz Cholewinski --- test-case/check-capture.sh | 25 ++++++++++--------------- test-case/check-playback.sh | 17 ++++++----------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/test-case/check-capture.sh b/test-case/check-capture.sh index 6ead37f0..444e5d4c 100755 --- a/test-case/check-capture.sh +++ b/test-case/check-capture.sh @@ -64,25 +64,20 @@ setup_kernel_check_point func_lib_check_sudo func_pipeline_export "$tplg" "type:capture & ${OPT_VAL['S']}" -for round in $(seq 1 $round_cnt) +for round in $(seq 1 "$round_cnt") do for idx in $(seq 0 $((PIPELINE_COUNT - 1))) do - channel=$(func_pipeline_parse_value "$idx" channel) - rate=$(func_pipeline_parse_value "$idx" rate) - fmt=$(func_pipeline_parse_value "$idx" fmt) - dev=$(func_pipeline_parse_value "$idx" dev) - pcm=$(func_pipeline_parse_value "$idx" pcm) - type=$(func_pipeline_parse_value "$idx" type) - snd=$(func_pipeline_parse_value "$idx" snd) - - if [ ${OPT_VAL['F']} = '1' ]; then - fmt=$(func_pipeline_parse_value "$idx" fmts) + + initialize_audio_params "$idx" + + if [ "${OPT_VAL['F']}" = '1' ]; then + fmts=$(func_pipeline_parse_value "$idx" fmts) fi - for fmt_elem in $fmt + for fmt_elem in $fmts do - for i in $(seq 1 $loop_cnt) + for i in $(seq 1 "$loop_cnt") do dlogi "===== Testing: (Round: $round/$round_cnt) (PCM: $pcm [$dev]<$type>) (Loop: $i/$loop_cnt) =====" # get the output file @@ -90,12 +85,12 @@ do dlogi "no file prefix, use /dev/null as dummy capture output" file=/dev/null else - mkdir -p $out_dir + mkdir -p "$out_dir" file=$out_dir/${file_prefix}_${dev}_${i}.wav dlogi "using $file as capture output" fi - if ! arecord_opts -D"$dev" -r "$rate" -c "$channel" -f "$fmt_elem" -d $duration "$file" -v -q; + if ! arecord_opts -D"$dev" -r "$rate" -c "$channel" -f "$fmt_elem" -d "$duration" "$file" -v -q; then func_lib_lsof_error_dump "$snd" die "arecord on PCM $dev failed at $i/$loop_cnt." diff --git a/test-case/check-playback.sh b/test-case/check-playback.sh index 5da20850..0958647b 100755 --- a/test-case/check-playback.sh +++ b/test-case/check-playback.sh @@ -71,25 +71,20 @@ setup_kernel_check_point func_lib_check_sudo func_pipeline_export "$tplg" "type:playback & ${OPT_VAL['S']}" -for round in $(seq 1 $round_cnt) +for round in $(seq 1 "$round_cnt") do for idx in $(seq 0 $((PIPELINE_COUNT - 1))) do - channel=$(func_pipeline_parse_value "$idx" channel) - rate=$(func_pipeline_parse_value "$idx" rate) - fmts=$(func_pipeline_parse_value "$idx" fmt) - dev=$(func_pipeline_parse_value "$idx" dev) - pcm=$(func_pipeline_parse_value "$idx" pcm) - type=$(func_pipeline_parse_value "$idx" type) - snd=$(func_pipeline_parse_value "$idx" snd) - - if [ ${OPT_VAL['F']} = '1' ]; then + + initialize_audio_params "$idx" + + if [ "${OPT_VAL['F']}" = '1' ]; then fmts=$(func_pipeline_parse_value "$idx" fmts) fi for fmt_elem in $fmts do - for i in $(seq 1 $loop_cnt) + for i in $(seq 1 "$loop_cnt") do dlogi "===== Testing: (Round: $round/$round_cnt) (PCM: $pcm [$dev]<$type>) (Loop: $i/$loop_cnt) =====" aplay_opts -D"$dev" -r "$rate" -c "$channel" -f "$fmt_elem" \ From 8c585a36e391f84e57a4202bcd99dbc1fb715654 Mon Sep 17 00:00:00 2001 From: Arkadiusz Cholewinski Date: Thu, 8 May 2025 09:22:35 +0200 Subject: [PATCH 3/3] TinyALSA: Add wrapper for tinyALSA's tests Add tinyalsa-wrapper used to run any test with tinyAlsa testsets. Signed-off-by: Arkadiusz Cholewinski --- test-case/tinyalsa-wrapper.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100755 test-case/tinyalsa-wrapper.sh diff --git a/test-case/tinyalsa-wrapper.sh b/test-case/tinyalsa-wrapper.sh new file mode 100755 index 00000000..57d62099 --- /dev/null +++ b/test-case/tinyalsa-wrapper.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +## +## Case Name: Wrapper to run a test case given with TinyALSA +## Preconditions: +## TinyALSA and SoX are installed. +## Description: +## This script serves as a wrapper to execute a test case script using TinyALSA. +## It expects the test case script file name (without path) as the first parameter, +## followed by other parameters required for that test case. +## Case step: +## 1. SOF_ALSA_TOOL environment variable is set to TinyALSA +## 2. The test case script is executed. +## Expect result: +## The test case script is executed using TinyALSA +## + +set -e + +# Ensure the test case script file name is provided +if [ -z "$1" ]; then + echo "Error: No test case script file name provided. Exiting..." + exit 1 +fi + +export SOF_ALSA_TOOL=tinyalsa + +TESTDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) + +# shellcheck disable=SC2145 +[ -x "$TESTDIR/test-case/$(basename "$1")" ] && exec "$TESTDIR"/test-case/"$@"