From 34ca8e2edb2c2ecb5ef317806ca7e83dc41331ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Sun, 5 Jun 2022 12:05:43 +0200 Subject: [PATCH] Add advanced audio settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit autoGainControl, echoCancellation, and noiseSuppression are audio processing options that are usually enabled by default on WebRTC input tracks. This commit adds the possibility to enable/disable them, as they can be undesirable in some cases (audiophile use cases). For example, one might want to stream electronic dance music, which is basically noise, so it should not be suppressed in that specific case. Signed-off-by: László Várady --- src/MediaDeviceHandler.ts | 42 +++++++++++++++++ .../tabs/user/VoiceUserSettingsTab.tsx | 45 +++++++++++++++++++ src/i18n/strings/en_EN.json | 4 ++ src/settings/Settings.tsx | 15 +++++++ 4 files changed, 106 insertions(+) diff --git a/src/MediaDeviceHandler.ts b/src/MediaDeviceHandler.ts index 59f624f0808..3c6c5f5051e 100644 --- a/src/MediaDeviceHandler.ts +++ b/src/MediaDeviceHandler.ts @@ -17,6 +17,7 @@ limitations under the License. import EventEmitter from 'events'; import { logger } from "matrix-js-sdk/src/logger"; +import { AudioSettings } from "matrix-js-sdk/src/webrtc/mediaHandler"; import SettingsStore from "./settings/SettingsStore"; import { SettingLevel } from "./settings/SettingLevel"; @@ -38,6 +39,8 @@ export enum MediaDeviceHandlerEvent { export default class MediaDeviceHandler extends EventEmitter { private static internalInstance; + private audioSettings: AudioSettings; + public static get instance(): MediaDeviceHandler { if (!MediaDeviceHandler.internalInstance) { MediaDeviceHandler.internalInstance = new MediaDeviceHandler(); @@ -78,6 +81,20 @@ export default class MediaDeviceHandler extends EventEmitter { await MatrixClientPeg.get().getMediaHandler().setAudioInput(audioDeviceId); await MatrixClientPeg.get().getMediaHandler().setVideoInput(videoDeviceId); + await MatrixClientPeg.get().getMediaHandler().setAudioSettings(MediaDeviceHandler.loadAudioSettings()); + } + + private static loadAudioSettings(): AudioSettings { + return { + autoGainControl: SettingsStore.getValue("webrtc_audio_autoGainControl"), + echoCancellation: SettingsStore.getValue("webrtc_audio_echoCancellation"), + noiseSuppression: SettingsStore.getValue("webrtc_audio_noiseSuppression"), + }; + } + + public constructor() { + super(); + this.audioSettings = MediaDeviceHandler.loadAudioSettings(); } public setAudioOutput(deviceId: string): void { @@ -113,6 +130,31 @@ export default class MediaDeviceHandler extends EventEmitter { } } + public async setAudioAutoGainControl(value: boolean): Promise { + this.audioSettings.autoGainControl = value; + SettingsStore.setValue("webrtc_audio_autoGainControl", null, SettingLevel.DEVICE, value); + + await MatrixClientPeg.get().getMediaHandler().setAudioSettings(this.audioSettings); + } + + public async setAudioEchoCancellation(value: boolean): Promise { + this.audioSettings.echoCancellation = value; + SettingsStore.setValue("webrtc_audio_echoCancellation", null, SettingLevel.DEVICE, value); + + await MatrixClientPeg.get().getMediaHandler().setAudioSettings(this.audioSettings); + } + + public async setAudioNoiseSuppression(value: boolean): Promise { + this.audioSettings.noiseSuppression = value; + SettingsStore.setValue("webrtc_audio_noiseSuppression", null, SettingLevel.DEVICE, value); + + await MatrixClientPeg.get().getMediaHandler().setAudioSettings(this.audioSettings); + } + + public getAudioSettings(): AudioSettings { + return this.audioSettings; + } + public static getAudioOutput(): string { return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_audiooutput"); } diff --git a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx index e027853e140..5c876e3b575 100644 --- a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx @@ -27,6 +27,7 @@ import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import Modal from "../../../../../Modal"; import { SettingLevel } from "../../../../../settings/SettingLevel"; import SettingsFlag from '../../../elements/SettingsFlag'; +import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import ErrorDialog from '../../../dialogs/ErrorDialog'; const getDefaultDevice = (devices: Array>) => { @@ -43,17 +44,24 @@ const getDefaultDevice = (devices: Array>) => { interface IState extends Record { mediaDevices: IMediaDevices; + audioAutoGainControl: boolean; + audioEchoCancellation: boolean; + audioNoiseSuppression: boolean; } export default class VoiceUserSettingsTab extends React.Component<{}, IState> { constructor(props: {}) { super(props); + const audioSettings = MediaDeviceHandler.instance.getAudioSettings(); this.state = { mediaDevices: null, [MediaDeviceKindEnum.AudioOutput]: null, [MediaDeviceKindEnum.AudioInput]: null, [MediaDeviceKindEnum.VideoInput]: null, + audioAutoGainControl: audioSettings.autoGainControl, + audioEchoCancellation: audioSettings.echoCancellation, + audioNoiseSuppression: audioSettings.noiseSuppression, }; } @@ -79,6 +87,16 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> { } }; + private async refreshAudioSettings(): Promise { + const audioSettings = MediaDeviceHandler.instance.getAudioSettings(); + + this.setState({ + audioAutoGainControl: audioSettings.autoGainControl, + audioEchoCancellation: audioSettings.echoCancellation, + audioNoiseSuppression: audioSettings.noiseSuppression, + }); + } + private requestMediaPermissions = async (): Promise => { let constraints; let stream; @@ -197,6 +215,33 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> {
{ _t("Advanced") }
+ { _t("Voice processing") } +
+ { + MediaDeviceHandler.instance.setAudioAutoGainControl(v); + this.refreshAudioSettings(); + }} + label={_t("Automatic gain control")} + /> + { + MediaDeviceHandler.instance.setAudioEchoCancellation(v); + this.refreshAudioSettings(); + }} + label={_t("Echo cancellation")} + /> + { + MediaDeviceHandler.instance.setAudioNoiseSuppression(v); + this.refreshAudioSettings(); + }} + label={_t("Noise suppression")} + /> +
{ _t("Connection") } Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 09987c43861..980d2064afc 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -645,6 +645,21 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, default: "default", }, + "webrtc_audio_autoGainControl": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("Automatic gain control"), + default: true, + }, + "webrtc_audio_echoCancellation": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("Echo cancellation"), + default: true, + }, + "webrtc_audio_noiseSuppression": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("Noise suppression"), + default: true, + }, "language": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, default: "en",