|
#!/bin/sh |
|
SCREEN_NAME=vban_mic |
|
VBAN_SOURCE_NAME=rawmic |
|
VBAN_HOST=10.0.0.1 |
|
VBAN_PORT=6980 |
|
|
|
SINK_NAME=VBAN_Sink |
|
SINK_DESC="VBANSink" |
|
SOURCE_NAME=VBAN_Mic |
|
SOURCE_DESC="VBANMic" |
|
|
|
ensure_pipewire_ready() { |
|
i=0 |
|
while [ $i -lt 5 ]; do |
|
if pw-cli ls Core >/dev/null 2>&1; then return 0; fi |
|
sleep 1 |
|
i=$((i+1)) |
|
done |
|
echo "Warning: PipeWire may not be ready yet." >&2 |
|
} |
|
|
|
ensure_virtual_devices() { |
|
# Create a dedicated null sink (mono, 48k) if not present |
|
if ! pactl list short sinks | awk '{print $2}' | grep -qx "$SINK_NAME"; then |
|
pactl load-module module-null-sink \ |
|
sink_name="$SINK_NAME" \ |
|
sink_properties="device.description=$SINK_DESC" \ |
|
rate=48000 channels=1 >/dev/null |
|
fi |
|
|
|
# Create a mic from the sink's monitor (mono) if not present |
|
if ! pactl list short sources | awk '{print $2}' | grep -qx "$SOURCE_NAME"; then |
|
pactl load-module module-remap-source \ |
|
master="${SINK_NAME}.monitor" \ |
|
source_name="$SOURCE_NAME" \ |
|
source_properties="device.description=$SOURCE_DESC" \ |
|
rate=48000 channels=1 >/dev/null |
|
fi |
|
} |
|
|
|
# Resolve the mono playback port name for our sink (playback_MONO or playback_FL) |
|
_get_sink_playback_port() { |
|
# Look for a port that starts with "<SINK_NAME>:playback_" |
|
pw-link -l 2>/dev/null | awk -v s="$SINK_NAME" ' |
|
$1 ~ "^" s ":playback_" { print $1 }' | head -n1 |
|
} |
|
|
|
link_vban_to_sink() { |
|
# Wait for VBAN to appear |
|
tries=0 |
|
while [ $tries -lt 40 ]; do |
|
if pw-link -l 2>/dev/null | grep -q '^vban:playback_1$'; then |
|
break |
|
fi |
|
sleep 0.25; tries=$((tries+1)) |
|
done |
|
|
|
# Disconnect VBAN from anything else |
|
pw-link -l 2>/dev/null | awk ' |
|
$1=="vban:playback_1" && $2=="|->" { print $3 } |
|
$1=="vban_receptor:playback_1" && $2=="|->" { print $3 } |
|
' | while read -r dest; do |
|
pw-link -d "vban:playback_1" "$dest" 2>/dev/null || true |
|
pw-link -d "vban_receptor:playback_1" "$dest" 2>/dev/null || true |
|
done |
|
|
|
# Link directly to your known mono mic input |
|
TARGET_IN="input.VBAN_Mic:input_MONO" |
|
pw-link "vban:playback_1" "$TARGET_IN" 2>/dev/null || \ |
|
pw-link "vban_receptor:playback_1" "$TARGET_IN" 2>/dev/null || { |
|
echo "Error: failed to link VBAN playback_1 → ${TARGET_IN}" >&2 |
|
return 1 |
|
} |
|
|
|
echo "Linked VBAN → ${TARGET_IN}" |
|
} |
|
|
|
|
|
|
|
start() { |
|
echo "Starting VBAN audio service (JACK via PipeWire)…" |
|
|
|
# Clean any previous VBAN screen |
|
screen -S "$SCREEN_NAME" -X quit 2>/dev/null |
|
|
|
# Make sure PipeWire is up and create persistent devices |
|
ensure_pipewire_ready |
|
ensure_virtual_devices |
|
|
|
# Launch vban_receptor through pw-jack (don’t try to spawn a real jackd) |
|
if ! command -v pw-jack >/dev/null 2>&1; then |
|
echo "Error: pw-jack not found. Install pipewire-jack." >&2 |
|
return 1 |
|
fi |
|
|
|
echo "Launching vban_receptor…" |
|
screen -dmS "$SCREEN_NAME" \ |
|
env JACK_NO_START_SERVER=1 pw-jack \ |
|
vban_receptor -i "$VBAN_HOST" -p "$VBAN_PORT" -s "$VBAN_SOURCE_NAME" -b jack --channels=1 |
|
|
|
# Wire VBAN to the sink, which feeds the persistent mic |
|
link_vban_to_sink |
|
|
|
echo "VBAN service started." |
|
echo "→ Mic available to apps as: $SOURCE_DESC ($SOURCE_NAME)" |
|
} |
|
|
|
stop() { |
|
echo "Stopping VBAN audio service…" |
|
screen -S "$SCREEN_NAME" -X quit |
|
echo "VBAN receiver stopped. (Virtual mic/sink left in place.)" |
|
} |
|
|
|
purge() { |
|
echo "Stopping and removing virtual devices…" |
|
stop |
|
|
|
# Unload remap-source and null-sink modules associated with our names |
|
pactl list short modules | grep -E "module-(remap-source|null-sink)" | \ |
|
grep -E "$SINK_NAME|$SOURCE_NAME" | awk '{print $1}' | while read -r MID; do |
|
pactl unload-module "$MID" |
|
done |
|
echo "Purged VBAN virtual devices." |
|
} |
|
|
|
status() { |
|
printf "%-50s" "Checking VBAN receiver…" |
|
if screen -list 2>/dev/null | grep -q "[.]$SCREEN_NAME"; then |
|
echo "Running" |
|
else |
|
echo "Not running" |
|
fi |
|
|
|
# Show presence of our virtual devices |
|
pactl list short sinks | awk '{print $2}' | grep -qx "$SINK_NAME" && echo "Sink: $SINK_NAME present" || echo "Sink: $SINK_NAME missing" |
|
pactl list short sources | awk '{print $2}' | grep -qx "$SOURCE_NAME" && echo "Source: $SOURCE_NAME present" || echo "Source: $SOURCE_NAME missing" |
|
} |
|
|
|
case "$1" in |
|
start) start ;; |
|
stop) stop ;; |
|
restart) stop; sleep 1; start ;; |
|
status) status ;; |
|
purge) purge ;; |
|
*) |
|
echo "Usage: $0 {start|stop|restart|status|purge}" |
|
;; |
|
esac |