Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8dc28bd
add krisp
olipyskoty Sep 2, 2022
3036db9
krisp implementation
olipyskoty Sep 2, 2022
f01a67f
ui
olipyskoty Sep 2, 2022
d5272d2
update UI
olipyskoty Sep 2, 2022
d1a48d5
add marginRight to MuiSwitch for theme
olipyskoty Sep 7, 2022
57607b1
move krisp state to useappstate
olipyskoty Sep 7, 2022
8495a14
remove css from settings button on desktop
olipyskoty Sep 7, 2022
e37520f
remove some padding from intro container
olipyskoty Sep 7, 2022
2f6ae8a
refactor device selection screen for noise cancellation
olipyskoty Sep 7, 2022
8fca262
add tool tip
olipyskoty Sep 7, 2022
c997e4a
krisp logo and disabling track ui
olipyskoty Sep 7, 2022
7908508
refactor device selection screen
olipyskoty Sep 7, 2022
7bb8e0f
add krisp to device selection dialog
olipyskoty Sep 7, 2022
5f5d13d
use krisp toggle useeffect
olipyskoty Sep 7, 2022
46722e6
remove browser supporession
olipyskoty Sep 7, 2022
9acd452
move toggle back to original spot
olipyskoty Sep 7, 2022
a889b46
polish css
olipyskoty Sep 7, 2022
f778d6b
remove krisp sdk
olipyskoty Sep 7, 2022
d1a286e
move isKrispInstalled to useLocalTracks hook
olipyskoty Sep 12, 2022
8122f4b
remove unnecessary useEffect in useKrispToggle
olipyskoty Sep 12, 2022
e359df8
fix switch button logic
olipyskoty Sep 12, 2022
052b865
fix flicker in UI for unsupported browsers
olipyskoty Sep 13, 2022
e720f8a
VIDEO-11239 Changes to make noise cancellation work.
manjeshbhargav Sep 23, 2022
c443ec6
VIDEO-11239 Updating twilio-video and CHANGELOG.md.
manjeshbhargav Sep 23, 2022
e2fb0e6
VIDEO-11239 UI changes.
manjeshbhargav Sep 23, 2022
f6c940c
VIDEO-11239 Prep for 0.8.0.
manjeshbhargav Sep 27, 2022
0dc7f37
VIDEO-11239 Get current unit tests to pass.
manjeshbhargav Sep 29, 2022
0d31ed7
VIDEO-11239 Updating twilio-video to 2.24.2.
manjeshbhargav Sep 30, 2022
c6bc0a7
Merge branch 'master' into VIDEO-11239-add-krisp
manjeshbhargav Nov 17, 2022
0486ea2
VIDEO-11239 Make Krisp dependency optional.
manjeshbhargav Nov 17, 2022
4acf714
VIDEO-11239 Fixing unit tests.
manjeshbhargav Nov 17, 2022
9e007e9
VIDEO-11239 s/suppression/cancellation/g in CHANGELOG.md.
manjeshbhargav Nov 17, 2022
43d0560
VIDEO-11239 Update README section for noise cancellation.
manjeshbhargav Nov 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ jobs:
- v1-deps-{{ .Branch }}
- v1-deps

- run: npm ci
- run:
name: 'Install Dependencies'
command: |
npm ci
npm run noisecancellation:krisp

