diff --git a/src/main/venmic.ts b/src/main/venmic.ts index 69efbe1..829cda1 100644 --- a/src/main/venmic.ts +++ b/src/main/venmic.ts @@ -10,6 +10,8 @@ import { join } from "path"; import { IpcEvents } from "shared/IpcEvents"; import { STATIC_DIR } from "shared/paths"; +import { Settings } from "./settings"; + type LinkData = Parameters[0]; type Node = Record; @@ -67,17 +69,17 @@ function getRendererAudioServicePid() { ); } -ipcMain.handle(IpcEvents.VIRT_MIC_LIST, (_, props?: string[]) => { +ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => { const audioPid = getRendererAudioServicePid(); const targets = obtainVenmic() - ?.list(props) + ?.list(Settings.store.audioGranularSelect ? ["application.process.id"] : undefined) .filter(s => s["application.process.id"] !== audioPid); return targets ? { ok: true, targets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated }; }); -ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: Node[], workaround?: boolean) => { +ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: Node[]) => { const pid = getRendererAudioServicePid(); const data: LinkData = { @@ -85,22 +87,42 @@ ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: Node[], workaround?: boole exclude: [{ "application.process.id": pid }] }; - if (workaround) { + const settings = Settings.store; + + if (settings.audioIgnoreInputMedia ?? true) { + data.exclude?.push({ "media.class": "Stream/Input/Audio" }); + } + + if (settings.audioIgnoreVirtual ?? true) { + data.exclude?.push({ "node.virtual": "true" }); + } + + if (settings.audioWorkaround) { data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }]; } return obtainVenmic()?.link(data); }); -ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean, onlyDefaultSpeakers?: boolean) => { +ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, () => { const pid = getRendererAudioServicePid(); + const settings = Settings.store; + const data: LinkData = { exclude: [{ "application.process.id": pid }], - only_default_speakers: onlyDefaultSpeakers + only_default_speakers: settings.audioOnlyDefaultSpeakers }; - if (workaround) { + if (settings.audioIgnoreInputMedia ?? true) { + data.exclude?.push({ "media.class": "Stream/Input/Audio" }); + } + + if (settings.audioIgnoreVirtual ?? true) { + data.exclude?.push({ "node.virtual": "true" }); + } + + if (settings.audioWorkaround) { data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }]; } diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index 9e047b7..1ff5f6a 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -62,13 +62,12 @@ export const VesktopNative = { }, /** only available on Linux. */ virtmic: { - list: (props?: string[]) => + list: () => invoke< { ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: Node[]; hasPipewirePulse: boolean } - >(IpcEvents.VIRT_MIC_LIST, props), - start: (targets: Node[], workaround?: boolean) => invoke(IpcEvents.VIRT_MIC_START, targets, workaround), - startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) => - invoke(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers), + >(IpcEvents.VIRT_MIC_LIST), + start: (targets: Node[]) => invoke(IpcEvents.VIRT_MIC_START, targets), + startSystem: () => invoke(IpcEvents.VIRT_MIC_START_SYSTEM), stop: () => invoke(IpcEvents.VIRT_MIC_STOP) }, arrpc: { diff --git a/src/renderer/components/ScreenSharePicker.tsx b/src/renderer/components/ScreenSharePicker.tsx index 0303a51..483f642 100644 --- a/src/renderer/components/ScreenSharePicker.tsx +++ b/src/renderer/components/ScreenSharePicker.tsx @@ -6,7 +6,7 @@ import "./screenSharePicker.css"; -import { closeModal, Logger, Margins, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils"; +import { closeModal, Logger, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils"; import { findStoreLazy, onceReady } from "@vencord/types/webpack"; import { Button, @@ -22,6 +22,7 @@ import { import { Node } from "@vencord/venmic"; import type { Dispatch, SetStateAction } from "react"; import { addPatch } from "renderer/patches/shared"; +import { useSettings } from "renderer/settings"; import { isLinux, isWindows } from "renderer/utils"; const StreamResolutions = ["480", "720", "1080", "1440"] as const; @@ -48,10 +49,6 @@ interface StreamSettings { audio: boolean; audioSources?: AudioSources; contentHint?: string; - workaround?: boolean; - ignoreInputMedia?: boolean; - onlyDefaultSpeakers?: boolean; - granularSelect?: boolean; } export interface StreamPick extends StreamSettings { @@ -133,9 +130,9 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) { didSubmit = true; if (v.audioSources && v.audioSources !== "None") { if (v.audioSources === "Entire System") { - await VesktopNative.virtmic.startSystem(v.workaround); + await VesktopNative.virtmic.startSystem(); } else { - await VesktopNative.virtmic.start(v.audioSources, v.workaround); + await VesktopNative.virtmic.start(v.audioSources); } } resolve(v); @@ -172,6 +169,86 @@ function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScre ); } +function AudioSettingsModal({ + modalProps, + close, + setAudioSources +}: { + modalProps: any; + close: () => void; + setAudioSources: (s: AudioSources) => void; +}) { + const Settings = useSettings(); + + return ( + + + Venmic Settings + + + + (Settings.audioWorkaround = v)} + value={Settings.audioWorkaround ?? false} + note={ + <> + Work around an issue that causes the microphone to be shared instead of the correct audio. + Only enable if you're experiencing this issue. + + } + > + Microphone Workaround + + (Settings.audioOnlyDefaultSpeakers = v)} + value={Settings.audioOnlyDefaultSpeakers ?? true} + note={<>When sharing entire desktop audio, only share apps that play to the default speakers.} + > + Only Default Speakers + + (Settings.audioIgnoreInputMedia = v)} + value={Settings.audioIgnoreInputMedia ?? true} + note={<>Exclude nodes that are intended to capture audio.} + > + Ignore Inputs + + (Settings.audioIgnoreVirtual = v)} + value={Settings.audioIgnoreVirtual ?? true} + note={ + <> + Exclude virtual nodes, such as nodes belonging to sinks, this might be useful when using + "mix bussing" + + } + > + Ignore Virtual + + { + Settings.audioGranularSelect = value; + setAudioSources("None"); + }} + value={Settings.audioGranularSelect ?? false} + note={<>Allow to select applications more granularly.} + > + Granular Selection + + + + + + + ); +} + function StreamSettings({ source, settings, @@ -183,6 +260,8 @@ function StreamSettings({ setSettings: Dispatch>; skipPicker: boolean; }) { + const Settings = useSettings(); + const [thumb] = useAwaiter( () => (skipPicker ? Promise.resolve(source.url) : VesktopNative.capturer.getLargeThumbnail(source.id)), { @@ -191,132 +270,129 @@ function StreamSettings({ } ); + const openSettings = () => { + const key = openModal(props => ( + props.onClose()} + setAudioSources={sources => setSettings(s => ({ ...s, audioSources: sources }))} + /> + )); + }; + return ( -
-
- What you're streaming - - - {source.name} - +
+ What you're streaming + + + {source.name} + - Stream Settings + Stream Settings - -
-
- Resolution + +
+
+ Resolution +
+ {StreamResolutions.map(res => ( + + ))} +
+
+ +
+ Frame Rate +
+ {StreamFps.map(fps => ( + + ))} +
+
+
+
+
+ Content Type +
- {StreamResolutions.map(res => ( - - ))} -
-
- -
- Frame Rate -
- {StreamFps.map(fps => ( - - ))} -
-
-
-
-
- Content Type -
-
- - -
-
-

- Choosing "Prefer Clarity" will result in a significantly lower framerate in - exchange for a much sharper and clearer image. -

-
-
- {isWindows && ( - setSettings(s => ({ ...s, audio: checked }))} - hideBorder - className="vcd-screen-picker-audio" + - )} -
-
-
-
+ Prefer Smoothness + setSettings(s => ({ ...s, contentHint: "motion" }))} + /> + + +
+
+

+ Choosing "Prefer Clarity" will result in a significantly lower framerate in exchange + for a much sharper and clearer image. +

+
+
+ {isWindows && ( + setSettings(s => ({ ...s, audio: checked }))} + hideBorder + className="vcd-screen-picker-audio" + > + Stream With Audio + + )} + +
-
{isLinux && ( setSettings(s => ({ ...s, audioSources: sources }))} - setWorkaround={value => setSettings(s => ({ ...s, workaround: value }))} - setOnlyDefaultSpeakers={value => setSettings(s => ({ ...s, onlyDefaultSpeakers: value }))} - setIgnoreInputMedia={value => setSettings(s => ({ ...s, ignoreInputMedia: value }))} - setGranularSelect={value => setSettings(s => ({ ...s, granularSelect: value }))} /> )} -
+ ); } @@ -387,30 +463,16 @@ function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[ function AudioSourcePickerLinux({ audioSources, - workaround, - onlyDefaultSpeakers, - ignoreInputMedia, granularSelect, setAudioSources, - setWorkaround, - setIgnoreInputMedia, - setOnlyDefaultSpeakers, - setGranularSelect + openSettings }: { audioSources?: AudioSources; - workaround?: boolean; - onlyDefaultSpeakers?: boolean; - ignoreInputMedia?: boolean; granularSelect?: boolean; - setAudioSources(s: AudioSources): void; - setWorkaround(b: boolean): void; - setIgnoreInputMedia(b: boolean): void; - setOnlyDefaultSpeakers(b: boolean): void; - setGranularSelect(b: boolean): void; + openSettings: () => void; + setAudioSources: (s: AudioSources) => void; }) { - const properties = granularSelect ? ["application.process.id"] : undefined; - - const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(properties), { + const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), { fallbackValue: { ok: true, targets: [], hasPipewirePulse: true } }); @@ -455,109 +517,71 @@ function AudioSourcePickerLinux({ list.findIndex(x => x.name === value.name) === index; return ( - <> - Audio Settings - - {loading ? ( - Loading Audio Sources... - ) : ( - Audio Source - )} +
+ {loading ? ( + Loading Audio Sources... + ) : ( + Audio Source + )} - {!sources.ok && sources.isGlibCxxOutdated && ( - - Failed to retrieve Audio Sources because your C++ library is too old to run - - venmic - - . See{" "} - - this guide - {" "} - for possible solutions. - - )} + {!sources.ok && sources.isGlibCxxOutdated && ( + + Failed to retrieve Audio Sources because your C++ library is too old to run + + venmic + + . See{" "} + + this guide + {" "} + for possible solutions. + + )} - {hasPipewirePulse || ignorePulseWarning ? ( - allSources && ( - mapToAudioItem(target, granularSelect)) + .flat() + .filter(uniqueName) + .map(({ name, value }) => ({ + label: name, + value: value, + default: name === "None" + }))} + isSelected={isSelected} + select={update} + serialize={String} + popoutPosition="top" + /> + ) + ) : ( + + Could not find pipewire-pulse. See{" "} + + this guide + {" "} + on how to switch to pipewire.
+ You can still continue, however, please{" "} + beware that you can only share audio of apps that are running under pipewire. +
+
+ setIgnorePulseWarning(true)}>I know what I'm doing +
+ )} - - - - Work around an issue that causes the microphone to be shared instead of the correct audio. - Only enable if you're experiencing this issue. - - } - > - Microphone Workaround - - - When sharing entire desktop audio, only share apps that play to the default speakers.} - > - Only Default Speakers - - Ignore Nodes that are intended to capture audio.} - > - Ignore Input Media - - { - setGranularSelect(value); - setAudioSources("None"); - }} - value={granularSelect ?? false} - note={<>Allow to select applications more granularly.} - > - Granular Selection - - - + +
); } diff --git a/src/renderer/components/screenSharePicker.css b/src/renderer/components/screenSharePicker.css index c2586a1..8b46c67 100644 --- a/src/renderer/components/screenSharePicker.css +++ b/src/renderer/components/screenSharePicker.css @@ -11,17 +11,6 @@ gap: 1em; } -.vcd-screen-picker-settings-grid { - gap: 1em; - display: grid; - grid-template-columns: 1fr 1fr; -} - -.vcd-screen-picker-settings-grid>div { - display: flex; - flex-direction: column; -} - .vcd-screen-picker-card { flex-grow: 1; } @@ -68,7 +57,7 @@ } .vcd-screen-picker-preview-img-linux { - width: 100%; + width: 60%; margin-bottom: 0.5em; } @@ -120,6 +109,11 @@ flex: 1 1 auto; } +.vcd-screen-picker-settings-button { + margin-left: auto; + margin-top: 0.3rem; +} + .vcd-screen-picker-radios { display: flex; width: 100%; @@ -148,4 +142,4 @@ font-size: 14px; line-height: 20px; font-weight: 400; -} \ No newline at end of file +} diff --git a/src/shared/settings.d.ts b/src/shared/settings.d.ts index a30d5cd..0efff76 100644 --- a/src/shared/settings.d.ts +++ b/src/shared/settings.d.ts @@ -30,6 +30,13 @@ export interface Settings { splashBackground?: string; spellCheckLanguages?: string[]; + + audioWorkaround?: boolean; + audioGranularSelect?: boolean; + + audioIgnoreVirtual?: boolean; + audioIgnoreInputMedia?: boolean; + audioOnlyDefaultSpeakers?: boolean; } export interface State {