Skip to content

Commit

Permalink
Feat/use voice to text (#127)
Browse files Browse the repository at this point in the history
* ⚡️ add useVoiceToText Hook

* 0.2.44

* 0.2.45

* chore: add export useVoiceToText
  • Loading branch information
kevin-btc authored Jan 22, 2024
1 parent e0163fa commit 40de4f1
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 1 deletion.
2 changes: 2 additions & 0 deletions lib/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import usePolyfire, { PolyfireProvider } from "./usePolyfire";
import useChat from "./useChat";
import useAgent, { ActionAgent, Agent, DefinitionAction } from "./useAgent";
import useVoiceToText from "./useVoiceToText";
import {
useBrowserSpeechContext,
BrowserSpeechContextType,
Expand All @@ -16,6 +17,7 @@ export {
BrowserSpeechProvider,
useChat,
useAgent,
useVoiceToText,
useBrowserSpeechContext,
useBrowserSpeech,
ActionAgent,
Expand Down
125 changes: 125 additions & 0 deletions lib/hooks/useVoiceToText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/* eslint-env browser */

import { useState, useEffect, useCallback } from "react";

interface MediaRecorderOptions {
audioBitsPerSecond?: number;
bitsPerSecond?: number;
mimeType?: string;
videoBitsPerSecond?: number;
}

type UseMediaRecorderOptions = {
startKey?: string;
stopKey?: string;
recorderOption?: Omit<MediaRecorderOptions, "mimeType">;
mimeType?: string;
maxDuration?: number;
onStart?: () => void;
onStop?: (blobs: BlobPart[]) => void;
onError?: (error: Error) => void;
canRecord?: boolean;
};

type UseAudioRecorderReturn = {
isRecording: boolean;
startRecording: () => Promise<void>;
stopRecording: () => void;
isLoading: boolean;
setIsLoading: (isLoading: boolean) => void;
};

const useVoiceToText = (
{ stopKey = "ArrowUp", startKey = "ArrowUp", ...args }: UseMediaRecorderOptions = {},
deps: unknown[] = [],
): UseAudioRecorderReturn => {
const [isRecording, setIsRecording] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);

const stopRecording = useCallback(() => {
if (mediaRecorder) {
setIsRecording(false);
mediaRecorder.stop();
}
}, [mediaRecorder, ...deps]);

const startRecording = useCallback(async () => {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
});
const options: MediaRecorderOptions = {
...args.recorderOption,
mimeType: args.mimeType || "audio/webm",
};

const recorder = new MediaRecorder(stream, options);
const blobs: BlobPart[] = [];

recorder.ondataavailable = (event: BlobEvent) => {
blobs.push(event.data);
};

recorder.onstart = () => args?.onStart?.();
recorder.onstop = () => args?.onStop?.(blobs);

recorder.onerror = () => {
args?.onError?.(new Error("Recorder error"));
};

if (args.maxDuration) {
setTimeout(() => {
if (isRecording) {
stopRecording();
}
}, args.maxDuration);
}

recorder.start();
setMediaRecorder(recorder);
setIsRecording(true);
} catch (err) {
console.error("Error accessing the microphone:", err);
if (err instanceof Error) {
args?.onError?.(err);
}
}
} else {
console.error("MediaDevices API is not supported in this browser.");
}
}, [...deps]);

useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === startKey && !isRecording && args.canRecord) {
startRecording();
}
};

const handleKeyUp = (event: KeyboardEvent) => {
if (event.key === stopKey && isRecording) {
stopRecording();
}
};

window.addEventListener("keydown", handleKeyDown);
window.addEventListener("keyup", handleKeyUp);

return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener("keyup", handleKeyUp);
};
}, [startKey, stopKey, isRecording, startRecording, stopRecording, ...deps]);

return {
isRecording,
startRecording,
stopRecording,
isLoading,
setIsLoading,
};
};

export default useVoiceToText;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "polyfire-js",
"version": "0.2.44",
"version": "0.2.45",
"main": "index.js",
"types": "index.d.ts",
"author": "Lancelot Owczarczak <lancelot@owczarczak.fr>",
Expand Down

0 comments on commit 40de4f1

Please sign in to comment.