First working noiseThreshold setting + voiceActivatedUnmute
parent
457d679382
commit
534e862573
|
|
@ -44,13 +44,14 @@ var config =
|
||||||
},
|
},
|
||||||
defaultAudio :
|
defaultAudio :
|
||||||
{
|
{
|
||||||
sampleRate : 48000,
|
sampleRate : 48000,
|
||||||
channelCount : 1,
|
channelCount : 1,
|
||||||
volume : 1.0,
|
volume : 1.0,
|
||||||
autoGainControl : true,
|
autoGainControl : true,
|
||||||
echoCancellation : true,
|
echoCancellation : true,
|
||||||
noiseSuppression : true,
|
noiseSuppression : true,
|
||||||
sampleSize : 16
|
voiceActivityMute : false,
|
||||||
|
sampleSize : 16
|
||||||
},
|
},
|
||||||
background : 'images/background.jpg',
|
background : 'images/background.jpg',
|
||||||
defaultLayout : 'democratic', // democratic, filmstrip
|
defaultLayout : 'democratic', // democratic, filmstrip
|
||||||
|
|
|
||||||
|
|
@ -980,21 +980,11 @@ export default class RoomClient
|
||||||
if (!this._harkStream.getAudioTracks()[0])
|
if (!this._harkStream.getAudioTracks()[0])
|
||||||
throw new Error('getMicStream():something went wrong with hark');
|
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
|
// 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);
|
volume = Math.round(volume);
|
||||||
|
|
||||||
if (this._micProducer && volume !== this._micProducer.volume)
|
if (this._micProducer && volume !== this._micProducer.volume)
|
||||||
|
|
@ -1004,13 +994,23 @@ export default class RoomClient
|
||||||
store.dispatch(peerVolumeActions.setPeerVolume(this._peerId, volume));
|
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));
|
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));
|
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
|
producerPaused
|
||||||
} = request.data;
|
} = request.data;
|
||||||
|
|
||||||
let codecOptions;
|
|
||||||
|
|
||||||
if (kind === 'audio')
|
|
||||||
{
|
|
||||||
codecOptions =
|
|
||||||
{
|
|
||||||
opusStereo : 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const consumer = await this._recvTransport.consume(
|
const consumer = await this._recvTransport.consume(
|
||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
producerId,
|
producerId,
|
||||||
kind,
|
kind,
|
||||||
rtpParameters,
|
rtpParameters,
|
||||||
codecOptions,
|
|
||||||
appData : { ...appData, peerId } // Trick.
|
appData : { ...appData, peerId } // Trick.
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3760,6 +3749,14 @@ export default class RoomClient
|
||||||
store.dispatch(meActions.setWebcamInProgress(false));
|
store.dispatch(meActions.setWebcamInProgress(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _setNoiseThreshold(threshold)
|
||||||
|
{
|
||||||
|
logger.debug('_setNoiseThreshold:%s', threshold);
|
||||||
|
this._hark.setThreshold(threshold);
|
||||||
|
store.dispatch(
|
||||||
|
settingsActions.setNoiseThreshold(threshold));
|
||||||
|
}
|
||||||
|
|
||||||
async _updateAudioDevices()
|
async _updateAudioDevices()
|
||||||
{
|
{
|
||||||
logger.debug('_updateAudioDevices()');
|
logger.debug('_updateAudioDevices()');
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,18 @@ export const setNoiseSuppression = (noiseSuppression) =>
|
||||||
payload : { 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) =>
|
export const setDefaultAudio = (audio) =>
|
||||||
({
|
({
|
||||||
type : 'SET_DEFAULT_AUDIO',
|
type : 'SET_DEFAULT_AUDIO',
|
||||||
|
|
|
||||||
|
|
@ -149,9 +149,16 @@ const makeMapStateToProps = (initialState, props) =>
|
||||||
{
|
{
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
|
if (state.peerVolumes[props.id]>state.settings.noiseThreshold)
|
||||||
|
{
|
||||||
return {
|
return {
|
||||||
volume : state.peerVolumes[props.id]
|
volume : Math.round((state.peerVolumes[props.id]+100) / 10)
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return { volume: 0 };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return mapStateToProps;
|
return mapStateToProps;
|
||||||
|
|
|
||||||
|
|
@ -6,31 +6,66 @@ import { withRoomContext } from '../../RoomContext';
|
||||||
import * as settingsActions from '../../actions/settingsActions';
|
import * as settingsActions from '../../actions/settingsActions';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import classnames from 'classnames';
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
import MenuItem from '@material-ui/core/MenuItem';
|
||||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
import FormHelperText from '@material-ui/core/FormHelperText';
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
import FormControl from '@material-ui/core/FormControl';
|
||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||||
import Select from '@material-ui/core/Select';
|
import Select from '@material-ui/core/Select';
|
||||||
import Checkbox from '@material-ui/core/Checkbox';
|
import Checkbox from '@material-ui/core/Checkbox';
|
||||||
|
import Slider from '@material-ui/core/Slider';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
|
||||||
const styles = (theme) =>
|
const NoiseSlider = withStyles(
|
||||||
({
|
{
|
||||||
setting :
|
root :
|
||||||
{
|
{
|
||||||
padding : theme.spacing(2)
|
color : '#3880ff',
|
||||||
|
height : 2,
|
||||||
|
padding : '15px 0'
|
||||||
},
|
},
|
||||||
formControl :
|
track : {
|
||||||
{
|
height : 2
|
||||||
display : 'flex'
|
},
|
||||||
|
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 = ({
|
const MediaSettings = ({
|
||||||
setEchoCancellation,
|
setEchoCancellation,
|
||||||
setAutoGainControl,
|
setAutoGainControl,
|
||||||
setNoiseSuppression,
|
setNoiseSuppression,
|
||||||
|
setVoiceActivatedUnmute,
|
||||||
roomClient,
|
roomClient,
|
||||||
me,
|
me,
|
||||||
|
volume,
|
||||||
settings,
|
settings,
|
||||||
classes
|
classes
|
||||||
}) =>
|
}) =>
|
||||||
|
|
@ -135,6 +170,32 @@ const MediaSettings = ({
|
||||||
}
|
}
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl className={classes.formControl}>
|
||||||
|
<Select
|
||||||
|
value={settings.resolution || ''}
|
||||||
|
onChange={(event) => {
|
||||||
|
if (event.target.value)
|
||||||
|
roomClient.changeVideoResolution(event.target.value);
|
||||||
|
}}
|
||||||
|
name='Video resolution'
|
||||||
|
autoWidth
|
||||||
|
className={classes.selectEmpty}
|
||||||
|
>
|
||||||
|
{resolutions.map((resolution, index) => {
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} value={resolution.value}>
|
||||||
|
{resolution.label}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
<FormattedMessage
|
||||||
|
id='settings.resolution'
|
||||||
|
defaultMessage='Select your video resolution'
|
||||||
|
/>
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
</form>
|
</form>
|
||||||
<form className={classes.setting} autoComplete='off'>
|
<form className={classes.setting} autoComplete='off'>
|
||||||
<FormControl className={classes.formControl}>
|
<FormControl className={classes.formControl}>
|
||||||
|
|
@ -148,7 +209,7 @@ const MediaSettings = ({
|
||||||
displayEmpty
|
displayEmpty
|
||||||
name={intl.formatMessage({
|
name={intl.formatMessage({
|
||||||
id : 'settings.audio',
|
id : 'settings.audio',
|
||||||
defaultMessage : 'Audio device'
|
defaultMessage : 'Audio input device'
|
||||||
})}
|
})}
|
||||||
autoWidth
|
autoWidth
|
||||||
className={classes.selectEmpty}
|
className={classes.selectEmpty}
|
||||||
|
|
@ -165,12 +226,12 @@ const MediaSettings = ({
|
||||||
{ audioDevices.length > 0 ?
|
{ audioDevices.length > 0 ?
|
||||||
intl.formatMessage({
|
intl.formatMessage({
|
||||||
id : 'settings.selectAudio',
|
id : 'settings.selectAudio',
|
||||||
defaultMessage : 'Select audio device'
|
defaultMessage : 'Select audio input device'
|
||||||
})
|
})
|
||||||
:
|
:
|
||||||
intl.formatMessage({
|
intl.formatMessage({
|
||||||
id : 'settings.cantSelectAudio',
|
id : 'settings.cantSelectAudio',
|
||||||
defaultMessage : 'Unable to select audio device'
|
defaultMessage : 'Unable to select audio input device'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
|
|
@ -225,34 +286,6 @@ const MediaSettings = ({
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
<form className={classes.setting} autoComplete='off'>
|
<form className={classes.setting} autoComplete='off'>
|
||||||
<FormControl className={classes.formControl}>
|
|
||||||
<Select
|
|
||||||
value={settings.resolution || ''}
|
|
||||||
onChange={(event) =>
|
|
||||||
{
|
|
||||||
if (event.target.value)
|
|
||||||
roomClient.changeVideoResolution(event.target.value);
|
|
||||||
}}
|
|
||||||
name='Video resolution'
|
|
||||||
autoWidth
|
|
||||||
className={classes.selectEmpty}
|
|
||||||
>
|
|
||||||
{ resolutions.map((resolution, index) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<MenuItem key={index} value={resolution.value}>
|
|
||||||
{resolution.label}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<FormHelperText>
|
|
||||||
<FormattedMessage
|
|
||||||
id='settings.resolution'
|
|
||||||
defaultMessage='Select your video resolution'
|
|
||||||
/>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
className={classes.setting}
|
className={classes.setting}
|
||||||
control={
|
control={
|
||||||
|
|
@ -298,6 +331,41 @@ const MediaSettings = ({
|
||||||
defaultMessage : 'Noise Suppression'
|
defaultMessage : 'Noise Suppression'
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
className={classes.setting}
|
||||||
|
control={
|
||||||
|
<Checkbox checked={settings.voiceActivatedUnmute} onChange={
|
||||||
|
(event) =>
|
||||||
|
{
|
||||||
|
setVoiceActivatedUnmute(event.target.checked);
|
||||||
|
}}
|
||||||
|
/>}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id : 'settings.voiceActivatedUnmute',
|
||||||
|
defaultMessage : 'Voice activated unmute'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<div className={classes.margin} />
|
||||||
|
<Typography gutterBottom>
|
||||||
|
{
|
||||||
|
intl.formatMessage({
|
||||||
|
id : 'settings.noiseThreshold',
|
||||||
|
defaultMessage : 'Noise threshold'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Typography>
|
||||||
|
<NoiseSlider className={classnames(classes.slider, classnames.setting)}
|
||||||
|
key={'noise-threshold-slider'}
|
||||||
|
min={-100}
|
||||||
|
value={settings.noiseThreshold}
|
||||||
|
max={0}
|
||||||
|
onChange={
|
||||||
|
(event, value) => {
|
||||||
|
roomClient._setNoiseThreshold(value);
|
||||||
|
}}
|
||||||
|
marks={[{ value: volume, label: 'level' }]} valueLabelDisplay='on'
|
||||||
|
/>
|
||||||
|
<div className={classes.margin} />
|
||||||
</form>
|
</form>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
@ -305,27 +373,31 @@ const MediaSettings = ({
|
||||||
|
|
||||||
MediaSettings.propTypes =
|
MediaSettings.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
setEchoCancellation : PropTypes.func.isRequired,
|
setEchoCancellation : PropTypes.func.isRequired,
|
||||||
setAutoGainControl : PropTypes.func.isRequired,
|
setAutoGainControl : PropTypes.func.isRequired,
|
||||||
setNoiseSuppression : PropTypes.func.isRequired,
|
setNoiseSuppression : PropTypes.func.isRequired,
|
||||||
me : appPropTypes.Me.isRequired,
|
setVoiceActivatedUnmute : PropTypes.func.isRequired,
|
||||||
settings : PropTypes.object.isRequired,
|
me : appPropTypes.Me.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
volume : PropTypes.number,
|
||||||
|
settings : PropTypes.object.isRequired,
|
||||||
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
me : state.me,
|
me : state.me,
|
||||||
|
volume : state.peerVolumes[state.me.id],
|
||||||
settings : state.settings
|
settings : state.settings
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setEchoCancellation : settingsActions.setEchoCancellation,
|
setEchoCancellation : settingsActions.setEchoCancellation,
|
||||||
setAutoGainControl : settingsActions.toggleAutoGainControl,
|
setAutoGainControl : settingsActions.toggleAutoGainControl,
|
||||||
setNoiseSuppression : settingsActions.toggleNoiseSuppression
|
setNoiseSuppression : settingsActions.toggleNoiseSuppression,
|
||||||
|
setVoiceActivatedUnmute : settingsActions.setVoiceActivatedUnmute
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
|
|
@ -337,7 +409,8 @@ export default withRoomContext(connect(
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.me === next.me &&
|
prev.me === next.me &&
|
||||||
prev.settings === next.settings
|
prev.settings === next.settings &&
|
||||||
|
prev.peerVolumes[prev.me.id] === next[next.me.id]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,10 @@ const peerVolumes = (state = initialState, action) =>
|
||||||
|
|
||||||
case 'SET_PEER_VOLUME':
|
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:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,25 @@
|
||||||
const initialState =
|
const initialState =
|
||||||
{
|
{
|
||||||
displayName : 'Guest',
|
displayName : 'Guest',
|
||||||
selectedWebcam : null,
|
selectedWebcam : null,
|
||||||
selectedAudioDevice : null,
|
selectedAudioDevice : null,
|
||||||
advancedMode : false,
|
advancedMode : false,
|
||||||
sampleRate : 48000,
|
sampleRate : 48000,
|
||||||
channelCount : 1,
|
channelCount : 1,
|
||||||
volume : 1.0,
|
volume : 1.0,
|
||||||
autoGainControl : true,
|
autoGainControl : false,
|
||||||
echoCancellation : true,
|
echoCancellation : true,
|
||||||
noiseSuppression : true,
|
noiseSuppression : true,
|
||||||
sampleSize : 16,
|
voiceActivatedUnmute : false,
|
||||||
|
noiseThreshold : -50,
|
||||||
|
sampleSize : 16,
|
||||||
// low, medium, high, veryhigh, ultra
|
// low, medium, high, veryhigh, ultra
|
||||||
resolution : window.config.defaultResolution || 'medium',
|
resolution : window.config.defaultResolution || 'medium',
|
||||||
lastN : 4,
|
lastN : 4,
|
||||||
permanentTopBar : true,
|
permanentTopBar : true,
|
||||||
hiddenControls : false,
|
hiddenControls : false,
|
||||||
showNotifications : true,
|
showNotifications : true,
|
||||||
notificationSounds : true,
|
notificationSounds : true,
|
||||||
...window.config.defaultAudio
|
...window.config.defaultAudio
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -96,6 +98,20 @@ const settings = (state = initialState, action) =>
|
||||||
return { ...state, noiseSuppression };
|
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':
|
case 'SET_DEFAULT_AUDIO':
|
||||||
{
|
{
|
||||||
const { audio } = action.payload;
|
const { audio } = action.payload;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue