Repository navigation
feat: Remove output tracks from sources feeding the input and add the source to every scene - #63
Merged
Merged
Conversation
semnil
force-pushed
the
feat/auto-clear-output-tracks
branch
3 times, most recently
from
October 25, 2026 04:35
f46fb81 to
9808141
Compare
semnil
force-pushed
the
feat/auto-clear-output-tracks
branch
from
October 25, 2026 06:30
9808141 to
6a7f6d9
Compare
This was referenced Nov 1, 2026
semnil
force-pushed
the
feat/auto-clear-output-tracks
branch
2 times, most recently
from
November 1, 2026 05:53
cfbf018 to
00bdfbb
Compare
When another audio source is routed to the plugin's input mix track, the plugin captures that mix and re-emits it on its output tracks. If the same source also outputs directly on one of those tracks, its audio is doubled. Remove the plugin's output tracks from any source that feeds its input track. - Clears on three triggers: the plugin's own output changing, a source being created or loaded, and the input track changing. Only sources routed to the input track are affected; other MixTrack2Source sources are skipped. - Remembers what it cleared and restores untouched bits when the plugin is removed or its input/output tracks change, and gives them back to a source that no longer sends audio to the input track at the next check. Bits the user re-enables are left alone, also across an input-track change. The record persists across scene save/load. - Signal callbacks and the settings update only note what changed. The sweeps and give-backs, with the source lookups and enumerations they need, run as UI tasks outside the signal that queued them, never on the audio thread. - The input-track loopback guard shares the plugin's own audio_mixers handler: it first takes the input track out of the outputs and hands the result to libobs, then queues a sweep only when the other outputs change. The sweeps take the plugin's output tracks from that value. - A source created on the UI thread is checked once its creator has set its routing. Loading a source, including the reload that undo/redo does, checks it again with the same result. - A cleared track is refcounted module-wide across instances, so it is restored only once the last instance stops needing it, and a track off from the start is never restored. - When an instance is destroyed, its cleared tracks are given back on the UI thread, in order with the checks the UI thread runs. - A destroyed source keeps its record for the session; such a record is not saved. A track whose last holder lets go while its source is gone waits for a source with the same uuid (undo): an instance that still needs it cleared takes it over, otherwise it is turned back on, once. - Waiting tracks are dropped when the frontend cleans up a scene collection (switch or exit), and none start waiting until the next one has loaded. With ENABLE_FRONTEND_API, CMake defines it for the plugin and the plugin calls obs-frontend-api directly; without it, the plugin looks obs-frontend-api up once at module load. When neither reaches it, a waiting track stays until its source appears or the module unloads. - Only the real mix tracks are cleared and restored. - README.md and README.ja.md describe the removal in the feature list, the usage and the usage example. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OBS outputs only the audio of the sources in the shown scene, so the audio source stopped working while a scene without it was shown. Once the frontend has put a new instance in a scene, the plugin adds it to every other scene of the main canvas, once, and adds each such instance to the scenes the user adds later. A scene the user removes it from keeps going without it. Scenes loaded from a scene collection keep their sources as saved, and an instance saved after this keeps its scenes as saved. The README describes this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
semnil
force-pushed
the
feat/auto-clear-output-tracks
branch
from
November 1, 2026 07:09
3aa8154 to
272d6cc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
When another audio source is routed to the plugin's input mix track, the plugin captures that mix and re-emits it on its output tracks. If the same source is also routed directly to one of those output tracks, its audio is doubled there. This change removes the plugin's output tracks from any source that feeds its input track.
OBS outputs only the audio of the sources in the shown scene, so the plugin stopped working while a scene without it was shown. The second commit adds the plugin to every scene.
Behavior
audio_mixershandler: it takes the input track out of the outputs, hands the result to libobs, and queues a sweep only when the outputs other than the input track change. The sweeps take the plugin's output tracks from that value. libobs stores it only after the signal returns, so a task that runs in between, for example while the loopback guard's write from the audio thread is still inside its signal, applies the new value.OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP, on a collection switch and at exit), and none start waiting until the next collection has loaded (OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED). Emptying the output channels for a while, as the auto-configuration wizard's tests do, keeps them.ENABLE_FRONTEND_API(on in the CMake presets), CMake definesENABLE_FRONTEND_APIfor the plugin, obs-frontend-api is linked, and the plugin registers its event callback throughobs-frontend-api.hat module load. Without it, at module load the plugin looks upobs_frontend_add_event_callbackandobs_frontend_remove_event_callbackonce (dlopen(NULL)anddlsymon macOS and Linux,GetModuleHandleWandGetProcAddresson Windows). Either way it logsfrontend events connectedorfrontend events not available. When no event arrives, a waiting track stays until a source with its uuid appears or the module unloads. The callback is removed onOBS_FRONTEND_EVENT_EXIT, before the frontend API goes away.source_loadis skipped), and an instance saved with this keeps its scenes as saved (settings keymt2s_every_scene); an instance saved by an earlier version is added to every scene the first time it is loaded. No output channel is used, so each scene mixes the plugin once.Docs
README.md and README.ja.md describe the removal in the feature list, the usage (a source routed to the input track later keeps its tracks, and a source that stops sending audio to it gets them back, when the plugin's input or output tracks change or the scene collection is loaded again), the usage example, and adding the audio source to every scene.
Verification
src/plugin-main.cof272d6ccpassesclang -fsyntax-only -Wall -Wextra -Wswitch -Wenum-conversionagainst the OBS 31.1.1 headers withoutENABLE_FRONTEND_API, and withENABLE_FRONTEND_APIandENABLE_UPDATE_CHECK. The only warning comes from the OBS headerutil_uint64.h(_udiv128under clang on Windows). clang-format reports no changes. CI builds the full plugin.272d6ccevery check passes (failures=0): the cleared track 6 comes back after the source drops the input track and the plugin output changes; a plugin the user adds to scene A is also in scene B (once in each) and stays active while B is shown; a scene added afterwards gets it, a scene that gotsource_loaddoes not; it stays out of B after being removed there; an instance created withmt2s_every_sceneset stays only where it was put. One50d13cthe three every-scene checks that need adding fail (failures=3), and without the give-back the give-back check fails as well. Not marking loaded scenes fails the loaded-scene check, and ignoringmt2s_every_scenefails the check for an instance created with it.0.125from scenes A and B:0.125steady on A, across 1 s fades A to B and B to A, and across a cut.The checks below were run before the rebase onto master (
cfbf018), on code that had neither the loopback guard of #66 nor the direct obs-frontend-api calls, the give-back, or adding to every scene.frontend/widgets/OBSBasic_SceneCollections.cppdoes.audio_mixerssignal while the UI task runs (the records match the output that write stores), deleting and reloading real scene items, waiting tracks taken over by several instances, and two instances on different input tracks. Its lock-order run (the audio thread inside the plugin's ownaudio_mixerssignal while the UI thread enumerates sources and connects to their signals, as Advanced Audio Properties does) completes on both.ENABLE_FRONTEND_API): the event numbers written in the source are checked at compile time againstobs-frontend-api.hof OBS 31.1.1 and 33.0.0. A module loaded withdlopeninto a process that links OBS 33.0.0'sobs-frontend-api.dylibfinds both functions, and does not find them in a process without it.frontend events connectedlog line and adding the instance to every scene there (undo and redo, scene duplication, a scene collection switch, studio mode); scenes in canvases other than the main one; the routing checks above against the current code; sources created by scripts or obs-websocket off the UI thread.🤖 Generated with Claude Code