- save_cache:
key: v1-deps-{{ .Branch }}-{{ checksum "package-lock.json" }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ yarn-debug.log*
yarn-error.log*

.env
.env.*
.vscode

test-reports
junit.xml
serviceAccountKey.json

public/noisecancellation/
public/virtualbackground/
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
## 0.9.0 (November 22, 2022)

### New Features

- Krisp audio noise cancellation has been added. [#750](https://github.com/twilio/twilio-video-app-react/pull/750)

## 0.8.0 (November 14, 2022)

### New Feature

- This release adds the ability to maintain audio continuity when the default audio input device changes. If the user chooses a specific audio device from the audio settings, then this feature does not apply.

### Dependency Changes

- `twilio-video` has been upgraded from 2.23.0 to 2.25.0.

## 0.7.1 (August 5, 2022)

### Dependency Upgrades
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ Run `npm install` inside the main project folder to install all dependencies fro

If you want to use `yarn` to install dependencies, first run the [yarn import](https://classic.yarnpkg.com/en/docs/cli/import/) command. This will ensure that yarn installs the package versions that are specified in `package-lock.json`.

### Add Noise Cancellation

Twilio Video has partnered with [Krisp Technologies Inc.](https://krisp.ai/) to add [noise cancellation](https://www.twilio.com/docs/video/noise-cancellation) to the local audio track. This feature is licensed under the [Krisp Plugin for Twilio](https://twilio.github.io/krisp-audio-plugin/LICENSE.html). In order to add this feature to your application, please run `npm install noisecancellation:krisp` immediately after the [previous step](#install-dependencies).

## Install Twilio CLI and RTC Plugin

### Install the Twilio CLI
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
},
"scripts": {
"postinstall": "rimraf public/virtualbackground && copyfiles -f node_modules/@twilio/video-processors/dist/build/* public/virtualbackground",
"noisecancellation:krisp": "npm install @twilio/krisp-audio-plugin && rimraf public/noisecancellation && copyfiles -f \"node_modules/@twilio/krisp-audio-plugin/dist/*\" public/noisecancellation && copyfiles -f \"node_modules/@twilio/krisp-audio-plugin/dist/weights/*\" public/noisecancellation/weights",
"start": "concurrently npm:server npm:dev",
"dev": "react-scripts start",
"build": "node ./scripts/build.js",
Expand Down
1 change: 1 addition & 0 deletions server/__tests__/createExpressHandler.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable import/first */
process.env.REACT_APP_TWILIO_ENVIRONMENT = 'prod';
process.env.TWILIO_ACCOUNT_SID = 'mockAccountSid';
process.env.TWILIO_API_KEY_SID = 'mockApiKeySid';
process.env.TWILIO_API_KEY_SECRET = 'mockApiKeySecret';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ export default function AudioInputList() {
const { localTracks } = useVideoContext();

const localAudioTrack = localTracks.find(track => track.kind === 'audio') as LocalAudioTrack;
const srcMediaStreamTrack = localAudioTrack?.noiseCancellation?.sourceTrack;
const mediaStreamTrack = useMediaStreamTrack(localAudioTrack);
const localAudioInputDeviceId = mediaStreamTrack?.getSettings().deviceId;

const localAudioInputDeviceId =
srcMediaStreamTrack?.getSettings().deviceId || mediaStreamTrack?.getSettings().deviceId;
function replaceTrack(newDeviceId: string) {
window.localStorage.setItem(SELECTED_AUDIO_INPUT_KEY, newDeviceId);
localAudioTrack?.restart({ deviceId: { exact: newDeviceId } });
Expand Down
67 changes: 67 additions & 0 deletions src/components/DeviceSelectionDialog/DeviceSelectionDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ import {
Theme,
DialogTitle,
Hidden,
FormControlLabel,
Switch,
Tooltip,
} from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import VideoInputList from './VideoInputList/VideoInputList';
import MaxGalleryViewParticipants from './MaxGalleryViewParticipants/MaxGalleryViewParticipants';
import { useKrispToggle } from '../../hooks/useKrispToggle/useKrispToggle';
import SmallCheckIcon from '../../icons/SmallCheckIcon';
import InfoIconOutlined from '../../icons/InfoIconOutlined';
import KrispLogo from '../../icons/KrispLogo';
import { useAppState } from '../../state';
import useVideoContext from '../../hooks/useVideoContext/useVideoContext';

const useStyles = makeStyles((theme: Theme) => ({
container: {
Expand Down Expand Up @@ -46,9 +55,28 @@ const useStyles = makeStyles((theme: Theme) => ({
margin: '1em 0 2em 0',
},
},
noiseCancellationContainer: {
display: 'flex',
justifyContent: 'space-between',
},
krispContainer: {
display: 'flex',
alignItems: 'center',
'& svg': {
'&:not(:last-child)': {
margin: '0 0.3em',
},
},
},
krispInfoText: {
margin: '0 0 1.5em 0.5em',
},
}));

export default function DeviceSelectionDialog({ open, onClose }: { open: boolean; onClose: () => void }) {
const { isAcquiringLocalTracks } = useVideoContext();
const { isKrispEnabled, isKrispInstalled } = useAppState();
const { toggleKrisp } = useKrispToggle();
const classes = useStyles();

return (
Expand All @@ -67,6 +95,45 @@ export default function DeviceSelectionDialog({ open, onClose }: { open: boolean
<Typography variant="h6" className={classes.headline}>
Audio
</Typography>

{isKrispInstalled && (
<div className={classes.noiseCancellationContainer}>
<div className={classes.krispContainer}>
<Typography variant="subtitle2">Noise Cancellation powered by </Typography>
<KrispLogo />
<Tooltip
title="Suppress background noise from your microphone"
interactive
leaveDelay={250}
leaveTouchDelay={15000}
enterTouchDelay={0}
>
<div>
<InfoIconOutlined />
</div>
</Tooltip>
</div>
<FormControlLabel
control={
<Switch
checked={!!isKrispEnabled}
checkedIcon={<SmallCheckIcon />}
disableRipple={true}
onClick={toggleKrisp}
/>
}
label={isKrispEnabled ? 'Enabled' : 'Disabled'}
style={{ marginRight: 0 }}
disabled={isAcquiringLocalTracks}
/>
</div>
)}
{isKrispInstalled && (
<Typography variant="body1" color="textSecondary" className={classes.krispInfoText}>
Suppress background noise from your microphone.
</Typography>
)}

<AudioInputList />
</div>
<div className={classes.listSection}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/IntroContainer/IntroContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const useStyles = makeStyles((theme: Theme) => ({
content: {
background: 'white',
width: '100%',
padding: '4em',
padding: '3em 4em',
flex: 1,
[theme.breakpoints.down('sm')]: {
padding: '2em',
Expand Down
5 changes: 4 additions & 1 deletion src/components/MenuBar/Menu/Menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import useLocalVideoToggle from '../../../hooks/useLocalVideoToggle/useLocalVide
jest.mock('../../../hooks/useFlipCameraToggle/useFlipCameraToggle');
jest.mock('@material-ui/core/useMediaQuery');
jest.mock('../../../state');
jest.mock('../../../hooks/useVideoContext/useVideoContext', () => () => ({ room: { sid: 'mockRoomSid' } }));
jest.mock('../../../hooks/useVideoContext/useVideoContext', () => () => ({
localTracks: [],
room: { sid: 'mockRoomSid' },
}));
jest.mock('../../../hooks/useIsRecording/useIsRecording');
jest.mock('../../../hooks/useChatContext/useChatContext');
jest.mock('../../../hooks/useLocalVideoToggle/useLocalVideoToggle');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mockUseVideoContext.mockImplementation(() => ({
connect: mockConnect,
isAcquiringLocalTracks: false,
isConnecting: false,
localTracks: [],
}));

describe('the DeviceSelectionScreen component', () => {
Expand All @@ -38,6 +39,7 @@ describe('the DeviceSelectionScreen component', () => {
connect: mockConnect,
isAcquiringLocalTracks: false,
isConnecting: true,
localTracks: [],
}));

const wrapper = shallow(<DeviceSelectionScreen name="test name" roomName="test room name" setStep={() => {}} />);
Expand All @@ -60,6 +62,7 @@ describe('the DeviceSelectionScreen component', () => {
connect: mockConnect,
isAcquiringLocalTracks: true,
isConnecting: false,
localTracks: [],
}));

const wrapper = shallow(<DeviceSelectionScreen name="test name" roomName="test room name" setStep={() => {}} />);
Expand All @@ -82,6 +85,7 @@ describe('the DeviceSelectionScreen component', () => {
connect: mockConnect,
isAcquiringLocalTracks: false,
isConnecting: false,
localTracks: [],
}));
mockUseAppState.mockImplementationOnce(() => ({ getToken: mockGetToken, isFetching: true }));
const wrapper = shallow(<DeviceSelectionScreen name="test name" roomName="test room name" setStep={() => {}} />);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { makeStyles, Typography, Grid, Button, Theme, Hidden } from '@material-ui/core';
import { makeStyles, Typography, Grid, Button, Theme, Hidden, Switch, Tooltip } from '@material-ui/core';
import CircularProgress from '@material-ui/core/CircularProgress';
import Divider from '@material-ui/core/Divider';
import LocalVideoPreview from './LocalVideoPreview/LocalVideoPreview';
import SettingsMenu from './SettingsMenu/SettingsMenu';
import { Steps } from '../PreJoinScreens';
Expand All @@ -9,6 +10,10 @@ import ToggleVideoButton from '../../Buttons/ToggleVideoButton/ToggleVideoButton
import { useAppState } from '../../../state';
import useChatContext from '../../../hooks/useChatContext/useChatContext';
import useVideoContext from '../../../hooks/useVideoContext/useVideoContext';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import { useKrispToggle } from '../../../hooks/useKrispToggle/useKrispToggle';
import SmallCheckIcon from '../../../icons/SmallCheckIcon';
import InfoIconOutlined from '../../../icons/InfoIconOutlined';

const useStyles = makeStyles((theme: Theme) => ({
gutterBottom: {
Expand All @@ -24,6 +29,7 @@ const useStyles = makeStyles((theme: Theme) => ({
},
localPreviewContainer: {
paddingRight: '2em',
marginBottom: '2em',
[theme.breakpoints.down('sm')]: {
padding: '0 2.5em',
},
Expand All @@ -50,6 +56,17 @@ const useStyles = makeStyles((theme: Theme) => ({
padding: '0.8em 0',
margin: 0,
},
toolTipContainer: {
display: 'flex',
alignItems: 'center',
'& div': {
display: 'flex',
alignItems: 'center',
},
'& svg': {
marginLeft: '0.3em',
},
},
}));

interface DeviceSelectionScreenProps {
Expand All @@ -60,9 +77,10 @@ interface DeviceSelectionScreenProps {

export default function DeviceSelectionScreen({ name, roomName, setStep }: DeviceSelectionScreenProps) {
const classes = useStyles();
const { getToken, isFetching } = useAppState();
const { getToken, isFetching, isKrispEnabled, isKrispInstalled } = useAppState();
const { connect: chatConnect } = useChatContext();
const { connect: videoConnect, isAcquiringLocalTracks, isConnecting } = useVideoContext();
const { toggleKrisp } = useKrispToggle();
const disableButtons = isFetching || isAcquiringLocalTracks || isConnecting;

const handleJoin = () => {
Expand Down Expand Up @@ -102,32 +120,89 @@ export default function DeviceSelectionScreen({ name, roomName, setStep }: Devic
<Hidden mdUp>
<ToggleAudioButton className={classes.mobileButton} disabled={disableButtons} />
<ToggleVideoButton className={classes.mobileButton} disabled={disableButtons} />
<SettingsMenu mobileButtonClass={classes.mobileButton} />
</Hidden>
<SettingsMenu mobileButtonClass={classes.mobileButton} />
</div>
</Grid>
<Grid item md={5} sm={12} xs={12}>
<Grid container direction="column" justifyContent="space-between" style={{ height: '100%' }}>
<Grid container direction="column" justifyContent="space-between" style={{ alignItems: 'normal' }}>
<div>
<Hidden smDown>
<ToggleAudioButton className={classes.deviceButton} disabled={disableButtons} />
<ToggleVideoButton className={classes.deviceButton} disabled={disableButtons} />
</Hidden>
</div>
<div className={classes.joinButtons}>
<Button variant="outlined" color="primary" onClick={() => setStep(Steps.roomNameStep)}>
Cancel
</Button>
<Button
variant="contained"
color="primary"
data-cy-join-now
onClick={handleJoin}
disabled={disableButtons}
>
Join Now
</Button>
</div>
</Grid>
</Grid>

<Grid item md={12} sm={12} xs={12}>
{isKrispInstalled && (
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
style={{ marginBottom: '1em' }}
>
<div className={classes.toolTipContainer}>
<Typography variant="subtitle2">Noise Cancellation</Typography>
<Tooltip
title="Suppress background noise from your microphone"
interactive
leaveDelay={250}
leaveTouchDelay={15000}
enterTouchDelay={0}
>
<div>
<InfoIconOutlined />
</div>
</Tooltip>
</div>

<FormControlLabel
control={
<Switch
checked={!!isKrispEnabled}
checkedIcon={<SmallCheckIcon />}
disableRipple={true}
onClick={toggleKrisp}
/>
}
label={isKrispEnabled ? 'Enabled' : 'Disabled'}
style={{ marginRight: 0 }}
// Prevents <Switch /> from being temporarily enabled (and then quickly disabled) in unsupported browsers after
// isAcquiringLocalTracks becomes false:
disabled={isKrispEnabled && isAcquiringLocalTracks}
/>
</Grid>
)}
<Divider />
</Grid>

<Grid item md={12} sm={12} xs={12}>
<Grid container direction="row" alignItems="center" style={{ marginTop: '1em' }}>
<Hidden smDown>
<Grid item md={7} sm={12} xs={12}>
<SettingsMenu mobileButtonClass={classes.mobileButton} />
</Grid>
</Hidden>

<Grid item md={5} sm={12} xs={12}>
<div className={classes.joinButtons}>
<Button variant="outlined" color="primary" onClick={() => setStep(Steps.roomNameStep)}>
Cancel
</Button>
<Button
variant="contained"
color="primary"
data-cy-join-now
onClick={handleJoin}
disabled={disableButtons}
>
Join Now
</Button>
</div>
</Grid>
</Grid>
</Grid>
</Grid>
Expand Down
Loading