From 7fcdd8b94a1dad639b26ee9ba37634b43421bb04 Mon Sep 17 00:00:00 2001
From: Alexander Nazarov <alexzerg91@gmail.com>
Date: Fri, 27 Sep 2024 18:26:26 -0700
Subject: [PATCH] Hide invisible audio sources from mixer

---
 .../editor/elements/Mixer.tsx                 | 28 +++++++++++++++----
 .../editor/elements/mixer/GLVolmeters.tsx     |  8 ++++--
 app/services/audio/audio.ts                   |  4 +++
 3 files changed, 32 insertions(+), 8 deletions(-)

diff --git a/app/components-react/editor/elements/Mixer.tsx b/app/components-react/editor/elements/Mixer.tsx
index 1aece143acc4..a3df93ffad50 100644
--- a/app/components-react/editor/elements/Mixer.tsx
+++ b/app/components-react/editor/elements/Mixer.tsx
@@ -9,9 +9,31 @@ import { Services } from 'components-react/service-provider';
 import { Menu } from 'util/menus/Menu';
 import { $t } from 'services/i18n';
 import { useRealmObject } from 'components-react/hooks/realm';
+import { getDefined } from 'util/properties-type-guards';
 
 const mins = { x: 150, y: 120 };
 
+export function getVisibleAudioSourcesIds() {
+  const { ScenesService, AudioService } = Services;
+  const activeScene = getDefined(ScenesService.views.activeScene);
+
+  // Get sources ids for visible scene items
+  // using the Set to avoid duplicates and improve performance
+  const visibleSourcesIds = new Set(
+    activeScene.items.filter(item => item.visible).map(item => item.sourceId),
+  );
+
+  return AudioService.views.sourcesForCurrentScene
+    .filter(source => {
+      return (
+        !source.mixerHidden &&
+        source.isControlledViaObs &&
+        (visibleSourcesIds.has(source.sourceId) || source.isGlobal())
+      );
+    })
+    .map(source => source.sourceId);
+}
+
 export function Mixer() {
   const { EditorCommandsService, AudioService, CustomizationService } = Services;
 
@@ -26,11 +48,7 @@ export function Mixer() {
   }, []);
 
   const performanceMode = useRealmObject(CustomizationService.state).performanceMode;
-  const { audioSourceIds } = useVuex(() => ({
-    audioSourceIds: AudioService.views.sourcesForCurrentScene
-      .filter(source => !source.mixerHidden && source.isControlledViaObs)
-      .map(source => source.sourceId),
-  }));
+  const audioSourceIds = useVuex(getVisibleAudioSourcesIds);
 
   function showAdvancedSettings() {
     AudioService.actions.showAdvancedSettings();
diff --git a/app/components-react/editor/elements/mixer/GLVolmeters.tsx b/app/components-react/editor/elements/mixer/GLVolmeters.tsx
index 9d95c7ab7ff5..38a92fc8effa 100644
--- a/app/components-react/editor/elements/mixer/GLVolmeters.tsx
+++ b/app/components-react/editor/elements/mixer/GLVolmeters.tsx
@@ -7,6 +7,7 @@ import vShaderSrc from 'util/webgl/shaders/volmeter.vert';
 import fShaderSrc from 'util/webgl/shaders/volmeter.frag';
 import { Services } from 'components-react/service-provider';
 import { assertIsDefined, getDefined } from 'util/properties-type-guards';
+import { getVisibleAudioSourcesIds } from '../Mixer';
 
 // Configuration
 const CHANNEL_HEIGHT = 3;
@@ -133,9 +134,10 @@ class GLVolmetersController {
 
   // TODO: refactor into a single source of truth between Mixer and Volmeters
   get audioSources() {
-    return this.audioService.views.sourcesForCurrentScene.filter(source => {
-      return !source.mixerHidden && source.isControlledViaObs;
-    });
+    const vidibleSourceIds = getVisibleAudioSourcesIds();
+    return this.audioService.views.sourcesForCurrentScene.filter(source =>
+      vidibleSourceIds.includes(source.sourceId),
+    );
   }
 
   /**
diff --git a/app/services/audio/audio.ts b/app/services/audio/audio.ts
index b35425771499..7c9523c1aa1f 100644
--- a/app/services/audio/audio.ts
+++ b/app/services/audio/audio.ts
@@ -388,6 +388,10 @@ export class AudioSource implements IAudioSourceApi {
     return { ...this.source.state, ...this.audioSourceState };
   }
 
+  isGlobal() {
+    return this.source.channel !== void 0;
+  }
+
   get monitoringOptions() {
     return [
       { value: obs.EMonitoringType.None, label: $t('Monitor Off') },