Skip to content

Instantly share code, notes, and snippets.

@darkseed
Created December 19, 2025 18:07
Show Gist options
  • Select an option

  • Save darkseed/c0b5234bec02978b149a54ba000ddb25 to your computer and use it in GitHub Desktop.

Select an option

Save darkseed/c0b5234bec02978b149a54ba000ddb25 to your computer and use it in GitHub Desktop.
Reachy Mini Audio diag script
#!/usr/bin/env bash
# audio_diag_focus.sh — Focused audio diag for Reachy/Pollen/ReSpeaker class devices
# Usage:
# ./audio_diag_focus.sh
# PATTERNS='Reachy|Pollen|reSpeaker' ./audio_diag_focus.sh
set -euo pipefail
PATTERNS="${PATTERNS:-Reachy|Pollen|reSpeaker}"
# Case-insensitive grep helper
igrep() { grep -Ei "$PATTERNS" || true; }
have() { command -v "$1" >/dev/null 2>&1; }
hr() { printf '%*s\n' "${1:-92}" '' | tr ' ' '-'; }
title(){ echo; hr; echo "$1"; hr; }
kv(){ printf "%-30s %s\n" "$1" "$2"; }
# ---------- PipeWire (wpctl) ----------
pw_block() {
title "PipeWire (wpctl) — matching devices/nodes: /$PATTERNS/i"
if ! have wpctl; then
kv "wpctl" "not found"
return
fi
kv "Default sink volume" "$(wpctl get-volume @DEFAULT_AUDIO_SINK@ 2>/dev/null || echo '<unavailable>')"
kv "Default source volume" "$(wpctl get-volume @DEFAULT_AUDIO_SOURCE@ 2>/dev/null || echo '<unavailable>')"
echo
echo "wpctl status (filtered to matches + surrounding headers):"
# Print only Audio section and filter to matching lines plus structure lines.
wpctl status 2>/dev/null \
| awk '
/^Audio$/ {in_audio=1}
/^Video$/ {in_audio=0}
{ if(in_audio) print }
' \
| awk -v pat="$PATTERNS" '
BEGIN{IGNORECASE=1}
# always keep structural lines
/^[A-Za-z]/ || /^[[:space:]]*├─/ || /^[[:space:]]*└─/ || /^[[:space:]]*\*/ { print; next }
# keep only matching lines
$0 ~ pat { print }
'
echo
echo "Quick list (matching sinks/sources with IDs):"
wpctl status 2>/dev/null \
| awk '
/^Audio$/ {in_audio=1}
/^Video$/ {in_audio=0}
{ if(in_audio) print }
' \
| grep -E "Sinks:|Sources:|^\s*\*?\s*[0-9]+\." \
| igrep || true
}
# ---------- Pulse (pactl) ----------
pulse_block() {
title "PulseAudio compat (pactl via PipeWire) — matching sinks/sources: /$PATTERNS/i"
if ! have pactl; then
kv "pactl" "not found (pulseaudio-utils)"
return
fi
local def_sink def_source
def_sink="$(pactl info 2>/dev/null | awk -F': ' '/Default Sink:/{print $2}')"
def_source="$(pactl info 2>/dev/null | awk -F': ' '/Default Source:/{print $2}')"
kv "Default sink" "${def_sink:-<unavailable>}"
kv "Default source" "${def_source:-<unavailable>}"
echo
echo "Matching sinks (name | state):"
pactl list short sinks 2>/dev/null | igrep | awk '{print $2 " | " $NF}' || true
while read -r id name _; do
[ -z "${name:-}" ] && continue
echo
hr
echo "Sink: $name"
pactl get-sink-volume "$name" 2>/dev/null | head -n1 || true
pactl get-sink-mute "$name" 2>/dev/null | head -n1 || true
done < <(pactl list short sinks 2>/dev/null | igrep | awk '{print $1, $2, $3}')
echo
echo "Matching sources (name | state):"
pactl list short sources 2>/dev/null | igrep | awk '{print $2 " | " $NF}' || true
while read -r id name _; do
[ -z "${name:-}" ] && continue
echo
hr
echo "Source: $name"
pactl get-source-volume "$name" 2>/dev/null | head -n1 || true
pactl get-source-mute "$name" 2>/dev/null | head -n1 || true
done < <(pactl list short sources 2>/dev/null | igrep | awk '{print $1, $2, $3}')
}
# ---------- ALSA (amixer) ----------
alsa_block() {
title "ALSA (amixer) — matching cards: /$PATTERNS/i"
if ! have aplay || ! have arecord || ! have amixer; then
kv "alsa-utils" "missing (need aplay/arecord/amixer)"
return
fi
echo "Matching playback devices (aplay -l):"
aplay -l 2>/dev/null | igrep || echo "<none>"
echo
echo "Matching capture devices (arecord -l):"
arecord -l 2>/dev/null | igrep || echo "<none>"
# Collect cards that appear in either aplay -l or arecord -l and match PATTERNS
local cards
cards="$(
{ aplay -l 2>/dev/null; arecord -l 2>/dev/null; } \
| awk -v pat="$PATTERNS" 'BEGIN{IGNORECASE=1}
/^card [0-9]+:/ {line=$0}
{ if($0 ~ /^card [0-9]+:/) line=$0 }
$0 ~ pat && $0 ~ /^card [0-9]+:/ { print $2 }
' \
| sed 's/://g' \
| sort -nu \
| tr '\n' ' '
)"
if [ -z "${cards// }" ]; then
echo "No ALSA cards matched."
return
fi
for c in $cards; do
echo
hr
echo "Card $c info:"
amixer -c "$c" info 2>/dev/null | sed -n '1,8p' || true
# Playback controls to try in order (USB mixers vary)
echo
echo "Playback mixer snapshot:"
local found_play=0
for ctl in "PCM" "PCM,0" "PCM,1" "Master" "Speaker" "Headphone"; do
if amixer -c "$c" sget "$ctl" >/dev/null 2>&1; then
echo "Control: $ctl"
amixer -c "$c" sget "$ctl" 2>/dev/null | sed -n '1,14p'
found_play=1
break
fi
done
[ "$found_play" -eq 0 ] && echo "<no common playback control found>"
echo
echo "Capture mixer snapshot:"
local found_cap=0
for ctl in "Capture" "Mic" "Microphone" "ADC" "Input"; do
if amixer -c "$c" sget "$ctl" >/dev/null 2>&1; then
echo "Control: $ctl"
amixer -c "$c" sget "$ctl" 2>/dev/null | sed -n '1,14p'
found_cap=1
break
fi
done
[ "$found_cap" -eq 0 ] && echo "<no common capture control found>"
done
}
main() {
echo "audio_diag_focus.sh — $(date)"
echo "Patterns: /$PATTERNS/i"
pw_block
pulse_block
alsa_block
echo
hr
echo "Tip: If output is quiet, ALSA 'PCM' (amixer) is often the real limiter even when wpctl/pactl show 100%."
hr
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment