Skip to content

Add mic-only muting and playback volume control to MixingAudioDeviceModule#24

Open
paterkleomenis wants to merge 2 commits intodesktop-app:masterfrom
paterkleomenis:mixing-audio-mute-volume
Open

Add mic-only muting and playback volume control to MixingAudioDeviceModule#24
paterkleomenis wants to merge 2 commits intodesktop-app:masterfrom
paterkleomenis:mixing-audio-mute-volume

Conversation

@paterkleomenis
Copy link
Contributor

@paterkleomenis paterkleomenis commented Feb 22, 2026

⚠️ Depends on #28 — must be merged first

Summary

Adds two runtime controls to MixingAudioDeviceModule / MixingAudioControl:

  • Mic-only mute — silences the microphone at the ADM level while keeping the audio channel open, so system-audio loopback continues to flow uninterrupted.
  • Playback volume — scales incoming playback samples in software, providing per-call volume control independent of the system mixer.

Changes

All changes are in webrtc/webrtc_create_adm.cpp and webrtc/webrtc_create_adm.h.

MixingAudioTransport

RecordedDataIsAvailable — when _microphoneMuted is set, the mic buffer is zeroed before the loopback mix step and before forwarding to the inner transport. Loopback audio (if enabled) is mixed into the zeroed buffer, so system audio still reaches the remote peer.

NeedMorePlayData — after the inner transport fills the playback buffer, every sample is multiplied by _playbackVolume (float, 0.0–1.0). At unity gain (1.0) the branch is skipped entirely — no performance cost when unused.

Both atomics are written relaxed; the hot-path reads are relaxed too (no synchronisation needed — stale values are safe).

MixingAudioDeviceModule

New setMicrophoneMuted(bool) and setPlaybackVolume(float) methods forwarded to the active MixingAudioTransport. State is also re-applied when RegisterAudioCallback() installs a fresh transport (e.g. after an ADM restart).

MixingAudioControl (public API)

void setMicrophoneMuted(bool muted);
bool microphoneMuted() const;

void setPlaybackVolume(float volume);  // 0.0 = silent, 1.0 = unity gain
float playbackVolume() const;

State is persisted in MixingAudioControl and re-applied whenever a new MixingAudioDeviceModule attaches (via attach()), so callers can set these before the ADM is created.

Platform

Linux and Windows. macOS is unaffected.

@paterkleomenis paterkleomenis force-pushed the mixing-audio-mute-volume branch from 9fc6523 to a713995 Compare February 22, 2026 00:27
@paterkleomenis paterkleomenis changed the title Add mic-only muting and playback volume control to MixingAudioDeviceModule Add MixingAudioDeviceModule with loopback capture, mic muting, and playback volume control Feb 22, 2026
@paterkleomenis paterkleomenis force-pushed the mixing-audio-mute-volume branch from a713995 to f2ebab8 Compare February 22, 2026 17:35
@paterkleomenis paterkleomenis changed the title Add MixingAudioDeviceModule with loopback capture, mic muting, and playback volume control Add mic-only muting and playback volume control to MixingAudioDeviceModule Feb 22, 2026
@paterkleomenis paterkleomenis force-pushed the mixing-audio-mute-volume branch 3 times, most recently from 461db3e to 447ba22 Compare February 22, 2026 23:30
@ilya-fedin
Copy link
Contributor

The PR has conflicts

@ilya-fedin
Copy link
Contributor

This PR also has the commit from #26, it's a problem

@paterkleomenis paterkleomenis force-pushed the mixing-audio-mute-volume branch from 447ba22 to 1088f5e Compare March 5, 2026 11:41
@ilya-fedin
Copy link
Contributor

This PR also has the commit from #26, it's a problem

This isn't fixed

@paterkleomenis paterkleomenis force-pushed the mixing-audio-mute-volume branch from 1088f5e to 6fea61e Compare March 5, 2026 11:55
@ilya-fedin
Copy link
Contributor

ilya-fedin commented Mar 5, 2026

You made it worse. What I meant is that the commit either should go to the other PR and this PR get closed or this PR should explicitly mark that it depends on the other one. But you squashed the commits and the problem is still isn't fixed: once the other PR is merged, this one will conflict.

@ilya-fedin
Copy link
Contributor

ilya-fedin commented Mar 5, 2026

I don't see any tdesktop PR being dependent on #26. If the only reason of #26 is mic-only muting and playback volume control then just leave only one PR and name it accordingly.

@paterkleomenis paterkleomenis force-pushed the mixing-audio-mute-volume branch from 6fea61e to db2b20d Compare March 5, 2026 13:17
@ilya-fedin
Copy link
Contributor

I now see there's #27 has the same commits as #26 and this PR too...

Introduces infrastructure to mix system-audio loopback into the outgoing
microphone stream during a call, on both Linux and Windows.

New types (in webrtc_create_adm.cpp, anonymous namespace):

  LoopbackCollector
    Thread-safe mono ring buffer (max 2 s at 48 kHz). Loopback capture
    threads call pushSamples(); MixingAudioTransport calls readAndMix()
    to saturating-add loopback audio into the mic frame.

  DirectLoopbackCapture  (Linux only, guarded by WEBRTC_LINUX)
    Background std::thread that opens the PulseAudio monitor source via
    alcCaptureOpenDevice (stereo preferred, mono fallback), feeds decoded
    frames into LoopbackCollector, and stops cleanly on destruction.
    findMonitorDevice() prefers the monitor that matches the current
    default playback sink.

  MixingAudioTransport
    webrtc::AudioTransport decorator. When mixing is enabled it copies the
    mic buffer, calls LoopbackCollector::readAndMix, then forwards the
    blended frame to the inner transport. Disabled path is zero-overhead.

  MixingAudioDeviceModule  (details namespace)
    webrtc::AudioDeviceModule wrapper. Installs MixingAudioTransport in
    RegisterAudioCallback(), starts/stops DirectLoopbackCapture (Linux) or
    a dedicated loopback ADM + LoopbackAdmTransport (Windows) when
    setLoopbackEnabled() is toggled via MixingAudioControl.

New public API (webrtc_create_adm.h):

  MixingAudioControl
    Shared handle that survives ADM recreation. setLoopbackEnabled() /
    loopbackEnabled() let callers toggle mixing at any time; the control
    re-applies pending state whenever a new MixingAudioDeviceModule
    attaches itself.

  MixingAudioDeviceModuleCreator(innerCreator, control)
    Returns a creator lambda that wraps any ADM (e.g. the OpenAL ADM)
    inside a MixingAudioDeviceModule.
…odule

When screen audio is being shared in a call, muting the microphone should
not kill the system-audio stream. The two new runtime controls address this:

  setMicrophoneMuted(bool)
    Zeroes out the mic samples inside MixingAudioTransport::RecordedDataIsAvailable
    before they reach WebRTC, while keeping the audio channel open so that
    loopback (system audio) continues to flow through unchanged.

  setPlaybackVolume(float)
    Scales every decoded playback sample by the given factor (0.0-1.0) in
    MixingAudioTransport::NeedMorePlayData, providing per-call software volume
    control independent of the system mixer.

Both controls are exposed on the shared MixingAudioControl handle with their
respective atomic fields, so changes take effect immediately without any
lock held in the hot path.

State is persisted in MixingAudioControl and re-applied whenever a new
MixingAudioDeviceModule attaches (e.g. after an ADM restart) or whenever
RegisterAudioCallback() installs a fresh MixingAudioTransport.
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