-
-
Save wyf9/ff12240ae023da0a068f2466968e3681 to your computer and use it in GitHub Desktop.
Virtual Sink/Mic Script for PipeWire
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| LOCKDIR="/tmp/viraudio" | |
| mkdir -p "$LOCKDIR" | |
| command -v pactl >/dev/null || { echo "Missing pactl, please install it first." >&2; exit 1; } | |
| usage() { | |
| cat <<EOF | |
| Usage: | |
| $0 sink [NAME] | |
| $0 mic [NAME] | |
| $0 del <NAME> | |
| $0 rm <NAME> | |
| $0 rm --all | |
| $0 rm --all --sink | |
| $0 rm --all --mic | |
| Source: https://gist.github.com/wyf9/ff12240ae023da0a068f2466968e3681 | |
| Help: https://wyf9.top/p/virtual-sink#usage | |
| EOF | |
| exit 1 | |
| } | |
| die() { | |
| echo "$*" >&2 | |
| exit 1 | |
| } | |
| lock_file() { | |
| local type="$1" | |
| local name="$2" | |
| printf '%s/viraudio-%s-%s-%s.lock' "$LOCKDIR" "$USER" "$name" "$type" | |
| } | |
| exists_name() { | |
| local name="$1" | |
| [[ -f "$(lock_file sink "$name")" || -f "$(lock_file mic "$name")" ]] | |
| } | |
| generate_name() { | |
| local type="$1" | |
| local prefix | |
| case "$type" in | |
| sink) prefix="Virtual Sink" ;; | |
| mic) prefix="Virtual Mic" ;; | |
| esac | |
| local i=1 | |
| while exists_name "$prefix $i"; do | |
| ((i++)) | |
| done | |
| echo "$prefix $i" | |
| } | |
| create_sink() { | |
| local name="${1:-}" | |
| if [[ -z "$name" ]]; then | |
| name=$(generate_name sink) | |
| fi | |
| if exists_name "$name"; then | |
| die "Virtual device '$name' already exists." | |
| fi | |
| local module_index | |
| module_index=$( | |
| pactl load-module module-null-sink \ | |
| media.class=Audio/Sink \ | |
| sink_name="$name" \ | |
| sink_properties=device.description="$name" | |
| ) | |
| printf '%s\n' "$module_index" > "$(lock_file sink "$name")" | |
| echo "Created virtual sink '$name' (module $module_index)." | |
| } | |
| create_mic() { | |
| local name="${1:-}" | |
| if [[ -z "$name" ]]; then | |
| name=$(generate_name mic) | |
| fi | |
| if exists_name "$name"; then | |
| die "Virtual device '$name' already exists." | |
| fi | |
| local module_index | |
| module_index=$( | |
| pactl load-module module-null-sink \ | |
| media.class=Audio/Source/Virtual \ | |
| sink_name="$name" \ | |
| sink_properties=device.description="$name" | |
| ) | |
| printf '%s\n' "$module_index" > "$(lock_file mic "$name")" | |
| echo "Created virtual mic '$name' (module $module_index)." | |
| } | |
| remove_name() { | |
| local name="$1" | |
| local removed=0 | |
| for type in sink mic; do | |
| local lf | |
| lf="$(lock_file "$type" "$name")" | |
| if [[ -f "$lf" ]]; then | |
| local module_index | |
| module_index="$(<"$lf")" | |
| pactl unload-module "$module_index" | |
| rm -f "$lf" | |
| echo "Removed virtual $type '$name' (module $module_index)." | |
| removed=1 | |
| fi | |
| done | |
| if [[ $removed -eq 0 ]]; then | |
| die "No virtual sink or mic named '$name' found." | |
| fi | |
| } | |
| remove_all() { | |
| local type_filter="${1:-}" | |
| local types=() | |
| local removed=0 | |
| if [[ -z "$type_filter" ]]; then | |
| types=(sink mic) | |
| else | |
| types=("$type_filter") | |
| fi | |
| for type in "${types[@]}"; do | |
| for lf in "$LOCKDIR"/viraudio-*"-$type".lock; do | |
| if [[ -f "$lf" ]]; then | |
| local module_index | |
| module_index="$(<"$lf")" | |
| local name | |
| name=$(basename "$lf" "-$type.lock" | sed "s/^viraudio-[^-]*-//") | |
| pactl unload-module "$module_index" | |
| rm -f "$lf" | |
| echo "Removed virtual $type '$name' (module $module_index)." | |
| removed=$((removed + 1)) | |
| fi | |
| done | |
| done | |
| if [[ $removed -eq 0 ]]; then | |
| die "No virtual devices found." | |
| fi | |
| echo "Total removed: $removed." | |
| } | |
| if [[ $# -lt 1 ]]; then | |
| usage | |
| fi | |
| cmd="$1" | |
| shift | |
| name="$*" | |
| case "$cmd" in | |
| sink) | |
| create_sink "$name" | |
| ;; | |
| mic) | |
| create_mic "$name" | |
| ;; | |
| del|rm) | |
| if [[ "$name" == "--all" ]]; then | |
| remove_all | |
| elif [[ "$name" == "--all --sink" ]] || [[ "$name" == "--sink --all" ]]; then | |
| remove_all sink | |
| elif [[ "$name" == "--all --mic" ]] || [[ "$name" == "--mic --all" ]]; then | |
| remove_all mic | |
| elif [[ -z "$name" ]]; then | |
| die "NAME required for del/rm, or use --all." | |
| else | |
| remove_name "$name" | |
| fi | |
| ;; | |
| *) | |
| usage | |
| ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment