From fb7c11fccd22f38cec14a56cfd3a9b13fa4036d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Tue, 22 Nov 2022 22:51:53 +0100 Subject: [PATCH] VoiceRecordings: add higher-quality audio recording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When recording non-voice audio (e.g. music, FX), a different Opus encoder application should be specified. It is also recommended to increase the bitrate to 64-96 kb/s for musical use. Note: the HQ mode is currently activated when noise suppression is turned off. This is a very arbitrary condition. Signed-off-by: László Várady --- src/audio/VoiceRecording.ts | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/audio/VoiceRecording.ts b/src/audio/VoiceRecording.ts index 682360c33aa0..909dbca48233 100644 --- a/src/audio/VoiceRecording.ts +++ b/src/audio/VoiceRecording.ts @@ -32,12 +32,26 @@ import mxRecorderWorkletPath from "./RecorderWorklet"; const CHANNELS = 1; // stereo isn't important export const SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality. -const BITRATE = 24000; // 24kbps is pretty high quality for our use case in opus. const TARGET_MAX_LENGTH = 900; // 15 minutes in seconds. Somewhat arbitrary, though longer == larger files. const TARGET_WARN_TIME_LEFT = 10; // 10 seconds, also somewhat arbitrary. export const RECORDING_PLAYBACK_SAMPLES = 44; +interface RecorderOptions { + bitrate: number; + encoderApplication: number; +} + +const voiceRecorderOptions: RecorderOptions = { + bitrate: 24000, + encoderApplication: 2048, +}; + +const higQualityRecorderOptions: RecorderOptions = { + bitrate: 96000, + encoderApplication: 2049, +}; + export interface IRecordingUpdate { waveform: number[]; // floating points between 0 (low) and 1 (high). timeSeconds: number; // float @@ -88,6 +102,10 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { this.targetMaxLength = null; } + private shouldRecordInHighQuality(): boolean { + return !MediaDeviceHandler.getAudioNoiseSuppression(); + } + private async makeRecorder() { try { this.recorderStream = await navigator.mediaDevices.getUserMedia({ @@ -137,15 +155,17 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { this.recorderProcessor.addEventListener("audioprocess", this.onAudioProcess); } + const hqRecording = this.shouldRecordInHighQuality(); this.recorder = new Recorder({ encoderPath, // magic from webpack encoderSampleRate: SAMPLE_RATE, - encoderApplication: 2048, // voice (default is "audio") + encoderApplication: hqRecording ? higQualityRecorderOptions.encoderApplication + : voiceRecorderOptions.encoderApplication, streamPages: true, // this speeds up the encoding process by using CPU over time encoderFrameSize: 20, // ms, arbitrary frame size we send to the encoder numberOfChannels: CHANNELS, sourceNode: this.recorderSource, - encoderBitRate: BITRATE, + encoderBitRate: hqRecording ? higQualityRecorderOptions.bitrate : voiceRecorderOptions.bitrate, // We use low values for the following to ease CPU usage - the resulting waveform // is indistinguishable for a voice message. Note that the underlying library will