First working noiseThreshold setting + voiceActivatedUnmute

auto_join_3.3
Stefan Otto 2020-05-14 04:02:52 +02:00
parent 457d679382
commit 534e862573
7 changed files with 209 additions and 102 deletions

View File

@ -50,6 +50,7 @@ var config =
autoGainControl : true,
echoCancellation : true,
noiseSuppression : true,
voiceActivityMute : false,
sampleSize : 16
},
background : 'images/background.jpg',

View File

@ -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()');

View File

@ -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',

View File

@ -148,10 +148,17 @@ Volume.propTypes =
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;

View File

@ -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) =>
({
const NoiseSlider = withStyles(
{
root :
{
color : '#3880ff',
height : 2,
padding : '15px 0'
},
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 = ({
}
</FormHelperText>
</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 className={classes.setting} autoComplete='off'>
<FormControl className={classes.formControl}>
@ -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'
})
}
</FormHelperText>
@ -225,34 +286,6 @@ const MediaSettings = ({
</form>
}
<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
className={classes.setting}
control={
@ -298,6 +331,41 @@ const MediaSettings = ({
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>
</React.Fragment>
);
@ -309,7 +377,9 @@ MediaSettings.propTypes =
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
};
@ -318,6 +388,7 @@ const mapStateToProps = (state) =>
{
return {
me : state.me,
volume : state.peerVolumes[state.me.id],
settings : state.settings
};
};
@ -325,7 +396,8 @@ const mapStateToProps = (state) =>
const mapDispatchToProps = {
setEchoCancellation : settingsActions.setEchoCancellation,
setAutoGainControl : settingsActions.toggleAutoGainControl,
setNoiseSuppression : settingsActions.toggleNoiseSuppression
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]
);
}
}

View File

@ -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:

View File

@ -7,9 +7,11 @@ const initialState =
sampleRate : 48000,
channelCount : 1,
volume : 1.0,
autoGainControl : true,
autoGainControl : false,
echoCancellation : true,
noiseSuppression : true,
voiceActivatedUnmute : false,
noiseThreshold : -50,
sampleSize : 16,
// low, medium, high, veryhigh, ultra
resolution : window.config.defaultResolution || 'medium',
@ -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;