AudioShare: Even more granular selection, Allow device sharing (#750)
This commit is contained in:
parent
61bbd7f6aa
commit
9acc6652ff
3 changed files with 60 additions and 18 deletions
|
@ -72,7 +72,7 @@ ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
|
||||||
const { granularSelect } = Settings.store.audio ?? {};
|
const { granularSelect } = Settings.store.audio ?? {};
|
||||||
|
|
||||||
const targets = obtainVenmic()
|
const targets = obtainVenmic()
|
||||||
?.list(granularSelect ? ["application.process.id"] : undefined)
|
?.list(granularSelect ? ["node.name"] : undefined)
|
||||||
.filter(s => s["application.process.id"] !== audioPid);
|
.filter(s => s["application.process.id"] !== audioPid);
|
||||||
|
|
||||||
return targets ? { ok: true, targets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
|
return targets ? { ok: true, targets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
|
||||||
|
|
|
@ -254,7 +254,13 @@ function AudioSettingsModal({
|
||||||
</Switch>
|
</Switch>
|
||||||
<Switch
|
<Switch
|
||||||
hideBorder
|
hideBorder
|
||||||
onChange={v => (Settings.audio = { ...Settings.audio, ignoreDevices: v })}
|
onChange={v =>
|
||||||
|
(Settings.audio = {
|
||||||
|
...Settings.audio,
|
||||||
|
ignoreDevices: v,
|
||||||
|
deviceSelect: v ? false : Settings.audio?.deviceSelect
|
||||||
|
})
|
||||||
|
}
|
||||||
value={Settings.audio?.ignoreDevices ?? true}
|
value={Settings.audio?.ignoreDevices ?? true}
|
||||||
note={<>Exclude device nodes, such as nodes belonging to microphones or speakers.</>}
|
note={<>Exclude device nodes, such as nodes belonging to microphones or speakers.</>}
|
||||||
>
|
>
|
||||||
|
@ -271,6 +277,23 @@ function AudioSettingsModal({
|
||||||
>
|
>
|
||||||
Granular Selection
|
Granular Selection
|
||||||
</Switch>
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={value => {
|
||||||
|
Settings.audio = { ...Settings.audio, deviceSelect: value };
|
||||||
|
setAudioSources("None");
|
||||||
|
}}
|
||||||
|
value={Settings.audio?.deviceSelect ?? false}
|
||||||
|
disabled={Settings.audio?.ignoreDevices}
|
||||||
|
note={
|
||||||
|
<>
|
||||||
|
Allow to select devices such as microphones. Requires <b>Ignore Devices</b> to be turned
|
||||||
|
off.
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Device Selection
|
||||||
|
</Switch>
|
||||||
</Modals.ModalContent>
|
</Modals.ModalContent>
|
||||||
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
||||||
<Button color={Button.Colors.TRANSPARENT} onClick={close}>
|
<Button color={Button.Colors.TRANSPARENT} onClick={close}>
|
||||||
|
@ -423,6 +446,7 @@ function StreamSettings({
|
||||||
openSettings={openSettings}
|
openSettings={openSettings}
|
||||||
includeSources={settings.includeSources}
|
includeSources={settings.includeSources}
|
||||||
excludeSources={settings.excludeSources}
|
excludeSources={settings.excludeSources}
|
||||||
|
deviceSelect={Settings.audio?.deviceSelect}
|
||||||
granularSelect={Settings.audio?.granularSelect}
|
granularSelect={Settings.audio?.granularSelect}
|
||||||
setIncludeSources={sources => setSettings(s => ({ ...s, includeSources: sources }))}
|
setIncludeSources={sources => setSettings(s => ({ ...s, includeSources: sources }))}
|
||||||
setExcludeSources={sources => setSettings(s => ({ ...s, excludeSources: sources }))}
|
setExcludeSources={sources => setSettings(s => ({ ...s, excludeSources: sources }))}
|
||||||
|
@ -441,13 +465,23 @@ function hasMatchingProps(value: Node, other: Node) {
|
||||||
return Object.keys(value).every(key => value[key] === other[key]);
|
return Object.keys(value).every(key => value[key] === other[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[] {
|
function mapToAudioItem(node: AudioSource, granularSelect?: boolean, deviceSelect?: boolean): AudioItem[] {
|
||||||
if (isSpecialSource(node)) {
|
if (isSpecialSource(node)) {
|
||||||
return [{ name: node, value: node }];
|
return [{ name: node, value: node }];
|
||||||
}
|
}
|
||||||
|
|
||||||
const rtn: AudioItem[] = [];
|
const rtn: AudioItem[] = [];
|
||||||
|
|
||||||
|
const mediaClass = node["media.class"];
|
||||||
|
|
||||||
|
if (mediaClass?.includes("Video") || mediaClass?.includes("Midi")) {
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deviceSelect && node["device.id"]) {
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
const name = node["application.name"];
|
const name = node["application.name"];
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
|
@ -458,9 +492,15 @@ function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const binary = node["application.process.binary"];
|
const rawName = node["node.name"];
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
|
rtn.push({ name: rawName, value: { "node.name": rawName } });
|
||||||
|
}
|
||||||
|
|
||||||
|
const binary = node["application.process.binary"];
|
||||||
|
|
||||||
|
if (!name && binary) {
|
||||||
rtn.push({ name: binary, value: { "application.process.binary": binary } });
|
rtn.push({ name: binary, value: { "application.process.binary": binary } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,10 +509,12 @@ function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[
|
||||||
const first = rtn[0];
|
const first = rtn[0];
|
||||||
const firstValues = first.value as Node;
|
const firstValues = first.value as Node;
|
||||||
|
|
||||||
|
if (pid) {
|
||||||
rtn.push({
|
rtn.push({
|
||||||
name: `${first.name} (${pid})`,
|
name: `${first.name} (${pid})`,
|
||||||
value: { ...firstValues, "application.process.id": pid }
|
value: { ...firstValues, "application.process.id": pid }
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const mediaName = node["media.name"];
|
const mediaName = node["media.name"];
|
||||||
|
|
||||||
|
@ -483,16 +525,12 @@ function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const mediaClass = node["media.class"];
|
if (mediaClass) {
|
||||||
|
|
||||||
if (!mediaClass) {
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
rtn.push({
|
rtn.push({
|
||||||
name: `${first.name} [${mediaClass}]`,
|
name: `${first.name} [${mediaClass}]`,
|
||||||
value: { ...firstValues, "media.class": mediaClass }
|
value: { ...firstValues, "media.class": mediaClass }
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
@ -535,6 +573,7 @@ function updateItems(setSources: (s: AudioSources) => void, sources?: AudioSourc
|
||||||
function AudioSourcePickerLinux({
|
function AudioSourcePickerLinux({
|
||||||
includeSources,
|
includeSources,
|
||||||
excludeSources,
|
excludeSources,
|
||||||
|
deviceSelect,
|
||||||
granularSelect,
|
granularSelect,
|
||||||
openSettings,
|
openSettings,
|
||||||
setIncludeSources,
|
setIncludeSources,
|
||||||
|
@ -542,6 +581,7 @@ function AudioSourcePickerLinux({
|
||||||
}: {
|
}: {
|
||||||
includeSources?: AudioSources;
|
includeSources?: AudioSources;
|
||||||
excludeSources?: AudioSources;
|
excludeSources?: AudioSources;
|
||||||
|
deviceSelect?: boolean;
|
||||||
granularSelect?: boolean;
|
granularSelect?: boolean;
|
||||||
openSettings: () => void;
|
openSettings: () => void;
|
||||||
setIncludeSources: (s: AudioSources) => void;
|
setIncludeSources: (s: AudioSources) => void;
|
||||||
|
@ -592,7 +632,7 @@ function AudioSourcePickerLinux({
|
||||||
|
|
||||||
const allSources = sources.ok
|
const allSources = sources.ok
|
||||||
? [...specialSources, ...sources.targets]
|
? [...specialSources, ...sources.targets]
|
||||||
.map(target => mapToAudioItem(target, granularSelect))
|
.map(target => mapToAudioItem(target, granularSelect, deviceSelect))
|
||||||
.flat()
|
.flat()
|
||||||
.filter(uniqueName)
|
.filter(uniqueName)
|
||||||
: [];
|
: [];
|
||||||
|
|
2
src/shared/settings.d.ts
vendored
2
src/shared/settings.d.ts
vendored
|
@ -30,6 +30,8 @@ export interface Settings {
|
||||||
|
|
||||||
audio?: {
|
audio?: {
|
||||||
workaround?: boolean;
|
workaround?: boolean;
|
||||||
|
|
||||||
|
deviceSelect?: boolean;
|
||||||
granularSelect?: boolean;
|
granularSelect?: boolean;
|
||||||
|
|
||||||
ignoreVirtual?: boolean;
|
ignoreVirtual?: boolean;
|
||||||
|
|
Loading…
Reference in a new issue