331 lines
12 KiB
TypeScript
331 lines
12 KiB
TypeScript
/*
|
|
* Vencord, a modification for Discord's desktop app
|
|
* Copyright (c) 2023 Vendicated and contributors
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import { Flex } from "@components/Flex";
|
|
import { Switch } from "@components/Switch";
|
|
import { ModalSize } from "@utils/modal";
|
|
import { Card, Forms, Select, Slider, TextInput, useEffect, useState } from "@webpack/common";
|
|
import { SelectOption } from "@webpack/types";
|
|
|
|
import {
|
|
ProfilableStore,
|
|
SettingsModal,
|
|
SettingsModalCard,
|
|
SettingsModalCardItem,
|
|
SettingsModalCardRow,
|
|
SettingsModalProfilesCard,
|
|
validateNumberInput,
|
|
validateTextInputNumber
|
|
} from "../../philsPluginLibrary";
|
|
import { Styles } from "../../philsPluginLibrary/styles";
|
|
import { MicrophoneProfile, MicrophoneStore } from "../stores";
|
|
|
|
const simpleVoiceBitrates: readonly SelectOption[] = [
|
|
{
|
|
label: "Normal",
|
|
value: 96
|
|
},
|
|
{
|
|
label: "Medium-High",
|
|
value: 160
|
|
},
|
|
{
|
|
label: "High",
|
|
value: 320
|
|
},
|
|
{
|
|
label: "Very-High",
|
|
value: 512
|
|
}
|
|
] as const;
|
|
|
|
export interface MicrophoneSettingsModalProps extends React.ComponentProps<typeof SettingsModal> {
|
|
microphoneStore: ProfilableStore<MicrophoneStore, MicrophoneProfile>;
|
|
showInfo?: boolean;
|
|
}
|
|
|
|
export const MicrophoneSettingsModal = (props: MicrophoneSettingsModalProps) => {
|
|
const { microphoneStore, showInfo } = props;
|
|
|
|
const {
|
|
currentProfile,
|
|
simpleMode,
|
|
setSimpleMode,
|
|
deleteProfile,
|
|
duplicateProfile,
|
|
getCurrentProfile,
|
|
getDefaultProfiles,
|
|
getProfile,
|
|
getProfiles,
|
|
isCurrentProfileADefaultProfile,
|
|
profiles,
|
|
saveProfile,
|
|
setChannels,
|
|
setChannelsEnabled,
|
|
setCurrentProfile,
|
|
setFreq,
|
|
setFreqEnabled,
|
|
setPacsize,
|
|
setPacsizeEnabled,
|
|
setRate,
|
|
setRateEnabled,
|
|
setVoiceBitrate,
|
|
setVoiceBitrateEnabled
|
|
} = microphoneStore.use();
|
|
|
|
const {
|
|
name,
|
|
channels,
|
|
channelsEnabled,
|
|
freq,
|
|
freqEnabled,
|
|
pacsize,
|
|
pacsizeEnabled,
|
|
rate,
|
|
rateEnabled,
|
|
voiceBitrate,
|
|
voiceBitrateEnabled
|
|
} = currentProfile;
|
|
|
|
const [isSaving, setIsSaving] = useState(false);
|
|
|
|
const [rateInput, setRateInput] = useState<string>(rate ? rate.toString() : "");
|
|
const [freqInput, setFreqInput] = useState<string>(freq ? freq.toString() : "");
|
|
const [pacsizeInput, setPacsizeInput] = useState<string>(pacsize ? pacsize.toString() : "");
|
|
const [channelsInput, setChannelsInput] = useState<string>(channels ? channels.toString() : "");
|
|
|
|
useEffect(() => {
|
|
setRateInput(rate ? rate.toString() : "");
|
|
setFreqInput(freq ? freq.toString() : "");
|
|
setPacsizeInput(pacsize ? pacsize.toString() : "");
|
|
setChannelsInput(channels ? channels.toString() : "");
|
|
}, [rate, freq, pacsize, channels]);
|
|
|
|
const simpleToggle =
|
|
<Flex style={{ justifyContent: "center", alignItems: "center", gap: "0.6em" }}>
|
|
<Forms.FormTitle style={{ margin: 0 }} tag="h5">Simple</Forms.FormTitle>
|
|
<Switch checked={simpleMode ?? false} disabled={isSaving} onChange={checked => setSimpleMode(checked)} />
|
|
</Flex>;
|
|
|
|
const settingsCardVoiceBitrateSimple =
|
|
<SettingsModalCard
|
|
title="Audio Bitrate"
|
|
switchEnabled
|
|
flex={0.8}
|
|
switchProps={{
|
|
checked: voiceBitrateEnabled ?? false,
|
|
disabled: isSaving,
|
|
onChange: status => setVoiceBitrateEnabled(status)
|
|
}}>
|
|
<SettingsModalCardItem>
|
|
<Select
|
|
isDisabled={!voiceBitrateEnabled || isSaving}
|
|
options={simpleVoiceBitrates}
|
|
select={(value: number) => setVoiceBitrate(value)}
|
|
isSelected={(value: number) => value === voiceBitrate}
|
|
serialize={() => ""} />
|
|
</SettingsModalCardItem>
|
|
</SettingsModalCard>;
|
|
|
|
const settingsCardChannelsSimple =
|
|
<SettingsModalCard
|
|
title="Stereo"
|
|
flex={0.2}
|
|
switchEnabled
|
|
switchProps={{
|
|
checked: (channelsEnabled && channels === 2) ?? false,
|
|
disabled: isSaving,
|
|
onChange: status => void setChannelsEnabled(status) ?? setChannels(2)
|
|
}}>
|
|
</SettingsModalCard>;
|
|
|
|
const settingsCardVoiceBitrate =
|
|
<SettingsModalCard
|
|
title="Audio Bitrate"
|
|
switchEnabled
|
|
flex={0.4}
|
|
switchProps={{
|
|
checked: voiceBitrateEnabled ?? false,
|
|
disabled: isSaving,
|
|
onChange: status => setVoiceBitrateEnabled(status)
|
|
}}>
|
|
<SettingsModalCardItem title="Kb/s">
|
|
<div style={{ paddingTop: "0.3em", paddingRight: "0.4em", paddingLeft: "0.4em", boxSizing: "border-box" }}>
|
|
<Slider
|
|
disabled={!voiceBitrateEnabled || isSaving}
|
|
onValueChange={value => setVoiceBitrate(value)}
|
|
initialValue={voiceBitrate || 8}
|
|
minValue={8}
|
|
maxValue={512}
|
|
markers={[8, 96, 320, 512]}
|
|
onValueRender={value => `${value.toFixed(0)}kb/s`} />
|
|
</div>
|
|
</SettingsModalCardItem>
|
|
</SettingsModalCard>;
|
|
|
|
const settingsCardRate =
|
|
<SettingsModalCard
|
|
title="Sample Rate"
|
|
switchEnabled
|
|
switchProps={{
|
|
checked: rateEnabled ?? false,
|
|
disabled: isSaving,
|
|
onChange: status => setRateEnabled(status)
|
|
}}>
|
|
<SettingsModalCardItem>
|
|
<TextInput
|
|
disabled={!rateEnabled || isSaving}
|
|
value={rateInput}
|
|
onChange={value => validateTextInputNumber(value) && setRateInput(value)}
|
|
onBlur={e => {
|
|
const result = validateNumberInput(e.target.value);
|
|
setRate(result);
|
|
setRateInput(result ? result.toString() : "");
|
|
}} />
|
|
</SettingsModalCardItem>
|
|
</SettingsModalCard>;
|
|
|
|
const settingsCardFreq =
|
|
<SettingsModalCard
|
|
title="Sample Frequency"
|
|
switchEnabled
|
|
switchProps={{
|
|
checked: freqEnabled ?? false,
|
|
disabled: isSaving,
|
|
onChange: status => setFreqEnabled(status)
|
|
}}>
|
|
<SettingsModalCardItem>
|
|
<TextInput
|
|
disabled={!freqEnabled || isSaving}
|
|
value={freqInput}
|
|
onChange={value => validateTextInputNumber(value) && setFreqInput(value)}
|
|
onBlur={e => {
|
|
const result = validateNumberInput(e.target.value);
|
|
setFreq(result);
|
|
setFreqInput(result ? result.toString() : "");
|
|
}} />
|
|
</SettingsModalCardItem>
|
|
</SettingsModalCard>;
|
|
|
|
const settingsCardPacsize =
|
|
<SettingsModalCard
|
|
title="Pac Size"
|
|
switchEnabled
|
|
switchProps={{
|
|
checked: pacsizeEnabled ?? false,
|
|
disabled: isSaving,
|
|
onChange: status => setPacsizeEnabled(status)
|
|
}}>
|
|
<SettingsModalCardItem>
|
|
<TextInput
|
|
disabled={!pacsizeEnabled || isSaving}
|
|
value={pacsizeInput}
|
|
onChange={value => validateTextInputNumber(value) && setPacsizeInput(value)}
|
|
onBlur={e => {
|
|
const result = validateNumberInput(e.target.value);
|
|
setPacsize(result);
|
|
setPacsizeInput(result ? result.toString() : "");
|
|
}} />
|
|
</SettingsModalCardItem>
|
|
</SettingsModalCard>;
|
|
|
|
const settingsCardChannels =
|
|
<SettingsModalCard
|
|
title="Channels"
|
|
switchEnabled
|
|
switchProps={{
|
|
checked: channelsEnabled ?? false,
|
|
disabled: isSaving,
|
|
onChange: status => setChannelsEnabled(status)
|
|
}}>
|
|
<SettingsModalCardItem>
|
|
<TextInput
|
|
disabled={!channelsEnabled || isSaving}
|
|
value={channelsInput}
|
|
onChange={value => validateTextInputNumber(value) && setChannelsInput(value)}
|
|
onBlur={e => {
|
|
const result = validateNumberInput(e.target.value);
|
|
setChannels(result);
|
|
setChannelsInput(result ? result.toString() : "");
|
|
}} />
|
|
</SettingsModalCardItem>
|
|
</SettingsModalCard>;
|
|
|
|
const settingsCardProfiles =
|
|
<SettingsModalProfilesCard
|
|
flex={0.6}
|
|
onSaveStateChanged={state => setIsSaving(state)}
|
|
profileableStore={microphoneStore} />;
|
|
|
|
const infoCard =
|
|
<Card style={{ ...Styles.infoCard }}>
|
|
<Forms.FormTitle tag="h5">Important</Forms.FormTitle>
|
|
<Forms.FormText>
|
|
To take full advantage of this plugin, please disable <span style={{ fontWeight: "bold" }}>Krisp</span> and <span style={{ fontWeight: "bold" }}>Echo Cancellation</span>, otherwise features like Stereo (Channels) will not work.
|
|
</Forms.FormText>
|
|
</Card>;
|
|
|
|
return (
|
|
<SettingsModal
|
|
size={simpleMode ? ModalSize.DYNAMIC : ModalSize.DYNAMIC}
|
|
title="Microphone Settings"
|
|
closeButtonName="Apply"
|
|
footerContent={
|
|
<Flex style={{ justifyContent: "center", alignItems: "center", marginLeft: "auto" }}>
|
|
{simpleToggle}
|
|
</Flex>
|
|
}
|
|
{...props}
|
|
onDone={() => {
|
|
props.onClose();
|
|
props.onDone && props.onDone();
|
|
}}
|
|
>
|
|
{simpleMode
|
|
? <div style={{ width: "30em", display: "flex", flexDirection: "column", gap: "1em" }}>
|
|
<SettingsModalCardRow>
|
|
{settingsCardVoiceBitrateSimple}
|
|
{settingsCardChannelsSimple}
|
|
</SettingsModalCardRow>
|
|
{showInfo &&
|
|
<SettingsModalCardRow>
|
|
{infoCard}
|
|
</SettingsModalCardRow>
|
|
}
|
|
</div>
|
|
: <div style={{ display: "flex", flexDirection: "column", width: "50em", gap: "1em", maxHeight: "30em" }}>
|
|
<SettingsModalCardRow>
|
|
{settingsCardFreq}
|
|
{settingsCardRate}
|
|
{settingsCardPacsize}
|
|
{settingsCardChannels}
|
|
</SettingsModalCardRow>
|
|
<SettingsModalCardRow>
|
|
{settingsCardVoiceBitrate}
|
|
{settingsCardProfiles}
|
|
</SettingsModalCardRow>
|
|
{showInfo &&
|
|
<SettingsModalCardRow>
|
|
{infoCard}
|
|
</SettingsModalCardRow>
|
|
}
|
|
</div>
|
|
}
|
|
</SettingsModal>
|
|
);
|
|
};
|