799 lines
25 KiB
JavaScript
Executable file
799 lines
25 KiB
JavaScript
Executable file
import React from 'react';
|
|
import Flux from '../../lib/flux';
|
|
import {Timeout} from '../../lib/timers';
|
|
import classNames from 'classnames';
|
|
import {MediaEngineFeatures, supportedMediaEngineImpl} from '../../lib/MediaEngine';
|
|
import MediaEngineStore from '../../stores/MediaEngineStore';
|
|
import SpeakingStore from '../../stores/SpeakingStore';
|
|
import UserSettingsStore from '../../stores/UserSettingsStore';
|
|
import VideoCallExperimentStore from '../../stores/experiments/VideoCallExperimentStore';
|
|
import ExperimentStore from '../../stores/ExperimentStore';
|
|
import AudioActionCreators from '../../actions/AudioActionCreators';
|
|
import ModalActionCreators from '../../actions/ModalActionCreators';
|
|
import UserSettingsModalActionCreators from '../../actions/UserSettingsModalActionCreators';
|
|
import DownloadApps from '../DownloadApps';
|
|
import NativeUtils from '../../utils/NativeUtils';
|
|
import i18n from '../../i18n';
|
|
import lodash from 'lodash';
|
|
import FormSection from '../../uikit/form/FormSection';
|
|
import FormNotice, {Types as FormNoticeTypes} from '../../uikit/form/FormNotice';
|
|
import Flex from '../../uikit/Flex';
|
|
import FormItem from '../../uikit/form/FormItem';
|
|
import FormDivider from '../../uikit/form/FormDivider';
|
|
import FormText from '../../uikit/form/FormText';
|
|
import Switch from '../../uikit/Switch';
|
|
import Select from '../../uikit/Select';
|
|
import Slider from '../../uikit/Slider';
|
|
import RadioGroup from '../../uikit/RadioGroup';
|
|
import KeyRecorder from '../common/KeyRecorder';
|
|
import SwitchItem from '../../uikit/SwitchItem';
|
|
import FormTitle, {Tags} from '../../uikit/form/FormTitle';
|
|
import Button, {ButtonLooks, ButtonColors, ButtonSizes} from '../../uikit/Button';
|
|
import Alert from '../Alert';
|
|
import ConfirmModal from '../ConfirmModal';
|
|
import {InputModes, MAX_PTT_RELEASE_DELAY, ThemeTypes, UserSettingsSections, ExperimentTypes} from '../../Constants';
|
|
import '../../styles/user_settings_voice.styl';
|
|
|
|
class VoiceSensitivity extends React.PureComponent {
|
|
_timeout = new Timeout();
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
volume: -100,
|
|
};
|
|
|
|
lodash.bindAll(this, [
|
|
'setupVoiceActivity',
|
|
'handleVoiceActivity',
|
|
'handleAutoThresholdChange',
|
|
'handleSensitivityChange',
|
|
'handleValueRender',
|
|
]);
|
|
}
|
|
|
|
componentDidMount() {
|
|
// Delay voice activity to allow many required rendering to take place.
|
|
this._timeout.start(1000, this.setupVoiceActivity);
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
MediaEngineStore.getMediaEngine().removeListener('voiceactivity', this.handleVoiceActivity);
|
|
this._timeout.stop();
|
|
}
|
|
|
|
setupVoiceActivity() {
|
|
MediaEngineStore.getMediaEngine().on('voiceactivity', this.handleVoiceActivity);
|
|
this._timeout.stop();
|
|
}
|
|
|
|
handleVoiceActivity(volume) {
|
|
this.setState({volume});
|
|
}
|
|
|
|
handleAutoThresholdChange(e) {
|
|
const {onThresholdChange, threshold} = this.props;
|
|
onThresholdChange && onThresholdChange(threshold, e.target.checked);
|
|
}
|
|
|
|
handleSensitivityChange(value) {
|
|
const {onThresholdChange, auto} = this.props;
|
|
onThresholdChange && onThresholdChange((100 - value) * -1, auto);
|
|
}
|
|
|
|
handleValueRender(v) {
|
|
return `${((100 - v) * -1).toFixed(0)}dB`;
|
|
}
|
|
|
|
renderAutomaticVADToggle() {
|
|
const {auto} = this.props;
|
|
|
|
if (MediaEngineStore.supports(MediaEngineFeatures.AUTOMATIC_VAD)) {
|
|
return (
|
|
<div className="flex-horizontal margin-bottom-4">
|
|
<FormTitle tag={Tags.H3} className="margin-reset">{i18n.Messages.FORM_LABEL_AUTOMATIC_VAD}</FormTitle>
|
|
<Switch value={auto} onChange={this.handleAutoThresholdChange} />
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
renderSlider() {
|
|
const {auto, threshold, speaking} = this.props;
|
|
|
|
if (auto) {
|
|
return (
|
|
<section className="input-sensitivity-toggle">
|
|
<div className="ui-slider">
|
|
<div className={classNames('slider-bar', {speaking})} />
|
|
</div>
|
|
<FormText type={FormText.Types.DESCRIPTION} className="input-sensitivity-note margin-bottom-8">
|
|
{i18n.Messages.FORM_HELP_AUTOMATIC_VAD}
|
|
</FormText>
|
|
</section>
|
|
);
|
|
} else {
|
|
return (
|
|
<section className="input-sensitivity-toggle manual">
|
|
<Slider
|
|
defaultValue={threshold + 100}
|
|
onValueRender={this.handleValueRender}
|
|
onValueChange={this.handleSensitivityChange}>
|
|
<div className="slider-bar microphone">
|
|
<div className="fill" style={{width: this.state.volume + 100 + '%'}} />
|
|
<div className="grow" />
|
|
</div>
|
|
</Slider>
|
|
</section>
|
|
);
|
|
}
|
|
}
|
|
|
|
renderInputDisabledWarning() {
|
|
if (!MediaEngineStore.isEnabled()) {
|
|
return (
|
|
<FormText type={FormText.Types.DESCRIPTION} className="input-disabled-warning margin-bottom-8">
|
|
{i18n.Messages.FORM_WARNING_INPUT_SENSITIVTY.format({onEnableClick: AudioActionCreators.enable})}
|
|
</FormText>
|
|
);
|
|
}
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<FormItem className="sensitivity">
|
|
<FormTitle tag={Tags.H5} className="margin-bottom-8">{i18n.Messages.FORM_LABEL_INPUT_SENSITIVTY}</FormTitle>
|
|
<div>
|
|
{this.renderAutomaticVADToggle()}
|
|
{this.renderSlider()}
|
|
</div>
|
|
{this.renderInputDisabledWarning()}
|
|
</FormItem>
|
|
);
|
|
}
|
|
}
|
|
|
|
const VoiceSensitivityConnected = Flux.connectStores([SpeakingStore], () => {
|
|
return {
|
|
speaking: SpeakingStore.isCurrentUserSpeaking(),
|
|
};
|
|
})(VoiceSensitivity);
|
|
|
|
class UserSettingsVoice extends React.PureComponent {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
lodash.bindAll(this, [
|
|
'handleInputDeviceChange',
|
|
'handleOutputDeviceChange',
|
|
'handleInputVolumeChange',
|
|
'handleOutputVolumeChange',
|
|
'handleOutputRender',
|
|
'handleInputModeChange',
|
|
'handleDelayChange',
|
|
'handleThresholdChange',
|
|
'handleInputModeChange',
|
|
'handleVideoDeviceChange',
|
|
'handleEchoCancellationChange',
|
|
'handleNoiseSuppressionChange',
|
|
'handleAutomaticGainControlChange',
|
|
'handleSilenceWarningChange',
|
|
'handleQoSChanged',
|
|
'handleMediaEngineChange',
|
|
'handleAttenuationChange',
|
|
'handleAttenuateSelfChanged',
|
|
'handleAttenuateOthersChanged',
|
|
'handleVoiceReset',
|
|
'handleSubsystemChanged',
|
|
'handleShortcutChange',
|
|
]);
|
|
}
|
|
|
|
handleInputDeviceChange({value}) {
|
|
AudioActionCreators.setInputDevice(value);
|
|
}
|
|
|
|
handleOutputDeviceChange({value}) {
|
|
AudioActionCreators.setOutputDevice(value);
|
|
}
|
|
|
|
handleDownload(source) {
|
|
ModalActionCreators.push(DownloadApps, {source});
|
|
}
|
|
|
|
handleInputVolumeChange(inputVolume) {
|
|
AudioActionCreators.setInputVolume(inputVolume);
|
|
}
|
|
|
|
handleOutputVolumeChange(value) {
|
|
const outputVolume = value * 2;
|
|
AudioActionCreators.setOutputVolume(outputVolume);
|
|
}
|
|
|
|
handleOutputRender(v) {
|
|
return `${(v * 2).toFixed(0)}%`;
|
|
}
|
|
|
|
handleInputModeChange({value: inputMode}) {
|
|
if (inputMode === InputModes.PUSH_TO_TALK && this.props.usePTTLimited) {
|
|
ModalActionCreators.push(props => {
|
|
return (
|
|
<Alert
|
|
title={i18n.Messages.PTT_LIMITED_TITLE}
|
|
confirmText={i18n.Messages.DOWNLOAD}
|
|
cancelText={i18n.Messages.OKAY}
|
|
onConfirm={this.handleDownload.bind(this, 'PTT Limited Modal')}
|
|
body={i18n.Messages.PTT_LIMITED_BODY}
|
|
iconUrl={require('../../images/push_to_talk.svg')}
|
|
{...props}
|
|
/>
|
|
);
|
|
});
|
|
}
|
|
|
|
this.setMode(inputMode);
|
|
}
|
|
|
|
handleDelayChange(delay) {
|
|
this.setMode(this.props.inputMode, {delay});
|
|
}
|
|
|
|
handleThresholdChange(threshold, autoThreshold) {
|
|
this.setMode(this.props.inputMode, {threshold, autoThreshold});
|
|
}
|
|
|
|
handleDelayValueRender(delay) {
|
|
if (delay >= 1000) {
|
|
delay /= 1000;
|
|
return `${delay.toFixed(2)}s`;
|
|
}
|
|
return `${delay.toFixed(0)} ms`;
|
|
}
|
|
|
|
handleVideoDeviceChange({value}) {
|
|
AudioActionCreators.setVideoDevice(value);
|
|
}
|
|
|
|
handleEchoCancellationChange(e) {
|
|
AudioActionCreators.setEchoCancellation(e.currentTarget.checked);
|
|
}
|
|
|
|
handleNoiseSuppressionChange(e) {
|
|
AudioActionCreators.setNoiseSuppression(e.currentTarget.checked);
|
|
}
|
|
|
|
handleAutomaticGainControlChange(e) {
|
|
AudioActionCreators.setAutomaticGainControl(e.currentTarget.checked);
|
|
}
|
|
|
|
handleSilenceWarningChange(e) {
|
|
AudioActionCreators.setSilenceWarning(e.currentTarget.checked);
|
|
}
|
|
|
|
handleQoSChanged(e) {
|
|
AudioActionCreators.setQoS(e.currentTarget.checked);
|
|
}
|
|
|
|
handleMediaEngineChange({value}) {
|
|
AudioActionCreators.setMediaEngine(value);
|
|
}
|
|
|
|
handleAttenuationChange(attenuation) {
|
|
AudioActionCreators.setAttenuation(
|
|
attenuation,
|
|
this.props.attenuateWhileSpeakingSelf,
|
|
this.props.attenuateWhileSpeakingOthers
|
|
);
|
|
}
|
|
|
|
handleAttenuateSelfChanged(e) {
|
|
AudioActionCreators.setAttenuation(
|
|
this.props.attenuation,
|
|
e.currentTarget.checked,
|
|
this.props.attenuateWhileSpeakingOthers
|
|
);
|
|
}
|
|
|
|
handleAttenuateOthersChanged(e) {
|
|
AudioActionCreators.setAttenuation(
|
|
this.props.attenuation,
|
|
this.props.attenuateWhileSpeakingSelf,
|
|
e.currentTarget.checked
|
|
);
|
|
}
|
|
|
|
handleVoiceReset() {
|
|
ModalActionCreators.push(props => {
|
|
return (
|
|
<ConfirmModal
|
|
header={i18n.Messages.RESET_VOICE_SETTINGS}
|
|
confirmText={i18n.Messages.OKAY}
|
|
cancelText={i18n.Messages.CANCEL}
|
|
onConfirm={AudioActionCreators.reset}
|
|
red={true}
|
|
{...props}>
|
|
<p>{i18n.Messages.RESET_VOICE_SETTINGS_BODY}</p>
|
|
</ConfirmModal>
|
|
);
|
|
});
|
|
}
|
|
|
|
handleSubsystemChanged() {
|
|
ModalActionCreators.push(props => {
|
|
return (
|
|
<ConfirmModal
|
|
header={i18n.Messages.SWITCH_SUBSYSTEM}
|
|
confirmText={i18n.Messages.OKAY}
|
|
cancelText={i18n.Messages.CANCEL}
|
|
onConfirm={AudioActionCreators.switchSubsystem}
|
|
red={true}
|
|
{...props}>
|
|
<p>{i18n.Messages.SWITCH_SUBSYSTEM_BODY}</p>
|
|
</ConfirmModal>
|
|
);
|
|
});
|
|
}
|
|
|
|
handleShortcutChange(shortcut) {
|
|
this.setMode(this.props.inputMode, {shortcut});
|
|
}
|
|
|
|
setMode(mode, options = {}) {
|
|
const {vadThreshold, vadAutoThreshold, shortcut, delay} = this.props;
|
|
AudioActionCreators.setMode(mode, {
|
|
threshold: vadThreshold,
|
|
autoThreshold: vadAutoThreshold,
|
|
shortcut,
|
|
delay,
|
|
...options,
|
|
});
|
|
}
|
|
|
|
renderDevices() {
|
|
const {
|
|
inputDevices,
|
|
inputDeviceId,
|
|
outputDeviceId,
|
|
outputDevices,
|
|
canSetInputDevice,
|
|
canSetOutputDevice,
|
|
} = this.props;
|
|
|
|
let inputWarning;
|
|
if (!canSetInputDevice) {
|
|
inputWarning = (
|
|
<FormText type={FormText.Types.DESCRIPTION} className="margin-top-8">
|
|
{i18n.Messages.BROWSER_INPUT_DEVICE_WARNING.format({
|
|
onDownloadClick: this.handleDownload.bind(this, 'Help Text Input Devices'),
|
|
})}
|
|
</FormText>
|
|
);
|
|
}
|
|
let outputWarning;
|
|
if (!canSetOutputDevice) {
|
|
outputWarning = (
|
|
<FormText type={FormText.Types.DESCRIPTION} className="margin-top-8">
|
|
{i18n.Messages.BROWSER_OUTPUT_DEVICE_WARNING.format({
|
|
onDownloadClick: this.handleDownload.bind(this, 'Help Text Output Devices'),
|
|
})}
|
|
</FormText>
|
|
);
|
|
}
|
|
const inputDevicesDisabled = lodash(inputDevices).values().first().disabled || inputWarning != null;
|
|
const outputDevicesDisabled = lodash(outputDevices).values().first().disabled || outputWarning != null;
|
|
return (
|
|
<Flex className="margin-bottom-20">
|
|
<Flex.Child>
|
|
<FormTitle tag={Tags.H5} className="margin-bottom-8">{i18n.Messages.FORM_LABEL_INPUT_DEVICE}</FormTitle>
|
|
<Select
|
|
value={inputDeviceId}
|
|
clearable={false}
|
|
searchable={false}
|
|
onChange={this.handleInputDeviceChange}
|
|
options={lodash.map(inputDevices, ({id: value, name: label}) => ({value, label}))}
|
|
disabled={inputDevicesDisabled}
|
|
/>
|
|
{inputWarning}
|
|
</Flex.Child>
|
|
<Flex.Child>
|
|
<FormTitle tag={Tags.H5} className="margin-bottom-8">{i18n.Messages.FORM_LABEL_OUTPUT_DEVICE}</FormTitle>
|
|
<Select
|
|
value={outputDeviceId}
|
|
clearable={false}
|
|
searchable={false}
|
|
onChange={this.handleOutputDeviceChange}
|
|
options={lodash.map(outputDevices, ({id: value, name: label}) => ({value, label}))}
|
|
disabled={outputDevicesDisabled}
|
|
/>
|
|
{outputWarning}
|
|
</Flex.Child>
|
|
</Flex>
|
|
);
|
|
}
|
|
|
|
renderVolumeControls() {
|
|
const {inputVolume, outputVolume} = this.props;
|
|
|
|
return (
|
|
<Flex className="volume">
|
|
<Flex.Child>
|
|
<FormTitle tag={Tags.H5} className="margin-bottom-4">{i18n.Messages.FORM_LABEL_INPUT_VOLUME}</FormTitle>
|
|
<Slider defaultValue={inputVolume} asValueChanges={this.handleInputVolumeChange} />
|
|
</Flex.Child>
|
|
<Flex.Child>
|
|
<FormTitle tag={Tags.H5} className="margin-bottom-4">{i18n.Messages.FORM_LABEL_OUTPUT_VOLUME}</FormTitle>
|
|
<Slider
|
|
defaultValue={outputVolume / 2}
|
|
onValueRender={this.handleOutputRender}
|
|
asValueChanges={this.handleOutputVolumeChange}
|
|
/>
|
|
</Flex.Child>
|
|
</Flex>
|
|
);
|
|
}
|
|
|
|
renderInputMode() {
|
|
const {usePTTLimited, inputMode} = this.props;
|
|
|
|
const inputModes = [
|
|
{
|
|
value: InputModes.VOICE_ACTIVITY,
|
|
name: i18n.Messages.INPUT_MODE_VAD,
|
|
},
|
|
{
|
|
value: InputModes.PUSH_TO_TALK,
|
|
name: usePTTLimited ? i18n.Messages.INPUT_MODE_PTT_LIMITED : i18n.Messages.INPUT_MODE_PTT,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<FormItem title={i18n.Messages.FORM_LABEL_INPUT_MODE} className="margin-bottom-20">
|
|
<RadioGroup onChange={this.handleInputModeChange} options={inputModes} value={inputMode} />
|
|
</FormItem>
|
|
);
|
|
}
|
|
|
|
renderPTTTools() {
|
|
const {inputMode, delay, shortcut} = this.props;
|
|
if (inputMode !== InputModes.PUSH_TO_TALK) {
|
|
return null;
|
|
}
|
|
|
|
let secondaryMessage;
|
|
if (!NativeUtils.embedded && inputMode === InputModes.PUSH_TO_TALK) {
|
|
secondaryMessage = (
|
|
<FormText type={FormText.Types.DESCRIPTION} className="ptt-tools-message ptt-tools-warning margin-bottom-8">
|
|
{i18n.Messages.PTT_LIMITED_WARNING.format({
|
|
onDownloadClick: this.handleDownload.bind(this, 'Help Text PTT'),
|
|
})}
|
|
</FormText>
|
|
);
|
|
} else {
|
|
secondaryMessage = (
|
|
<FormText type={FormText.Types.DESCRIPTION} className="ptt-tools-message margin-bottom-8">
|
|
{i18n.Messages.USER_SETTINGS_VOICE_ADD_MULTIPLE.format({
|
|
onClick: () => UserSettingsModalActionCreators.setSection(UserSettingsSections.KEYBINDS),
|
|
})}
|
|
</FormText>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="ptt-tools">
|
|
<div className="flex-horizontal">
|
|
<FormItem title={i18n.Messages.FORM_LABEL_SHORTCUT}>
|
|
<KeyRecorder defaultValue={shortcut} onChange={this.handleShortcutChange} />
|
|
</FormItem>
|
|
<FormItem title={i18n.Messages.INPUT_MODE_PTT_RELEASE_DELAY}>
|
|
<Slider
|
|
defaultValue={delay}
|
|
onValueChange={this.handleDelayChange}
|
|
onValueRender={this.handleDelayValueRender}
|
|
maxValue={MAX_PTT_RELEASE_DELAY}
|
|
/>
|
|
</FormItem>
|
|
</div>
|
|
{secondaryMessage}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
renderVoiceSensitivityTools() {
|
|
const {inputMode, vadThreshold, vadAutoThreshold} = this.props;
|
|
|
|
if (inputMode !== InputModes.VOICE_ACTIVITY) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<VoiceSensitivityConnected
|
|
inputMode={inputMode}
|
|
threshold={vadThreshold}
|
|
auto={vadAutoThreshold}
|
|
onThresholdChange={this.handleThresholdChange}
|
|
/>
|
|
);
|
|
}
|
|
|
|
renderVideoDevices() {
|
|
const {videoDevices, videoDeviceId} = this.props;
|
|
|
|
let videoWarning;
|
|
if (!MediaEngineStore.isEnabled()) {
|
|
videoWarning = (
|
|
<FormText type={FormText.Types.DESCRIPTION} className="margin-top-8">
|
|
{i18n.Messages.FORM_WARNING_VIDEO_PREVIEW.format({onEnableClick: AudioActionCreators.enable})}
|
|
</FormText>
|
|
);
|
|
}
|
|
|
|
const videoDevicesDisabled = lodash(videoDevices).values().first().disabled || videoWarning != null;
|
|
|
|
const {Camera} = MediaEngineStore.getMediaEngine();
|
|
|
|
return (
|
|
<Flex>
|
|
<Flex.Child basis="50%">
|
|
<FormTitle tag={Tags.H5} className="margin-bottom-8">{i18n.Messages.FORM_LABEL_VIDEO_DEVICE}</FormTitle>
|
|
<Select
|
|
value={videoDeviceId}
|
|
clearable={false}
|
|
searchable={false}
|
|
disabled={videoDevicesDisabled}
|
|
onChange={this.handleVideoDeviceChange}
|
|
options={lodash.map(videoDevices, ({id: value, name: label}) => ({value, label}))}
|
|
/>
|
|
{videoWarning}
|
|
</Flex.Child>
|
|
<Flex.Child basis="50%">
|
|
<FormTitle tag={Tags.H5} className="margin-bottom-8">{i18n.Messages.FORM_LABEL_VIDEO_PREVIEW}</FormTitle>
|
|
<Camera deviceId={videoDeviceId} disabled={videoDevicesDisabled} />
|
|
</Flex.Child>
|
|
</Flex>
|
|
);
|
|
}
|
|
|
|
renderCodec() {
|
|
const {theme} = this.props;
|
|
let src;
|
|
if (theme === ThemeTypes.LIGHT) {
|
|
src = require('../../images/user_settings/img_opus_logo_light.png');
|
|
} else {
|
|
src = require('../../images/user_settings/img_opus_logo_dark.png');
|
|
}
|
|
|
|
return (
|
|
<FormNotice
|
|
className="margin-bottom-40"
|
|
type={FormNoticeTypes.PRIMARY}
|
|
imageData={{
|
|
src,
|
|
width: 70,
|
|
height: 40,
|
|
}}
|
|
title={i18n.Messages.USER_SETTINGS_VOICE_CODEC_TITLE}
|
|
body={i18n.Messages.USER_SETTINGS_VOICE_CODEC_DESCRIPTION}
|
|
/>
|
|
);
|
|
}
|
|
|
|
renderVoiceProcessing() {
|
|
if (!MediaEngineStore.supports(MediaEngineFeatures.VOICE_PROCESSING)) {
|
|
return null;
|
|
}
|
|
const {noiseSuppression, echoCancellation, automaticGainControl} = this.props;
|
|
|
|
return (
|
|
<FormSection className="margin-bottom-40" title={i18n.Messages.FORM_LABEL_VOICE_PROCESSING}>
|
|
<SwitchItem
|
|
className="margin-top-8 margin-bottom-20"
|
|
value={echoCancellation}
|
|
onChange={this.handleEchoCancellationChange}>
|
|
{i18n.Messages.ECHO_CANCELLATION}
|
|
</SwitchItem>
|
|
<SwitchItem value={noiseSuppression} onChange={this.handleNoiseSuppressionChange}>
|
|
{i18n.Messages.NOISE_SUPPRESSION}
|
|
</SwitchItem>
|
|
<SwitchItem className="" value={automaticGainControl} onChange={this.handleAutomaticGainControlChange}>
|
|
{i18n.Messages.AUTOMATIC_GAIN_CONTROL}
|
|
</SwitchItem>
|
|
</FormSection>
|
|
);
|
|
}
|
|
|
|
renderQoS() {
|
|
if (!MediaEngineStore.supports(MediaEngineFeatures.QOS)) {
|
|
return null;
|
|
}
|
|
const {qosEnabled} = this.props;
|
|
return (
|
|
<FormSection className="margin-bottom-40" title={i18n.Messages.FORM_LABEL_QOS}>
|
|
<SwitchItem className="" value={qosEnabled} onChange={this.handleQoSChanged} note={i18n.Messages.FORM_HELP_QOS}>
|
|
{i18n.Messages.FORM_CHECKBOX_QOS}
|
|
</SwitchItem>
|
|
</FormSection>
|
|
);
|
|
}
|
|
|
|
renderAttenutation() {
|
|
if (!MediaEngineStore.supports(MediaEngineFeatures.ATTENUATION)) {
|
|
return null;
|
|
}
|
|
|
|
const {attenuation, attenuateWhileSpeakingSelf, attenuateWhileSpeakingOthers} = this.props;
|
|
|
|
return (
|
|
<FormSection className="margin-bottom-40" title={i18n.Messages.FORM_LABEL_ATTENUATION}>
|
|
<Slider
|
|
defaultValue={attenuation}
|
|
onValueRender={this.handleOutputRender}
|
|
onValueChange={this.handleAttenuationChange}
|
|
/>
|
|
<FormText className="margin-bottom-20" type={FormText.Types.DESCRIPTION}>
|
|
{i18n.Messages.FORM_HELP_ATTENUATION}
|
|
</FormText>
|
|
<FormDivider lineOnly className="margin-bottom-20" />
|
|
<SwitchItem value={attenuateWhileSpeakingSelf} onChange={this.handleAttenuateSelfChanged}>
|
|
{i18n.Messages.ATTENUATE_WHILE_SPEAKING_SELF}
|
|
</SwitchItem>
|
|
<SwitchItem className="" value={attenuateWhileSpeakingOthers} onChange={this.handleAttenuateOthersChanged}>
|
|
{i18n.Messages.ATTENUATE_WHILE_SPEAKING_OTHERS}
|
|
</SwitchItem>
|
|
</FormSection>
|
|
);
|
|
}
|
|
|
|
renderSubsystemSettings() {
|
|
if (!MediaEngineStore.supports(MediaEngineFeatures.LEGACY_SUBSYSTEM)) {
|
|
return null;
|
|
}
|
|
const {legacySubsystemEnabled} = this.props;
|
|
return (
|
|
<FormSection className="margin-bottom-40" title={i18n.Messages.FORM_LABEL_SUBSYSTEM}>
|
|
<SwitchItem
|
|
className=""
|
|
value={legacySubsystemEnabled}
|
|
onChange={this.handleSubsystemChanged}
|
|
note={i18n.Messages.FORM_HELP_LEGACY_SUBSYSTEM}>
|
|
{i18n.Messages.FORM_CHECKBOX_LEGACY_SUBSYSTEM}
|
|
</SwitchItem>
|
|
</FormSection>
|
|
);
|
|
}
|
|
|
|
renderDiagnostics() {
|
|
if (!NativeUtils.embedded) {
|
|
return null;
|
|
}
|
|
const {silenceWarning} = this.props;
|
|
return (
|
|
<FormSection className="margin-bottom-40" title={i18n.Messages.FORM_LABEL_VOICE_DIAGNOSTICS}>
|
|
<SwitchItem className="" value={silenceWarning} onChange={this.handleSilenceWarningChange}>
|
|
{i18n.Messages.DISPLAY_SILENCE_WARNING}
|
|
</SwitchItem>
|
|
</FormSection>
|
|
);
|
|
}
|
|
|
|
renderResetVoiceSettings() {
|
|
return (
|
|
<FormItem>
|
|
<Button
|
|
className="reset-button"
|
|
look={ButtonLooks.OUTLINED}
|
|
color={ButtonColors.RED}
|
|
onClick={this.handleVoiceReset}
|
|
size={ButtonSizes.SMALL}>
|
|
{i18n.Messages.RESET_VOICE_SETTINGS}
|
|
</Button>
|
|
</FormItem>
|
|
);
|
|
}
|
|
|
|
renderMediaEngineImplGroup() {
|
|
if (process.env.NODE_ENV === 'development') {
|
|
const options = [];
|
|
for (const [impl, supported] of Object.entries(supportedMediaEngineImpl)) {
|
|
if (supported) {
|
|
options.push({
|
|
value: impl,
|
|
label: impl,
|
|
});
|
|
}
|
|
}
|
|
|
|
return (
|
|
<FormSection className="margin-bottom-40">
|
|
<FormTitle>{i18n.Messages.FORM_LABEL_MEDIA_ENGINE}</FormTitle>
|
|
<Select
|
|
value={this.props.mediaEngineImpl}
|
|
clearable={false}
|
|
searchable={false}
|
|
options={options}
|
|
onChange={this.handleMediaEngineChange}
|
|
/>
|
|
</FormSection>
|
|
);
|
|
}
|
|
}
|
|
|
|
renderVideoSettings() {
|
|
if (this.props.videoSettings) {
|
|
return (
|
|
<div>
|
|
<FormTitle tag={Tags.H2} className="margin-bottom-20">{i18n.Messages.VIDEO_SETTINGS}</FormTitle>
|
|
{this.renderVideoDevices()}
|
|
<FormDivider className="margin-bottom-40 margin-top-40" />
|
|
</div>
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<FormSection className="user-settings-voice" tag={Tags.H2} title={i18n.Messages.VOICE_SETTINGS}>
|
|
{this.renderDevices()}
|
|
{this.renderVolumeControls()}
|
|
<FormDivider className="margin-top-8 margin-bottom-40" />
|
|
{this.renderInputMode()}
|
|
{this.renderPTTTools()}
|
|
{this.renderVoiceSensitivityTools()}
|
|
<FormDivider className="margin-bottom-40" />
|
|
{this.renderVideoSettings()}
|
|
<FormTitle tag={Tags.H2} className="margin-bottom-20">{i18n.Messages.SETTINGS_ADVANCED}</FormTitle>
|
|
{this.renderCodec()}
|
|
{this.renderMediaEngineImplGroup()}
|
|
{this.renderVoiceProcessing()}
|
|
{this.renderQoS()}
|
|
{this.renderAttenutation()}
|
|
{this.renderSubsystemSettings()}
|
|
{this.renderDiagnostics()}
|
|
{this.renderResetVoiceSettings()}
|
|
</FormSection>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default Flux.connectStores([UserSettingsStore, MediaEngineStore, VideoCallExperimentStore], () => {
|
|
let videoSettings = false;
|
|
const override = ExperimentStore.getOverrideExperimentDescriptor(VideoCallExperimentStore.getExperimentId());
|
|
if ((override != null && override.type === ExperimentTypes.DEVELOPER) || VideoCallExperimentStore.hasVideoCall()) {
|
|
videoSettings = true;
|
|
}
|
|
return {
|
|
theme: UserSettingsStore.theme,
|
|
canSetInputDevice: MediaEngineStore.supports(MediaEngineFeatures.AUDIO_INPUT_DEVICE),
|
|
canSetOutputDevice: MediaEngineStore.supports(MediaEngineFeatures.AUDIO_OUTPUT_DEVICE),
|
|
inputVolume: MediaEngineStore.getInputVolume(),
|
|
outputVolume: MediaEngineStore.getOutputVolume(),
|
|
inputDeviceId: MediaEngineStore.getInputDeviceId(),
|
|
inputDevices: MediaEngineStore.getInputDevices(),
|
|
outputDevices: MediaEngineStore.getOutputDevices(),
|
|
outputDeviceId: MediaEngineStore.getOutputDeviceId(),
|
|
videoDevices: MediaEngineStore.getVideoDevices(),
|
|
videoDeviceId: MediaEngineStore.getVideoDeviceId(),
|
|
echoCancellation: MediaEngineStore.getEchoCancellation(),
|
|
noiseSuppression: MediaEngineStore.getNoiseSuppression(),
|
|
automaticGainControl: MediaEngineStore.getAutomaticGainControl(),
|
|
inputMode: MediaEngineStore.getMode(),
|
|
shortcut: MediaEngineStore.getModeOptions().shortcut,
|
|
vadThreshold: MediaEngineStore.getModeOptions().threshold,
|
|
vadAutoThreshold: MediaEngineStore.getModeOptions().autoThreshold,
|
|
delay: MediaEngineStore.getModeOptions().delay,
|
|
usePTTLimited: !NativeUtils.embedded,
|
|
attenuation: MediaEngineStore.getAttenuation(),
|
|
attenuateWhileSpeakingSelf: MediaEngineStore.getAttenuateWhileSpeakingSelf(),
|
|
attenuateWhileSpeakingOthers: MediaEngineStore.getAttenuateWhileSpeakingOthers(),
|
|
silenceWarning: MediaEngineStore.getEnableSilenceWarning(),
|
|
qosEnabled: MediaEngineStore.getQoS(),
|
|
legacySubsystemEnabled: MediaEngineStore.isUsingLegacySubsystem(),
|
|
mediaEngineImpl: MediaEngineStore.getMediaEngineImpl(),
|
|
videoSettings,
|
|
};
|
|
})(UserSettingsVoice);
|
|
|
|
|
|
|
|
// WEBPACK FOOTER //
|
|
// ./discord_app/components/user_settings/UserSettingsVoice.js
|