#!/bin/sh if [ "$1" = "-h" ]; then echo "usage: pwloop SOURCE TARGET [VOLUME]" >&2 exit 1 fi source_name=$1 target_name=$2 volume=0.2 [ -n "$3" ] && volume=$3 # spawn pwloopback pw-loopback \ -m '[[FL FR]]' \ -g 'pwloop' \ --capture-props='media.class=Audio/Sink/Virtual audio.position=[FL FR] node.description="pwloop sink" node.name=pwloop' \ --playback-props='media.class=Audio/Source/Virtual audio.position=[FL FR] node.description="pwloop source" node.name=pwloop' \ & # save pid for later pwloop_pid=$! trap '{ kill $pwloop_pid; }' EXIT set -x # set monitor volume prop pw-cli set-param "$pwloop_oid" 2 "{ \"monitorVolumes\": [$volume, $volume] }" # connect ports to pwloop pw-link "$(pw-link -o | grep "$source_name" | grep 'FL$')" pwloop:input_FL pw-link "$(pw-link -o | grep "$source_name" | grep 'FR$')" pwloop:input_FR # connect ports from pwloop pw-link pwloop:capture_FL "$(pw-link -i | grep "$target_name" | grep -v monitor | grep 'FL$')" pw-link pwloop:capture_FR "$(pw-link -i | grep "$target_name" | grep -v monitor | grep 'FR$')" # get object.id pwloop_oid=$(pw-dump | jq -r --arg name "pwloop" --arg s "Audio/Sink/Virtual" \ '.[]|select(.type=="PipeWire:Interface:Node" and .info.props["node.name"]==$name and .info.props["media.class"]==$s)|.info.props["object.id"]') wait