Upload files to "/"
depends on dialog
This commit is contained in:
parent
b07db5f297
commit
1831473306
1 changed files with 629 additions and 0 deletions
629
audio.sh
Normal file
629
audio.sh
Normal file
|
|
@ -0,0 +1,629 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_DIR="$HOME/.config/wireplumber/wireplumber.conf.d"
|
||||
|
||||
declare -a DEVICES
|
||||
declare -a DEVICE_STREAMS
|
||||
declare -a DEVICE_CARDS
|
||||
declare -a DEVICE_FORMATS
|
||||
declare -a DEVICE_RATES
|
||||
|
||||
SELECTED_CUSTOM_NAME=""
|
||||
|
||||
cleanup() {
|
||||
clear
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
die() {
|
||||
dialog \
|
||||
--title "Error" \
|
||||
--msgbox "$1" \
|
||||
10 70
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Format sample rate for display
|
||||
# ------------------------------------------------------------
|
||||
|
||||
rate_display() {
|
||||
case "$1" in
|
||||
44100) echo "44.1kHz" ;;
|
||||
48000) echo "48kHz" ;;
|
||||
88200) echo "88.2kHz" ;;
|
||||
96000) echo "96kHz" ;;
|
||||
176400) echo "176.4kHz" ;;
|
||||
192000) echo "192kHz" ;;
|
||||
352800) echo "352.8kHz" ;;
|
||||
384000) echo "384kHz" ;;
|
||||
*) echo "${1}Hz" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Generate a clean filename from the device name
|
||||
# ------------------------------------------------------------
|
||||
|
||||
make_config_filename() {
|
||||
local name="$1"
|
||||
|
||||
name="$(
|
||||
printf '%s' "$name" |
|
||||
sed -E 's/[[:space:]]*:[[:space:]]*USB Audio.*$//' |
|
||||
sed -E 's/[[:space:]]+at usb-.*$//' |
|
||||
sed -E 's/^[^[:space:]]+[[:space:]]+//' |
|
||||
tr '[:upper:]' '[:lower:]' |
|
||||
sed -E 's/[^a-z0-9]+/-/g' |
|
||||
sed -E 's/^-+//; s/-+$//'
|
||||
)"
|
||||
|
||||
printf '%s/%s.conf\n' "$CONFIG_DIR" "$name"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Create WirePlumber configuration directory
|
||||
# ------------------------------------------------------------
|
||||
|
||||
create_config_directory() {
|
||||
if ! mkdir -p "$CONFIG_DIR"; then
|
||||
die "Could not create the WirePlumber configuration directory:
|
||||
|
||||
$CONFIG_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Discover ALSA PCM playback devices
|
||||
# ------------------------------------------------------------
|
||||
|
||||
discover_devices() {
|
||||
local stream
|
||||
local card
|
||||
local name
|
||||
local playback
|
||||
local formats
|
||||
local format
|
||||
local rates
|
||||
local rate
|
||||
local display_format
|
||||
|
||||
DEVICES=()
|
||||
DEVICE_STREAMS=()
|
||||
DEVICE_CARDS=()
|
||||
DEVICE_FORMATS=()
|
||||
DEVICE_RATES=()
|
||||
|
||||
for stream in /proc/asound/card*/stream*; do
|
||||
[[ -f "$stream" ]] || continue
|
||||
|
||||
# Only playback devices.
|
||||
grep -q '^Playback:' "$stream" || continue
|
||||
|
||||
card="${stream#/proc/asound/card}"
|
||||
card="${card%%/*}"
|
||||
|
||||
name="$(head -n1 "$stream")"
|
||||
|
||||
# Extract Playback section.
|
||||
playback=$(
|
||||
awk '
|
||||
/^Playback:/ {
|
||||
in_playback=1
|
||||
next
|
||||
}
|
||||
|
||||
/^Capture:/ {
|
||||
in_playback=0
|
||||
}
|
||||
|
||||
in_playback {
|
||||
print
|
||||
}
|
||||
' "$stream"
|
||||
)
|
||||
|
||||
# Supported PCM formats.
|
||||
formats=$(
|
||||
printf '%s\n' "$playback" |
|
||||
grep -E '^[[:space:]]+Format:' |
|
||||
awk '{print $2}' |
|
||||
grep -E '^(S16_LE|S24_3LE|S24_LE|S32_LE)$' |
|
||||
sort -u
|
||||
)
|
||||
|
||||
[[ -n "$formats" ]] || continue
|
||||
|
||||
DEVICES+=("$name")
|
||||
DEVICE_STREAMS+=("$stream")
|
||||
DEVICE_CARDS+=("$card")
|
||||
|
||||
local format_list=""
|
||||
local rate_list=""
|
||||
|
||||
while read -r format; do
|
||||
[[ -n "$format" ]] || continue
|
||||
|
||||
case "$format" in
|
||||
S16_LE)
|
||||
display_format="S16LE"
|
||||
;;
|
||||
S24_3LE|S24_LE)
|
||||
display_format="S24LE"
|
||||
;;
|
||||
S32_LE)
|
||||
display_format="S32LE"
|
||||
;;
|
||||
*)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
format_list+="$display_format"$'\n'
|
||||
|
||||
# Get rates for this specific format.
|
||||
rates=$(
|
||||
printf '%s\n' "$playback" |
|
||||
awk -v fmt="$format" '
|
||||
$1 == "Format:" && $2 == fmt {
|
||||
found=1
|
||||
next
|
||||
}
|
||||
|
||||
found && $1 == "Rates:" {
|
||||
$1=""
|
||||
sub(/^[[:space:]]+/, "")
|
||||
print
|
||||
found=0
|
||||
}
|
||||
' |
|
||||
grep -oE '[0-9]+' |
|
||||
sort -nu
|
||||
)
|
||||
|
||||
while read -r rate; do
|
||||
[[ -n "$rate" ]] || continue
|
||||
rate_list+="$rate"$'\n'
|
||||
done <<< "$rates"
|
||||
|
||||
done <<< "$formats"
|
||||
|
||||
DEVICE_FORMATS+=("$format_list")
|
||||
DEVICE_RATES+=("$rate_list")
|
||||
done
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Get actual PipeWire sink IDs
|
||||
# ------------------------------------------------------------
|
||||
|
||||
get_sink_ids() {
|
||||
wpctl status 2>/dev/null |
|
||||
awk '
|
||||
/Sinks:/ {
|
||||
in_sinks=1
|
||||
next
|
||||
}
|
||||
|
||||
/Sources:/ {
|
||||
in_sinks=0
|
||||
}
|
||||
|
||||
in_sinks {
|
||||
if (match($0, /[0-9]+\./)) {
|
||||
id = substr($0, RSTART, RLENGTH - 1)
|
||||
print id
|
||||
}
|
||||
}
|
||||
' |
|
||||
sort -nu
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Find PipeWire sink by ALSA card number
|
||||
# ------------------------------------------------------------
|
||||
|
||||
find_node_for_device() {
|
||||
local card_number="$1"
|
||||
|
||||
local id
|
||||
local info
|
||||
local node_name
|
||||
|
||||
while read -r id; do
|
||||
[[ "$id" =~ ^[0-9]+$ ]] || continue
|
||||
|
||||
info="$(wpctl inspect "$id" 2>/dev/null)" || continue
|
||||
|
||||
# Make absolutely sure this is an audio sink.
|
||||
grep -q 'media.class = "Audio/Sink"' <<< "$info" || continue
|
||||
|
||||
# Match the exact ALSA card number.
|
||||
if grep -q "alsa.card = \"$card_number\"" <<< "$info"; then
|
||||
|
||||
node_name="$(
|
||||
grep -m1 'node.name = ' <<< "$info" |
|
||||
sed -E 's/.*node.name = "(.*)"/\1/'
|
||||
)"
|
||||
|
||||
if [[ -n "$node_name" ]]; then
|
||||
printf '%s\n' "$node_name"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
done < <(get_sink_ids)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Select device
|
||||
# ------------------------------------------------------------
|
||||
|
||||
select_device() {
|
||||
local args=()
|
||||
local i
|
||||
|
||||
for i in "${!DEVICES[@]}"; do
|
||||
args+=(
|
||||
"$i"
|
||||
"${DEVICES[$i]}"
|
||||
)
|
||||
done
|
||||
|
||||
SELECTED_DEVICE="$(
|
||||
dialog \
|
||||
--title "PipeWire Audio Configurator" \
|
||||
--menu \
|
||||
"Select an audio output device:" \
|
||||
20 100 10 \
|
||||
"${args[@]}" \
|
||||
2>&1 >/dev/tty
|
||||
)" || exit 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Select format
|
||||
# ------------------------------------------------------------
|
||||
|
||||
select_format() {
|
||||
local index="$1"
|
||||
local args=()
|
||||
local format
|
||||
local i=0
|
||||
|
||||
while read -r format; do
|
||||
[[ -n "$format" ]] || continue
|
||||
|
||||
args+=(
|
||||
"$i"
|
||||
"$format"
|
||||
)
|
||||
|
||||
((i++))
|
||||
done <<< "${DEVICE_FORMATS[$index]}"
|
||||
|
||||
local choice
|
||||
|
||||
choice="$(
|
||||
dialog \
|
||||
--title "Select PCM Format" \
|
||||
--menu \
|
||||
"Device:
|
||||
|
||||
${DEVICES[$index]}
|
||||
|
||||
Select format:" \
|
||||
16 70 6 \
|
||||
"${args[@]}" \
|
||||
2>&1 >/dev/tty
|
||||
)" || exit 0
|
||||
|
||||
SELECTED_FORMAT="$(
|
||||
printf '%s\n' "${DEVICE_FORMATS[$index]}" |
|
||||
sed -n "$((choice + 1))p"
|
||||
)"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Select sample rate
|
||||
# ------------------------------------------------------------
|
||||
|
||||
select_rate() {
|
||||
local index="$1"
|
||||
local args=()
|
||||
local rate
|
||||
|
||||
while read -r rate; do
|
||||
[[ -n "$rate" ]] || continue
|
||||
|
||||
args+=(
|
||||
"$rate"
|
||||
"$(rate_display "$rate")"
|
||||
)
|
||||
|
||||
done < <(
|
||||
printf '%s\n' "${DEVICE_RATES[$index]}" |
|
||||
sort -nu
|
||||
)
|
||||
|
||||
SELECTED_RATE="$(
|
||||
dialog \
|
||||
--title "Select Sample Rate" \
|
||||
--menu \
|
||||
"Format: $SELECTED_FORMAT
|
||||
|
||||
Select sample rate:" \
|
||||
20 70 10 \
|
||||
"${args[@]}" \
|
||||
2>&1 >/dev/tty
|
||||
)" || exit 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Select custom device name
|
||||
# ------------------------------------------------------------
|
||||
|
||||
select_custom_name() {
|
||||
local choice
|
||||
|
||||
choice="$(
|
||||
dialog \
|
||||
--title "Device Name" \
|
||||
--menu \
|
||||
"Current device:
|
||||
|
||||
${DEVICES[$SELECTED_DEVICE]}
|
||||
|
||||
Would you like to give this device a custom name?" \
|
||||
14 80 2 \
|
||||
"1" "Keep default name" \
|
||||
"2" "Set custom name" \
|
||||
2>&1 >/dev/tty
|
||||
)" || exit 0
|
||||
|
||||
case "$choice" in
|
||||
1)
|
||||
SELECTED_CUSTOM_NAME=""
|
||||
;;
|
||||
|
||||
2)
|
||||
SELECTED_CUSTOM_NAME="$(
|
||||
dialog \
|
||||
--title "Custom Device Name" \
|
||||
--inputbox \
|
||||
"Enter the name that PipeWire should display for this device:
|
||||
|
||||
Current device:
|
||||
${DEVICES[$SELECTED_DEVICE]}" \
|
||||
12 80 \
|
||||
2>&1 >/dev/tty
|
||||
)" || exit 0
|
||||
|
||||
if [[ -z "$SELECTED_CUSTOM_NAME" ]]; then
|
||||
dialog \
|
||||
--title "Invalid Name" \
|
||||
--msgbox \
|
||||
"The custom device name cannot be empty.
|
||||
|
||||
The default device name will be used instead." \
|
||||
10 70
|
||||
|
||||
SELECTED_CUSTOM_NAME=""
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Generate configuration
|
||||
# ------------------------------------------------------------
|
||||
|
||||
generate_config() {
|
||||
local node_name="$1"
|
||||
|
||||
OUTPUT_FILE="$(make_config_filename "${DEVICES[$SELECTED_DEVICE]}")"
|
||||
|
||||
cat > "$OUTPUT_FILE" <<EOF
|
||||
# PipeWire Audio Configurator
|
||||
# Generated configuration
|
||||
#
|
||||
# Device:
|
||||
# ${DEVICES[$SELECTED_DEVICE]}
|
||||
#
|
||||
# ALSA card:
|
||||
# ${DEVICE_CARDS[$SELECTED_DEVICE]}
|
||||
#
|
||||
# PipeWire node:
|
||||
# $node_name
|
||||
#
|
||||
# Format:
|
||||
# $SELECTED_FORMAT
|
||||
#
|
||||
# Sample rate:
|
||||
# $SELECTED_RATE
|
||||
EOF
|
||||
|
||||
if [[ -n "$SELECTED_CUSTOM_NAME" ]]; then
|
||||
cat >> "$OUTPUT_FILE" <<EOF
|
||||
#
|
||||
# Custom device name:
|
||||
# $SELECTED_CUSTOM_NAME
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat >> "$OUTPUT_FILE" <<EOF
|
||||
|
||||
monitor.alsa.rules = [
|
||||
{
|
||||
matches = [
|
||||
{
|
||||
node.name = "$node_name"
|
||||
}
|
||||
]
|
||||
actions = {
|
||||
update-props = {
|
||||
EOF
|
||||
|
||||
if [[ -n "$SELECTED_CUSTOM_NAME" ]]; then
|
||||
printf ' node.description = "%s"\n' "$SELECTED_CUSTOM_NAME" >> "$OUTPUT_FILE"
|
||||
fi
|
||||
|
||||
cat >> "$OUTPUT_FILE" <<EOF
|
||||
audio.format = "$SELECTED_FORMAT"
|
||||
audio.rate = $SELECTED_RATE
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
EOF
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Ask whether to restart PipeWire/WirePlumber
|
||||
# ------------------------------------------------------------
|
||||
|
||||
ask_restart() {
|
||||
if dialog \
|
||||
--title "Apply Configuration" \
|
||||
--yesno \
|
||||
"Configuration has been installed:
|
||||
|
||||
$OUTPUT_FILE
|
||||
|
||||
Would you like to restart PipeWire and WirePlumber now?
|
||||
|
||||
This will immediately apply the new configuration." \
|
||||
12 75
|
||||
then
|
||||
|
||||
if systemctl --user restart pipewire pipewire-pulse wireplumber; then
|
||||
|
||||
dialog \
|
||||
--title "Configuration Applied" \
|
||||
--msgbox \
|
||||
"PipeWire and WirePlumber were restarted successfully.
|
||||
|
||||
The new configuration is now active." \
|
||||
10 70
|
||||
|
||||
else
|
||||
|
||||
dialog \
|
||||
--title "Restart Failed" \
|
||||
--msgbox \
|
||||
"Configuration was installed successfully, but the PipeWire/WirePlumber restart failed.
|
||||
|
||||
You can try manually with:
|
||||
|
||||
systemctl --user restart pipewire pipewire-pulse wireplumber" \
|
||||
14 80
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
dialog \
|
||||
--title "Configuration Installed" \
|
||||
--msgbox \
|
||||
"Configuration was installed successfully.
|
||||
|
||||
It has NOT been applied yet.
|
||||
|
||||
To apply it later, run:
|
||||
|
||||
systemctl --user restart pipewire pipewire-pulse wireplumber" \
|
||||
14 80
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Main
|
||||
# ------------------------------------------------------------
|
||||
|
||||
create_config_directory
|
||||
|
||||
discover_devices
|
||||
|
||||
if (( ${#DEVICES[@]} == 0 )); then
|
||||
die "No PCM playback devices were found."
|
||||
fi
|
||||
|
||||
select_device
|
||||
|
||||
select_format "$SELECTED_DEVICE"
|
||||
|
||||
select_rate "$SELECTED_DEVICE"
|
||||
|
||||
select_custom_name
|
||||
|
||||
CARD_NUMBER="${DEVICE_CARDS[$SELECTED_DEVICE]}"
|
||||
|
||||
NODE_NAME="$(
|
||||
find_node_for_device "$CARD_NUMBER"
|
||||
)"
|
||||
|
||||
if [[ -z "$NODE_NAME" ]]; then
|
||||
|
||||
dialog \
|
||||
--title "PipeWire Node Not Found" \
|
||||
--msgbox \
|
||||
"ALSA device was detected, but its PipeWire Audio/Sink node could not be mapped.
|
||||
|
||||
Device:
|
||||
${DEVICES[$SELECTED_DEVICE]}
|
||||
|
||||
ALSA card:
|
||||
$CARD_NUMBER
|
||||
|
||||
No configuration was written." \
|
||||
14 80
|
||||
|
||||
exit 1
|
||||
fi
|
||||
|
||||
generate_config "$NODE_NAME"
|
||||
|
||||
CUSTOM_NAME_TEXT="Default"
|
||||
|
||||
if [[ -n "$SELECTED_CUSTOM_NAME" ]]; then
|
||||
CUSTOM_NAME_TEXT="$SELECTED_CUSTOM_NAME"
|
||||
fi
|
||||
|
||||
dialog \
|
||||
--title "Configuration Generated" \
|
||||
--msgbox \
|
||||
"Configuration installed successfully.
|
||||
|
||||
Device:
|
||||
${DEVICES[$SELECTED_DEVICE]}
|
||||
|
||||
ALSA card:
|
||||
$CARD_NUMBER
|
||||
|
||||
Format:
|
||||
$SELECTED_FORMAT
|
||||
|
||||
Sample rate:
|
||||
$(rate_display "$SELECTED_RATE")
|
||||
|
||||
Device name:
|
||||
$CUSTOM_NAME_TEXT
|
||||
|
||||
PipeWire node:
|
||||
$NODE_NAME
|
||||
|
||||
Output:
|
||||
$OUTPUT_FILE" \
|
||||
18 80
|
||||
|
||||
ask_restart
|
||||
|
||||
exit 0
|
||||
Loading…
Reference in a new issue