diff --git a/app/public/config/config.example.js b/app/public/config/config.example.js index b591c16..9ee5e37 100644 --- a/app/public/config/config.example.js +++ b/app/public/config/config.example.js @@ -44,13 +44,14 @@ var config = }, defaultAudio : { - sampleRate : 48000, - channelCount : 1, - volume : 1.0, - autoGainControl : true, - echoCancellation : true, - noiseSuppression : true, - sampleSize : 16 + sampleRate : 48000, + channelCount : 1, + volume : 1.0, + autoGainControl : true, + echoCancellation : true, + noiseSuppression : true, + voiceActivityMute : false, + sampleSize : 16 }, background : 'images/background.jpg', defaultLayout : 'democratic', // democratic, filmstrip diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index df276ae..9fece61 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -980,21 +980,11 @@ export default class RoomClient if (!this._harkStream.getAudioTracks()[0]) throw new Error('getMicStream():something went wrong with hark'); - this._hark = hark(this._harkStream, { play: false }); + this._hark = hark(this._harkStream, { play: false, interval: 5 }); // eslint-disable-next-line no-unused-vars - this._hark.on('volume_change', (dBs, threshold) => + this._hark.on('volume_change', (volume, threshold) => { - // The exact formula to convert from dBs (-100..0) to linear (0..1) is: - // Math.pow(10, dBs / 20) - // However it does not produce a visually useful output, so let exagerate - // it a bit. Also, let convert it from 0..1 to 0..10 and avoid value 1 to - // minimize component renderings. - let volume = Math.round(Math.pow(10, dBs / 85) * 10); - - if (volume === 1) - volume = 0; - volume = Math.round(volume); if (this._micProducer && volume !== this._micProducer.volume) @@ -1004,13 +994,23 @@ export default class RoomClient store.dispatch(peerVolumeActions.setPeerVolume(this._peerId, volume)); } }); - this._hark.on('speaking', function() + this._hark.on('speaking', () => { + this._hark.setInterval(300); store.dispatch(meActions.setIsSpeaking(true)); + if (store.getState().settings.voiceActivatedUnmute && this._micProducer.paused) + { + this.unmuteMic(); + } }); - this._hark.on('stopped_speaking', function() + this._hark.on('stopped_speaking', () => { store.dispatch(meActions.setIsSpeaking(false)); + if (store.getState().settings.voiceActivatedUnmute && !this._micProducer.paused) + { + this.muteMic(); + } + this._hark.setInterval(5); }); } @@ -1856,23 +1856,12 @@ export default class RoomClient producerPaused } = request.data; - let codecOptions; - - if (kind === 'audio') - { - codecOptions = - { - opusStereo : 1 - }; - } - const consumer = await this._recvTransport.consume( { id, producerId, kind, rtpParameters, - codecOptions, appData : { ...appData, peerId } // Trick. }); @@ -3760,6 +3749,14 @@ export default class RoomClient store.dispatch(meActions.setWebcamInProgress(false)); } + async _setNoiseThreshold(threshold) + { + logger.debug('_setNoiseThreshold:%s', threshold); + this._hark.setThreshold(threshold); + store.dispatch( + settingsActions.setNoiseThreshold(threshold)); + } + async _updateAudioDevices() { logger.debug('_updateAudioDevices()'); diff --git a/app/src/actions/settingsActions.js b/app/src/actions/settingsActions.js index 63c12bf..f168ac3 100644 --- a/app/src/actions/settingsActions.js +++ b/app/src/actions/settingsActions.js @@ -61,6 +61,18 @@ export const setNoiseSuppression = (noiseSuppression) => payload : { noiseSuppression } }); +export const setVoiceActivatedUnmute = (voiceActivatedUnmute) => + ({ + type: 'SET_VOICE_ACTIVATED_UNMUTE', + payload: { voiceActivatedUnmute } + }); + +export const setNoiseThreshold = (noiseThreshold) => + ({ + type: 'SET_NOISE_THRESHOLD', + payload: { noiseThreshold } + }); + export const setDefaultAudio = (audio) => ({ type : 'SET_DEFAULT_AUDIO', diff --git a/app/src/components/Containers/Volume.js b/app/src/components/Containers/Volume.js index 3c13a39..14d9d5d 100644 --- a/app/src/components/Containers/Volume.js +++ b/app/src/components/Containers/Volume.js @@ -149,9 +149,16 @@ const makeMapStateToProps = (initialState, props) => { const mapStateToProps = (state) => { + if (state.peerVolumes[props.id]>state.settings.noiseThreshold) + { return { - volume : state.peerVolumes[props.id] + volume : Math.round((state.peerVolumes[props.id]+100) / 10) }; + } + else + { + return { volume: 0 }; + } }; return mapStateToProps; diff --git a/app/src/components/Settings/MediaSettings.js b/app/src/components/Settings/MediaSettings.js index d215181..334b93f 100644 --- a/app/src/components/Settings/MediaSettings.js +++ b/app/src/components/Settings/MediaSettings.js @@ -6,31 +6,66 @@ import { withRoomContext } from '../../RoomContext'; import * as settingsActions from '../../actions/settingsActions'; import PropTypes from 'prop-types'; import { useIntl, FormattedMessage } from 'react-intl'; +import classnames from 'classnames'; import MenuItem from '@material-ui/core/MenuItem'; import FormHelperText from '@material-ui/core/FormHelperText'; import FormControl from '@material-ui/core/FormControl'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Select from '@material-ui/core/Select'; import Checkbox from '@material-ui/core/Checkbox'; +import Slider from '@material-ui/core/Slider'; +import Typography from '@material-ui/core/Typography'; -const styles = (theme) => - ({ - setting : +const NoiseSlider = withStyles( + { + root : { - padding : theme.spacing(2) + color : '#3880ff', + height : 2, + padding : '15px 0' }, - formControl : - { - display : 'flex' + track : { + height : 2 + }, + rail : { + height : 2, + opacity : 0.2, + }, + mark : { + backgroundColor : '#bfbfbf', + height : 10, + width : 3, + marginTop : -3 + }, + markActive : { + opacity : 1, + backgroundColor : 'currentColor' } - }); + })(Slider); + +const styles = (theme) => ({ + setting : + { + padding : theme.spacing(2) + }, + margin : + { + height : theme.spacing(3), + }, + formControl : + { + display : 'flex' + } +}); const MediaSettings = ({ setEchoCancellation, setAutoGainControl, setNoiseSuppression, + setVoiceActivatedUnmute, roomClient, me, + volume, settings, classes }) => @@ -135,6 +170,32 @@ const MediaSettings = ({ } + + + + + +
@@ -148,7 +209,7 @@ const MediaSettings = ({ displayEmpty name={intl.formatMessage({ id : 'settings.audio', - defaultMessage : 'Audio device' + defaultMessage : 'Audio input device' })} autoWidth className={classes.selectEmpty} @@ -165,12 +226,12 @@ const MediaSettings = ({ { audioDevices.length > 0 ? intl.formatMessage({ id : 'settings.selectAudio', - defaultMessage : 'Select audio device' + defaultMessage : 'Select audio input device' }) : intl.formatMessage({ id : 'settings.cantSelectAudio', - defaultMessage : 'Unable to select audio device' + defaultMessage : 'Unable to select audio input device' }) } @@ -225,34 +286,6 @@ const MediaSettings = ({ }
- - - - - - + + { + setVoiceActivatedUnmute(event.target.checked); + }} + />} + label={intl.formatMessage({ + id : 'settings.voiceActivatedUnmute', + defaultMessage : 'Voice activated unmute' + })} + /> +
+ + { + intl.formatMessage({ + id : 'settings.noiseThreshold', + defaultMessage : 'Noise threshold' + }) + } + + { + roomClient._setNoiseThreshold(value); + }} + marks={[{ value: volume, label: 'level' }]} valueLabelDisplay='on' + /> +
); @@ -305,27 +373,31 @@ const MediaSettings = ({ MediaSettings.propTypes = { - roomClient : PropTypes.any.isRequired, - setEchoCancellation : PropTypes.func.isRequired, - setAutoGainControl : PropTypes.func.isRequired, - setNoiseSuppression : PropTypes.func.isRequired, - me : appPropTypes.Me.isRequired, - settings : PropTypes.object.isRequired, - classes : PropTypes.object.isRequired + roomClient : PropTypes.any.isRequired, + setEchoCancellation : PropTypes.func.isRequired, + setAutoGainControl : PropTypes.func.isRequired, + setNoiseSuppression : PropTypes.func.isRequired, + setVoiceActivatedUnmute : PropTypes.func.isRequired, + me : appPropTypes.Me.isRequired, + volume : PropTypes.number, + settings : PropTypes.object.isRequired, + classes : PropTypes.object.isRequired }; const mapStateToProps = (state) => { return { me : state.me, + volume : state.peerVolumes[state.me.id], settings : state.settings }; }; const mapDispatchToProps = { - setEchoCancellation : settingsActions.setEchoCancellation, - setAutoGainControl : settingsActions.toggleAutoGainControl, - setNoiseSuppression : settingsActions.toggleNoiseSuppression + setEchoCancellation : settingsActions.setEchoCancellation, + setAutoGainControl : settingsActions.toggleAutoGainControl, + setNoiseSuppression : settingsActions.toggleNoiseSuppression, + setVoiceActivatedUnmute : settingsActions.setVoiceActivatedUnmute }; export default withRoomContext(connect( @@ -337,7 +409,8 @@ export default withRoomContext(connect( { return ( prev.me === next.me && - prev.settings === next.settings + prev.settings === next.settings && + prev.peerVolumes[prev.me.id] === next[next.me.id] ); } } diff --git a/app/src/reducers/peerVolumes.js b/app/src/reducers/peerVolumes.js index fafe739..cd5032f 100644 --- a/app/src/reducers/peerVolumes.js +++ b/app/src/reducers/peerVolumes.js @@ -31,9 +31,10 @@ const peerVolumes = (state = initialState, action) => case 'SET_PEER_VOLUME': { - const { peerId, volume } = action.payload; + const { peerId } = action.payload; + const dBs = action.payload.volume; - return { ...state, [peerId]: volume }; + return { ...state, [peerId]: Math.round(dBs) }; } default: diff --git a/app/src/reducers/settings.js b/app/src/reducers/settings.js index 7186fdc..cee3d88 100644 --- a/app/src/reducers/settings.js +++ b/app/src/reducers/settings.js @@ -1,23 +1,25 @@ const initialState = { - displayName : 'Guest', - selectedWebcam : null, - selectedAudioDevice : null, - advancedMode : false, - sampleRate : 48000, - channelCount : 1, - volume : 1.0, - autoGainControl : true, - echoCancellation : true, - noiseSuppression : true, - sampleSize : 16, + displayName : 'Guest', + selectedWebcam : null, + selectedAudioDevice : null, + advancedMode : false, + sampleRate : 48000, + channelCount : 1, + volume : 1.0, + autoGainControl : false, + echoCancellation : true, + noiseSuppression : true, + voiceActivatedUnmute : false, + noiseThreshold : -50, + sampleSize : 16, // low, medium, high, veryhigh, ultra - resolution : window.config.defaultResolution || 'medium', - lastN : 4, - permanentTopBar : true, - hiddenControls : false, - showNotifications : true, - notificationSounds : true, + resolution : window.config.defaultResolution || 'medium', + lastN : 4, + permanentTopBar : true, + hiddenControls : false, + showNotifications : true, + notificationSounds : true, ...window.config.defaultAudio }; @@ -96,6 +98,20 @@ const settings = (state = initialState, action) => return { ...state, noiseSuppression }; } + case 'SET_VOICE_ACTIVATED_UNMUTE': + { + const { voiceActivatedUnmute } = action.payload; + + return { ...state, voiceActivatedUnmute }; + } + + case 'SET_NOISE_THRESHOLD': + { + const { noiseThreshold } = action.payload; + + return { ...state, noiseThreshold }; + } + case 'SET_DEFAULT_AUDIO': { const { audio } = action.payload;