somewhat experimental update for voice chat but it aint making it worse i guess

This commit is contained in:
jenz 2026-06-30 21:53:06 +01:00
parent 0cfde5300c
commit 0f0a732049
2 changed files with 38 additions and 11 deletions

View File

@ -72,15 +72,22 @@ export PULSE_SERVER="unix:${PULSE_RUNTIME_PATH}/native"
# ~800% on 8 cores. A larger quantum gives the audio graph more buffer # ~800% on 8 cores. A larger quantum gives the audio graph more buffer
# headroom to absorb brief scheduling jitter, trading a few ms of latency # headroom to absorb brief scheduling jitter, trading a few ms of latency
# (irrelevant for one-way streaming) for fewer audible underruns/crackling. # (irrelevant for one-way streaming) for fewer audible underruns/crackling.
# Default PipeWire quantum is often 1024; bump to 4096.
pw-metadata -n settings 0 clock.force-quantum 4096 || true pw-metadata -n settings 0 clock.force-quantum 4096 || true
# Virtual microphone — use || true so a pre-existing module doesn't abort # ── Audio sinks ───────────────────────────────────────────────────────────────
# the script (set -e is active). The "Failure: No such entity" in the old # game_output: CSS's own audio output (game sounds, music) — set as CSS's
# logs was module-virtual-source failing; under PipeWire-Pulse we use # default playback sink.
# module-null-sink + module-remap-source which is better supported. # mic_sink: browser mic input ONLY — pacat writes here, nothing else.
pactl load-module module-null-sink sink_name=mic_sink sink_properties=device.description=mic_sink || true #
# Without this split, CSS's own audio output was also landing in mic_sink
# (the only sink that existed), which fed back into virtual_mic — CSS's own
# game audio was drowning out / interfering with the actual browser mic
# signal, so other players never heard a clean voice transmission.
pactl load-module module-null-sink sink_name=game_output sink_properties=device.description=game_output || true
pactl load-module module-null-sink sink_name=mic_sink sink_properties=device.description=mic_sink rate=48000 channels=1 channel_map=mono || true
pactl load-module module-remap-source source_name=virtual_mic master=mic_sink.monitor || true pactl load-module module-remap-source source_name=virtual_mic master=mic_sink.monitor || true
pactl set-default-sink game_output || true
pactl set-default-source virtual_mic || true pactl set-default-source virtual_mic || true
# ── Launch CSS ──────────────────────────────────────────────────────────────── # ── Launch CSS ────────────────────────────────────────────────────────────────
@ -94,5 +101,17 @@ for i in $(seq 1 60); do
done done
sleep 3 sleep 3
# gst_agent.py targets game_output explicitly via pipewiresrc target-object,
# so no extra env var is needed here for game audio capture.
# ── Mic audio: socat → pacat (on-demand, per TCP connection) ─────────────────
# signaling-server.js connects a TCP socket to port 12345 only while the user
# holds PTT. socat spawns a fresh pacat per connection and kills it when the
# connection closes — pacat only runs while actively transmitting, which also
# keeps mic_sink idle (not RUNNING) the rest of the time.
socat TCP-LISTEN:12345,reuseaddr,fork \
EXEC:"pacat --playback --device=mic_sink --format=s16le --rate=48000 --channels=1 --latency-msec=20" &
echo "socat mic listener started on TCP :12345"
# ── Start streaming agent ───────────────────────────────────────────────────── # ── Start streaming agent ─────────────────────────────────────────────────────
python3 ~/streaming-agent/gst_agent.py python3 ~/streaming-agent/gst_agent.py

View File

@ -125,10 +125,18 @@ class CSSStreamAgent:
f'webrtcbin. ' f'webrtcbin. '
# Game audio: pipewiresrc → Opus # Game audio: pipewiresrc → Opus
# pipewiresrc is the native PipeWire GStreamer source (Ubuntu 24+/26). # pipewiresrc with no target follows PipeWire's default *capture*
# It connects to the default audio output monitor via PipeWire directly, # node (the microphone, virtual_mic) — not the default sink's
# avoiding the PulseAudio compatibility layer entirely. # monitor — so without explicit targeting it silently captured
f'pipewiresrc ! ' # the mic into this track instead of game audio.
# game_output is a single PipeWire node with both playback ports
# (what CSS writes to) and monitor ports (the loopback read side)
# on the same node — there's no separate "game_output.monitor"
# node. stream.capture.sink=true tells PipeWire's autoconnect to
# link to that node's monitor/capture-sink ports rather than
# treating it as a regular source.
f'pipewiresrc target-object=game_output '
f'stream-properties="props,stream.capture.sink=(boolean)true" ! '
f'audio/x-raw,format=F32LE,rate=48000,channels=2 ! ' f'audio/x-raw,format=F32LE,rate=48000,channels=2 ! '
f'audioconvert ! ' f'audioconvert ! '
f'opusenc ! ' f'opusenc ! '