From 5ba09a5f903f72c76a197d02fd6ace778962abe6 Mon Sep 17 00:00:00 2001 From: Joao Pedro Antunes Borie Date: Thu, 9 Apr 2026 12:07:32 +0100 Subject: [PATCH] Fix #32727: Ensure VoiceRecording uses the selected microphone (#32887) Voice messages were being recorded using the system default microphone instead of the device selected in Element settings. This was fixed by ensuring the preferred deviceId is correctly passed to the MediaStream constraints in VoiceRecording.ts. Added unit tests in VoiceRecording-test.ts to verify that the application correctly requests the user-selected device. Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com> --- apps/web/src/audio/VoiceRecording.ts | 6 ++++- .../unit-tests/audio/VoiceRecording-test.ts | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/apps/web/src/audio/VoiceRecording.ts b/apps/web/src/audio/VoiceRecording.ts index 705b96375a..44a016b995 100644 --- a/apps/web/src/audio/VoiceRecording.ts +++ b/apps/web/src/audio/VoiceRecording.ts @@ -103,10 +103,14 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { private async makeRecorder(): Promise { try { + const requestedDeviceId = MediaDeviceHandler.getAudioInput(); + const deviceIdConstraint = + requestedDeviceId && requestedDeviceId !== "default" ? { deviceId: { exact: requestedDeviceId } } : {}; + this.recorderStream = await navigator.mediaDevices.getUserMedia({ audio: { channelCount: CHANNELS, - deviceId: MediaDeviceHandler.getAudioInput(), + ...deviceIdConstraint, autoGainControl: { ideal: MediaDeviceHandler.getAudioAutoGainControl() }, echoCancellation: { ideal: MediaDeviceHandler.getAudioEchoCancellation() }, noiseSuppression: { ideal: MediaDeviceHandler.getAudioNoiseSuppression() }, diff --git a/apps/web/test/unit-tests/audio/VoiceRecording-test.ts b/apps/web/test/unit-tests/audio/VoiceRecording-test.ts index eb14b9364a..73329ba566 100644 --- a/apps/web/test/unit-tests/audio/VoiceRecording-test.ts +++ b/apps/web/test/unit-tests/audio/VoiceRecording-test.ts @@ -120,6 +120,29 @@ describe("VoiceRecording", () => { }), ); }); + + it("should request the selected microphone as an exact device constraint", async () => { + MediaDeviceHandlerMock.getAudioInput.mockReturnValue("selected-mic"); + await recording.start(); + + expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith( + expect.objectContaining({ + audio: expect.objectContaining({ deviceId: { exact: "selected-mic" } }), + }), + ); + }); + + it("should not force an exact microphone when default device is selected", async () => { + MediaDeviceHandlerMock.getAudioInput.mockReturnValue("default"); + await recording.start(); + + const constraints = mocked(navigator.mediaDevices.getUserMedia).mock.calls[0][0] as MediaStreamConstraints; + expect(constraints.audio).toEqual( + expect.not.objectContaining({ + deviceId: expect.anything(), + }), + ); + }); }); describe("when recording", () => {