Skip to content

services/pipewire: meter nodes whose channels have no position - #1147

Open
GrantAbell wants to merge 1 commit into
quickshell-mirror:masterfrom
GrantAbell:pipewire-peak-unpositioned-channels
Open

GrantAbell wants to merge 1 commit into
quickshell-mirror:masterfrom
GrantAbell:pipewire-peak-unpositioned-channels

Conversation

@GrantAbell

Copy link
Copy Markdown

Problem

  1. PwPeakStream::start() connects a capture stream to the node asking only
    for F32; it does not request channel positions.
  2. PipeWire negotiates a default stereo layout for the stream: FL, FR.
  3. handleProcess() wants to divide each stream channel's peak by that
    channel's volume, so the meter shows the pre-fader level. To find the
    volume it looks each stream position (FL, FR) up in the node's channel
    list (audioData->channels()).
  4. A node on a pro-audio profile has channels AUX0, AUX1, ... (no speaker
    positions). Nothing matches, volumes.length() != channelCount, and the
    function logged qCCritical and returned. No peak was ever published, and
    the log line repeated for every buffer.

Changes

PipeWire's channel mixer, spa/plugins/audioconvert/channelmix-ops.c,
make_matrix(): when either side has no known positions
(src_mask == 0 || dst_mask == 0) and both sides have more than one channel,
it calls pair_mix(), which is the identity matrix: input channel i goes to
output channel i. So stream channel i carries node channel i's audio, and node
volume i is the right divisor. AUX positions (>= 0x1000) never set a mask bit,
which is what makes the mask zero. If the node is mono it is distributed to
every stream channel, and if the stream is mono it gets the average of the
node's channels; the mean volume is the right compensation there, hence the
second branch.

  • handleProcess(): on a mismatch, use the node's volumes by index when the
    counts agree, otherwise the mean volume; then carry on and publish the peak.
  • Log the mismatch once per format negotiation, at debug level, instead of a
    critical line per buffer. warnedChannelMismatch is reset in resetFormat().
  • changelog/next.md entry under Bug Fixes.

Verification

Fix verified via pw-loopback source pinned to audio.position=[AUX0 AUX1] with a clip
played into it. Stock /usr/bin/quickshell 0.3.1: 199 "missing channels"
errors in 5 s, peak stays 0. This build: 0 errors, peaks reported, with the
node still on [4096, 4097] and the stream on [3, 4] (FL, FR), so the index
fallback is what did the work.

PwPeakStream matched each capture stream channel to the node's channel
list by position to look up its volume, and dropped the buffer when any
lookup failed. A node without speaker positions, such as an ALSA device
on a pro-audio profile (AUX0, AUX1, ...), never matches the FL/FR layout
the stream negotiates, so PwNodePeakMonitor.peak stayed at zero for it
and a critical error was logged for every buffer.

Pipewire's channelmix has no positions to mix by in that case and pairs
channels by index (pair_mix in channelmix-ops.c), so match volumes by
index too when the counts agree, and use the mean volume when they do
not. Log the mismatch once, at debug level.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant