Skip to content

Conversation

@fly602
Copy link
Contributor

@fly602 fly602 commented Dec 26, 2025

初始化时会加载null-sink, 概率性触发底层事件,将默认输入输出设置为null-sink.
当输入输出设置为null-sink时,再检查一次端口是否有可用端口,如果有则切换

Log:
PMS: BUG-340327
Influence: audio

Summary by Sourcery

Ensure null-sink audio module is safely initialized and avoid keeping default input/output on null-sink by auto-switching to available ports.

Bug Fixes:

  • Prevent default audio input and output from remaining on null-sink when physical ports are available by triggering automatic port switching.
  • Avoid redundant loading of the null-sink module and handle cases where it is already the default sink or source.

Enhancements:

  • Initialize the null-sink module during audio initialization instead of conditionally during sink refresh for more predictable behavior.

初始化时会加载null-sink, 概率性触发底层事件,将默认输入输出设置为null-sink.
当输入输出设置为null-sink时,再检查一次端口是否有可用端口,如果有则切换

Log:
PMS: BUG-340327
Influence: audio
@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: fly602

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 26, 2025

Reviewer's Guide

This PR changes when and how the PulseAudio null-sink module is loaded and adds null-sink–specific fallback logic for default input/output selection to avoid ending up with null-sink as the active device when real ports are available.

Sequence diagram for audio initialization and null-sink module loading

sequenceDiagram
actor System
participant Audio
participant PulseContext

System->>Audio: init()
Audio->>Audio: connect()
Audio-->>System: error?
System->>Audio: (on success) continue init
Audio->>Audio: LoadNullSinkModule()
Audio->>Audio: isModuleExist(nullSinkModuleName)
alt module not loaded
  Audio->>PulseContext: LoadModule(nullSinkModuleName, "")
else module already loaded
  Audio-->>Audio: skip loading
end
Audio->>Audio: refresh()
Audio-->>System: init complete
Loading

Sequence diagram for default sink update with null-sink fallback and auto-switch

sequenceDiagram
actor System
participant Audio
participant PulseContext

System->>Audio: updateDefaultSink(sinkName)
Audio->>Audio: getPhySinkInfoByName(sinkName)
alt sinkInfo is nil
  Audio->>Audio: setPropDefaultSink("/")
  Audio->>Audio: defaultSink = nil
  alt sinkName contains null-sink
    Audio->>Audio: autoSwitchOutputPort()
    Audio->>Audio: checkAutoSwitchOutputPort()
    alt auto and real port available
      Audio->>Audio: setPort(cardId, portName, DirectionSink, true)
    else auto and no real port
      Audio->>PulseContext: GetDefaultSink()
      alt default sink not null-sink
        Audio->>Audio: LoadNullSinkModule()
        Audio->>PulseContext: SetDefaultSink(nullSinkName)
      else default sink already null-sink
        Audio-->>Audio: keep current default
      end
    end
  else sinkName does not contain null-sink
    Audio-->>System: log warning failed to get sinkInfo
  end
else sinkInfo found
  Audio->>Audio: moveSinkInputsToSink(sinkInfo)
  Audio->>PulseContext: SetDefaultSink(sinkName)
  Audio->>Audio: defaultSink = sinkInfo
end

System-->>Audio: updateDefaultSink complete
Loading

Class diagram for Audio null-sink handling changes

classDiagram
class Audio {
  +init() error
  +refreshSinks()
  +updateDefaultSink(sinkName string)
  +updateDefaultSource(sourceName string)
  +autoSwitchOutputPort() bool
  +autoSwitchInputPort() bool
  +LoadNullSinkModule()
  +isModuleExist(moduleName string) bool
}

class PulseContext {
  +LoadModule(name string, args string)
  +GetDefaultSink() string
  +GetDefaultSource() string
  +SetDefaultSink(name string)
  +SetDefaultSource(name string)
}

Audio --> PulseContext : uses
Loading

File-Level Changes

Change Details Files
Load the null-sink PulseAudio module once during audio initialization and avoid reloading if it already exists.
  • Remove per-refresh null-sink existence tracking in sink refresh logic
  • Move null-sink module loading from refreshSinks into the Audio.init() flow
  • Guard null-sink module loading with a module-existence check to prevent duplicate loads
audio1/audio.go
Add null-sink–aware fallback when updating default sink and source so that the system attempts to auto-switch to real ports if available.
  • When updating default sink, if the target is null-sink and no sinkInfo is found, clear the default and trigger automatic output port switching instead of only logging a warning
  • When updating default source, if the target is null-sink and no sourceInfo is found, clear the default and trigger automatic input port switching instead of only logging a warning
audio1/audio.go
Refine auto-switch logic for output and input ports to handle null-sink as a safe fallback without reloading the module or resetting defaults unnecessarily.
  • In automatic output port switching, if no preferred physical port exists, only load the null-sink module and set default sink to null-sink when the current default is not already a null-sink, otherwise just log that it is already null-sink
  • In automatic input port switching, apply analogous logic for the default source, setting it to the null-sink monitor only when the current default is not already a null-sink
