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 (
{i18n.Messages.FORM_LABEL_AUTOMATIC_VAD}
); } } renderSlider() { const {auto, threshold, speaking} = this.props; if (auto) { return (
{i18n.Messages.FORM_HELP_AUTOMATIC_VAD}
); } else { return (
); } } renderInputDisabledWarning() { if (!MediaEngineStore.isEnabled()) { return ( {i18n.Messages.FORM_WARNING_INPUT_SENSITIVTY.format({onEnableClick: AudioActionCreators.enable})} ); } } render() { return ( {i18n.Messages.FORM_LABEL_INPUT_SENSITIVTY}
{this.renderAutomaticVADToggle()} {this.renderSlider()}
{this.renderInputDisabledWarning()}
); } } 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 ( ); }); } 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 (

{i18n.Messages.RESET_VOICE_SETTINGS_BODY}

); }); } handleSubsystemChanged() { ModalActionCreators.push(props => { return (

{i18n.Messages.SWITCH_SUBSYSTEM_BODY}

); }); } 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 = ( {i18n.Messages.BROWSER_INPUT_DEVICE_WARNING.format({ onDownloadClick: this.handleDownload.bind(this, 'Help Text Input Devices'), })} ); } let outputWarning; if (!canSetOutputDevice) { outputWarning = ( {i18n.Messages.BROWSER_OUTPUT_DEVICE_WARNING.format({ onDownloadClick: this.handleDownload.bind(this, 'Help Text Output Devices'), })} ); } const inputDevicesDisabled = lodash(inputDevices).values().first().disabled || inputWarning != null; const outputDevicesDisabled = lodash(outputDevices).values().first().disabled || outputWarning != null; return ( {i18n.Messages.FORM_LABEL_INPUT_DEVICE} ({value, label}))} disabled={outputDevicesDisabled} /> {outputWarning} ); } renderVolumeControls() { const {inputVolume, outputVolume} = this.props; return ( {i18n.Messages.FORM_LABEL_INPUT_VOLUME} {i18n.Messages.FORM_LABEL_OUTPUT_VOLUME} ); } 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 ( ); } 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 = ( {i18n.Messages.PTT_LIMITED_WARNING.format({ onDownloadClick: this.handleDownload.bind(this, 'Help Text PTT'), })} ); } else { secondaryMessage = ( {i18n.Messages.USER_SETTINGS_VOICE_ADD_MULTIPLE.format({ onClick: () => UserSettingsModalActionCreators.setSection(UserSettingsSections.KEYBINDS), })} ); } return (
{secondaryMessage}
); } renderVoiceSensitivityTools() { const {inputMode, vadThreshold, vadAutoThreshold} = this.props; if (inputMode !== InputModes.VOICE_ACTIVITY) { return null; } return ( ); } renderVideoDevices() { const {videoDevices, videoDeviceId} = this.props; let videoWarning; if (!MediaEngineStore.isEnabled()) { videoWarning = ( {i18n.Messages.FORM_WARNING_VIDEO_PREVIEW.format({onEnableClick: AudioActionCreators.enable})} ); } const videoDevicesDisabled = lodash(videoDevices).values().first().disabled || videoWarning != null; const {Camera} = MediaEngineStore.getMediaEngine(); return ( {i18n.Messages.FORM_LABEL_VIDEO_DEVICE} ); } } renderVideoSettings() { if (this.props.videoSettings) { return (
{i18n.Messages.VIDEO_SETTINGS} {this.renderVideoDevices()}
); } return null; } render() { return ( {this.renderDevices()} {this.renderVolumeControls()} {this.renderInputMode()} {this.renderPTTTools()} {this.renderVoiceSensitivityTools()} {this.renderVideoSettings()} {i18n.Messages.SETTINGS_ADVANCED} {this.renderCodec()} {this.renderMediaEngineImplGroup()} {this.renderVoiceProcessing()} {this.renderQoS()} {this.renderAttenutation()} {this.renderSubsystemSettings()} {this.renderDiagnostics()} {this.renderResetVoiceSettings()} ); } } 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