#!/usr/bin/env bash # rofi-audio-switch: pick a PulseAudio/PipeWire sink via rofi set -euo pipefail # Get list of sinks: index, name, description mapfile -t sinks < <(pactl list sinks | awk -F': ' ' /^Sink #/ { idx=$2 } /Name:/ { name=$2 } /Description:/ { desc=$2; print idx"\t"name"\t"desc } ') if [ "${#sinks[@]}" -eq 0 ]; then notify-send "Audio Switch" "No sinks found" exit 1 fi current_sink=$(pactl get-default-sink) # Build rofi menu: show description, mark current with a star menu="" for line in "${sinks[@]}"; do name=$(echo "$line" | cut -f2) desc=$(echo "$line" | cut -f3) if [ "$name" = "$current_sink" ]; then menu+="* $desc\n" else menu+=" $desc\n" fi done chosen=$(echo -e "$menu" | rofi -dmenu -i -p "Audio Output" -markup-rows \ -theme-str 'listview { lines: 3; }' \ -theme-str 'window { height: 220px; }') [ -z "$chosen" ] && exit 0 # Strip the leading marker to match description text chosen_desc=$(echo "$chosen" | sed 's/^[* ] //') # Find matching sink name for that description target_name="" for line in "${sinks[@]}"; do desc=$(echo "$line" | cut -f3) if [ "$desc" = "$chosen_desc" ]; then target_name=$(echo "$line" | cut -f2) break fi done if [ -z "$target_name" ]; then notify-send "Audio Switch" "Could not resolve selection" exit 1 fi # Set as default sink pactl set-default-sink "$target_name" # Move all currently running streams to the new sink mapfile -t inputs < <(pactl list short sink-inputs | cut -f1) for input in "${inputs[@]}"; do pactl move-sink-input "$input" "$target_name" done notify-send "Audio Switch" "Switched to: $chosen_desc"