audio1/audio_events.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The checks for strings.Contains(..., "null-sink") introduce magic strings and partial matches; consider using the existing nullSinkName constant (and, where needed, appending .monitor) or a small helper like isNullSink(name string) to keep the logic consistent and less error-prone.
  • The new updateDefaultSink/updateDefaultSource branches that auto-switch when encountering null-sink skip the previous warning log; it may be useful to log an explicit info/debug message when autoSwitch*Port is triggered to aid in diagnosing unexpected default device changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The checks for `strings.Contains(..., "null-sink")` introduce magic strings and partial matches; consider using the existing `nullSinkName` constant (and, where needed, appending `.monitor`) or a small helper like `isNullSink(name string)` to keep the logic consistent and less error-prone.
- The new `updateDefaultSink`/`updateDefaultSource` branches that auto-switch when encountering `null-sink` skip the previous warning log; it may be useful to log an explicit info/debug message when `autoSwitch*Port` is triggered to aid in diagnosing unexpected default device changes.

## Individual Comments

### Comment 1
<location> `audio1/audio_events.go:209-210` </location>
<code_context>
 	auto, cardId, portName := a.checkAutoSwitchOutputPort()
 	if auto {
 		if cardId == 0 || portName == "" {
-			if !a.isModuleExist(nullSinkModuleName) {
-				a.ctx.LoadModule(nullSinkModuleName, "")
+			if !strings.Contains(a.ctx.GetDefaultSink(), "null-sink") {
+				a.LoadNullSinkModule()
+				logger.Info("no prefer port, set defaut sink to", nullSinkName)
</code_context>

<issue_to_address>
**suggestion:** Avoid hard-coding "null-sink" and reuse the existing constant instead.

In both this check and the input case below, use the `nullSinkName` constant instead of the literal "null-sink" to avoid hidden coupling. That way, future changes to the sink name won’t silently break these `Contains` checks.

Suggested implementation:

```golang
		if cardId == 0 || portName == "" {
			if !strings.Contains(a.ctx.GetDefaultSink(), nullSinkName) {
				a.LoadNullSinkModule()
				logger.Info("no prefer port, set defaut sink to", nullSinkName)
				a.ctx.SetDefaultSink(nullSinkName)
			} else {
				logger.Info("no prefer port, default sink is", nullSinkName)
			}
			return true
		} else {
			err := a.setPort(cardId, portName, pulse.DirectionSink, true)

```

There is likely a similar `"null-sink"` literal in the "input case below" you mentioned (perhaps checking the default source or input module). That occurrence should be updated in the same way: replace the hard-coded `"null-sink"` with `nullSinkName` in both any `Contains` checks and log messages to keep the coupling explicit and centralized on the constant.
</issue_to_address>

### Comment 2
<location> `audio1/audio_events.go:212` </location>
<code_context>
-				a.ctx.LoadModule(nullSinkModuleName, "")
+			if !strings.Contains(a.ctx.GetDefaultSink(), "null-sink") {
+				a.LoadNullSinkModule()
+				logger.Info("no prefer port, set defaut sink to", nullSinkName)
+				a.ctx.SetDefaultSink(nullSinkName)
+			} else {
</code_context>

<issue_to_address>
**nitpick (typo):** Minor typo in log message: "defaut" should be "default".

The same misspelling (`"defaut"`) exists in the source-side log message; please correct both so log searches remain consistent and reliable.

Suggested implementation:

```golang
				a.LoadNullSinkModule()
				logger.Info("no prefer port, set default sink to", nullSinkName)
				a.ctx.SetDefaultSink(nullSinkName)

```

There is another log message on the "source" side with the same typo `"defaut"`. In `audio1/audio_events.go`, search for the string `set defaut source` (or any log line containing `"defaut"` related to the source/default source) and update it to `"set default source"` (or similarly correct wording) to keep logging consistent for sink and source.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

a.ctx.LoadModule(nullSinkModuleName, "")
if !strings.Contains(a.ctx.GetDefaultSink(), "null-sink") {
a.LoadNullSinkModule()
logger.Info("no prefer port, set defaut sink to", nullSinkName)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Minor typo in log message: "defaut" should be "default".

The same misspelling ("defaut") exists in the source-side log message; please correct both so log searches remain consistent and reliable.

Suggested implementation:

				a.LoadNullSinkModule()
				logger.Info("no prefer port, set default sink to", nullSinkName)
				a.ctx.SetDefaultSink(nullSinkName)

There is another log message on the "source" side with the same typo "defaut". In audio1/audio_events.go, search for the string set defaut source (or any log line containing "defaut" related to the source/default source) and update it to "set default source" (or similarly correct wording) to keep logging consistent for sink and source.

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