summaryrefslogtreecommitdiff
path: root/apps/web/src/voiceover/recorder.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/voiceover/recorder.ts')
-rw-r--r--apps/web/src/voiceover/recorder.ts40
1 files changed, 38 insertions, 2 deletions
diff --git a/apps/web/src/voiceover/recorder.ts b/apps/web/src/voiceover/recorder.ts
index b71ca74f..48322898 100644
--- a/apps/web/src/voiceover/recorder.ts
+++ b/apps/web/src/voiceover/recorder.ts
@@ -5,6 +5,8 @@
* actual microphone stream.
*/
+import { buildVoiceOverAudioConstraints } from "@/voiceover/devices";
+
export const PREFERRED_MIME_TYPES = [
"audio/webm;codecs=opus",
"audio/webm",
@@ -95,6 +97,11 @@ interface ActiveRecording {
startedAtMs: number;
}
+export interface VoiceOverRecordingSession {
+ stream: MediaStream;
+ mimeType: string;
+}
+
let activeRecording: ActiveRecording | null = null;
function stopStreamTracks({ stream }: { stream: MediaStream }): void {
@@ -103,7 +110,15 @@ function stopStreamTracks({ stream }: { stream: MediaStream }): void {
}
}
-export async function startVoiceOverRecording(): Promise<void> {
+export function getActiveVoiceOverRecordingStream(): MediaStream | null {
+ return activeRecording?.stream ?? null;
+}
+
+export async function startVoiceOverRecording({
+ deviceId,
+}: {
+ deviceId?: string | null;
+} = {}): Promise<VoiceOverRecordingSession> {
if (activeRecording) {
throw new Error("A voice-over recording is already in progress");
}
@@ -113,7 +128,24 @@ export async function startVoiceOverRecording(): Promise<void> {
throw new Error("Voice-over recording is not supported in this browser");
}
- const stream = await mediaDevices.getUserMedia({ audio: true });
+ let stream: MediaStream;
+ try {
+ stream = await mediaDevices.getUserMedia(
+ buildVoiceOverAudioConstraints({ deviceId }),
+ );
+ } catch (error) {
+ if (
+ deviceId &&
+ error instanceof DOMException &&
+ (error.name === "NotFoundError" || error.name === "OverconstrainedError")
+ ) {
+ stream = await mediaDevices.getUserMedia(
+ buildVoiceOverAudioConstraints({ deviceId: null }),
+ );
+ } else {
+ throw error;
+ }
+ }
try {
const mimeType = pickSupportedMimeType({
isTypeSupported: (candidate) => MediaRecorder.isTypeSupported(candidate),
@@ -135,6 +167,10 @@ export async function startVoiceOverRecording(): Promise<void> {
mimeType: recorder.mimeType || mimeType || "audio/webm",
startedAtMs: performance.now(),
};
+ return {
+ stream,
+ mimeType: activeRecording.mimeType,
+ };
} catch (error) {
stopStreamTracks({ stream });
throw error;