Linking Audio Input and Output from Applications with Pipewire pw-link

If you are recording audio output from an application, one challenge is that recording the default output can mean you record all the sounds that are produced by all applications. In Linux, you can use pipewire command-line utility pw-link to create a dedicated and isolated link between the application producing the audio output and the application that will be recording the audio output. Below is a bash script that does this – the command-line flags -o and -i imply the only links for the output and input applications should be to each other (exclusive mode). Note that this script makes a few assumptions (outside of assumptions about the format of the output from pw-link itself but I consider that to be not as problematic) – it assumes the application names do not contain _ – a more robust version could probably split the port name on ‘:’ and use the second field on split as channel.

#!/bin/bash

output_exclusive=false
input_exclusive=false
output_application_name="Amazon Music"
input_application_name="OBS"

while getopts "oi" opt; do
    case $opt in
        o)
            output_exclusive=true
            ;;
        i)
            input_exclusive=true
            ;;
        \?)
            echo "Invalid Option: -$OPTARG" >&2
            exit 1
            ;;
    esac
done
shift $((OPTIND - 1))

declare -A deleted_links
node_matched=false

while IFS= read -r line1; do
    if [[ ("$line1" =~ "<" || "$line1" =~ ">") && "$node_matched" = true ]]; then
        link_id=$(echo $line1 | cut -d ' ' -f 1)
        if [[ -z "${deleted_links[$link_id]}" ]]; then
            deleted_links["$link_id"]=1
            echo pw-link -d "$link_id"
            pw-link -d "$link_id"
        fi
    else
        node_matched=false
        if [[ "$output_exclusive" = true && "$line1" =~ $output_application_name ]]; then
            node_matched=true
        elif [[ "$input_exclusive" = true && "$line1" =~ $input_application_name ]]; then
            node_matched=true
        fi
    fi
done < <(pw-link -l -I)


declare -a output_ports
declare -a input_ports

while IFS= read -r line1; do
    if [[ "$line1" =~ $output_application_name ]]; then
        output_ports+=("$line1")
    fi
done < <(pw-link -o)



while IFS= read -r line1; do
    if [[ "$line1" =~ $input_application_name ]]; then
        input_ports+=("$line1")
    fi
done < <(pw-link -i)


for ((i = 0; i < ${#output_ports[@]}; i++)); do
    for ((j = 0; j < ${#input_ports[@]}; j++)); do
        output_channel=$(echo "${output_ports[i]}" | cut -d '_' -f 2)
        input_channel=$(echo "${input_ports[j]}" | cut -d '_' -f 2)

        if [[ "$output_channel" == "$input_channel" ]]; then
            echo pw-link "${output_ports[i]}" "${input_ports[j]}"
            pw-link "${output_ports[i]}" "${input_ports[j]}"
        fi
    done
done

Leave a Reply

Your email address will not be published. Required fields are marked *