53 lines
1.5 KiB
TypeScript
53 lines
1.5 KiB
TypeScript
/**
|
|
* React hook for voice recording using MediaRecorder API.
|
|
* Records audio in WebM/Opus format for efficient streaming.
|
|
*/
|
|
import { useRef, useCallback, useState } from "react";
|
|
|
|
export type RecordingState = "idle" | "recording" | "stopped";
|
|
|
|
export function useVoiceRecorder() {
|
|
const [state, setState] = useState<RecordingState>("idle");
|
|
const mediaRecorderRef = useRef<MediaRecorder | null>(null);
|
|
const chunksRef = useRef<Blob[]>([]);
|
|
|
|
const startRecording = useCallback(async (): Promise<void> => {
|
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
const recorder = new MediaRecorder(stream, {
|
|
mimeType: "audio/webm;codecs=opus",
|
|
});
|
|
|
|
mediaRecorderRef.current = recorder;
|
|
chunksRef.current = [];
|
|
|
|
recorder.ondataavailable = (e) => {
|
|
if (e.data.size > 0) chunksRef.current.push(e.data);
|
|
};
|
|
|
|
recorder.start(100); // Collect chunks every 100ms
|
|
setState("recording");
|
|
}, []);
|
|
|
|
const stopRecording = useCallback((): Promise<Blob> => {
|
|
return new Promise((resolve) => {
|
|
const recorder = mediaRecorderRef.current;
|
|
if (!recorder || recorder.state !== "recording") {
|
|
resolve(new Blob());
|
|
return;
|
|
}
|
|
|
|
recorder.onstop = () => {
|
|
const blob = new Blob(chunksRef.current, { type: "audio/webm" });
|
|
recorder.stream.getTracks().forEach((t) => t.stop());
|
|
setState("stopped");
|
|
resolve(blob);
|
|
};
|
|
|
|
recorder.stop();
|
|
});
|
|
}, []);
|
|
|
|
return { state, startRecording, stopRecording };
|
|
}
|
|
|