Skip to content

Follow system default audio output - #2

Open
Zezenta wants to merge 1 commit into
omacom:masterfrom
Zezenta:fix/follow-default-audio-output
Open

Zezenta wants to merge 1 commit into
omacom:masterfrom
Zezenta:fix/follow-default-audio-output

Conversation

@Zezenta

@Zezenta Zezenta commented Aug 22, 2026

Copy link
Copy Markdown

Summary

  • Bind the preview AudioOutput to Qt Multimedia's current default audio device.
  • Follow runtime output changes between headphones, speakers, and monitor audio.

Previously, OMACut selected the default output when the AudioOutput was created and kept playing through that original sink after the system default changed.

Testing

  • ./bin/build
  • ./bin/test — 27 passed, 0 failed
  • Manual PipeWire test confirmed the OMACut stream moved from the analog/headphone sink to the HDMI monitor sink after switching the default output; the original default was restored afterward.

@greptile-apps

greptile-apps Bot commented Aug 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR makes preview audio follow the system’s current default output device.

  • Adds a MediaDevices instance to observe the default audio output.
  • Binds the dynamically created AudioOutput.device to MediaDevices.defaultAudioOutput.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete blocking or non-blocking defects identified.

The declarative device binding remains active for the lifetime of the dynamically created AudioOutput, and no changed-code path was found that overrides or breaks it.

Important Files Changed

Filename Overview
src/Main.qml Adds a persistent MediaDevices observer and a live device binding so preview playback follows runtime default-output changes.

Reviews (1): Last reviewed commit: "Follow system default audio output" | Re-trigger Greptile

@omarchybot

Copy link
Copy Markdown
Collaborator

Reviewed by Claude Opus 5 in Claude Code, with an independent second opinion from Codex at xhigh reasoning. Nothing found: the change does what it says, and the two ways a device binding can go wrong were measured rather than assumed.

Everything below ran on a disposable Omarchy VM with Qt 6.11.2 and PipeWire, against a build of master as the control, using two module-null-sink outputs so the default could be switched underneath a running player. ./bin/test on this branch: 27 passed, 0 failed, including qmlDoesNotCreateAudioOutputWithoutVideo.

It follows the default, and master does not. Running the built omacut on a clip and switching with pactl set-default-sink probeB mid-playback: on this branch the PipeWire stream moved to probeB and probeA went idle; on master the stream stayed on probeA and probeB was never touched. Qt 6.11's PipeWire backend does notice a default-only change, so the binding tracks it without any device being added or removed — worth confirming, since defaultAudioOutput is declared with audioOutputsChanged as its notify signal and that name suggests it only fires for the list.

Re-binding mid-playback costs no state. Sampling the live AudioOutput every 500 ms across the switch: muted stayed false, volume stayed 1, playbackState stayed PlayingState, and position kept advancing (4458 → 5034 → 5482 ms). muted: player.priming is a separate binding on the same object and the object is not recreated, so the priming guard is untouched. Qt does tear down and rebuild the audio stream for the new device — the sink-input index changes — so a brief gap at the moment of the switch is possible; nothing pauses or restarts.

Startup with no device does not silence playback. Logging the device at the instant of createObject in ensureAudioOutput, it was already the valid default, so there is no window in which the binding hands the output a null device. With no audio outputs at all (Qt ignores PipeWire's auto_null dummy) this branch and master behave identically: empty device, no stream, playback still advances. And when a sink appears later, this branch attaches to it and plays through it, where master stays silent for the rest of the session.

The lazy audio output from 6b360ee still holds. On the empty start screen both builds sit at 15 threads with no PipeWire file descriptor and no client registered with the server, so the always-present MediaDevices does not re-introduce the cost that commit removed: the platform provider is only started once something connects to a device-change signal, which here is the binding inside the Component, created with the video.

Codex reached the same verdict, and where it agreed with a measurement already taken its independence is not currently guaranteed — it can read the reviewing session's own transcript. What it added stands on its own, from the Qt 6.11.2 sources: QAudioOutput::setDevice re-queries QMediaDevices::defaultAudioOutput() when handed a null device and returns without assigning if that is null too, which is exactly why the output kept its last device when every sink was removed; Qt's PipeWire context manager watches the default.audio.sink metadata and the device monitor re-emits the output list with updated default flags, which is the mechanism behind the tracking measured above; and the FFmpeg renderer rebuilds its QAudioSink on deviceChanged, re-applying the current mute and volume, so a switch landing inside the 250 ms priming window would not leak priming audio. That last case was not reproduced here — it rests on the source, not on a measurement.

Waiting on the maintainer.

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.

2 participants