Skip to content

Instantly share code, notes, and snippets.

@maciekChmura
Created March 16, 2023 09:05
Show Gist options
  • Save maciekChmura/a1c9cfb45f32b72ea6c2b4a4dcfe898f to your computer and use it in GitHub Desktop.
Save maciekChmura/a1c9cfb45f32b72ea6c2b4a4dcfe898f to your computer and use it in GitHub Desktop.
React AudioRecorder - speech to text with Whisper API
import { useState, useRef } from "react";
import { reportError, getErrorMessage } from "~/utils/error";
import axios from "axios";
import { env } from "~/env.mjs";
type RecordingStatus = "inactive" | "recording" | "paused";
const mimeType = "audio/mp3";
const fileName = "recording.mp3";
const AudioRecorder = () => {
const [permission, setPermission] = useState(false);
const [stream, setStream] = useState<MediaStream | null>(null);
const mediaRecorder = useRef<MediaRecorder | null>(null);
const [recordingStatus, setRecordingStatus] =
useState<RecordingStatus>("inactive");
const [audioChunks, setAudioChunks] = useState<Blob[]>([]);
const [audio, setAudio] = useState<string>("");
const [audioBlob, setAudioBlob] = useState<Blob>();
const [audioFile, setAudioFile] = useState<File>();
const [text, setText] = useState<string>("");
const handelSendFile = async () => {
try {
const response = await axios({
method: "post",
url: "https://api.openai.com/v1/audio/transcriptions",
data: { file: audioFile, model: "whisper-1", language: "PL" },
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${env.NEXT_PUBLIC_WHISPER_KEY}`,
},
});
setText(response.data.text);
} catch (error) {
console.log(error.response.data);
}
};
const getMicrophonePermission = async () => {
if ("MediaRecorder" in window) {
try {
const streamData = await navigator.mediaDevices.getUserMedia({
audio: true,
video: false,
});
setPermission(true);
setStream(streamData);
} catch (error) {
reportError({ message: getErrorMessage(error) });
}
} else {
alert("The MediaRecorder API is not supported in your browser.");
}
};
const startRecording = () => {
setRecordingStatus("recording");
//create new Media recorder instance using the stream
if (!stream) return;
const media = new MediaRecorder(stream, {
type: mimeType,
} as MediaRecorderOptions);
//set the MediaRecorder instance to the mediaRecorder ref
mediaRecorder.current = media;
//invokes the start method to start the recording process
mediaRecorder.current.start();
const localAudioChunks = [] as Blob[];
mediaRecorder.current.ondataavailable = (event) => {
if (typeof event.data === "undefined") return;
if (event.data.size === 0) return;
localAudioChunks.push(event.data);
};
setAudioChunks(localAudioChunks);
};
const stopRecording = () => {
setRecordingStatus("inactive");
//stops the recording instance
if (!mediaRecorder.current) return;
mediaRecorder.current.stop();
mediaRecorder.current.onstop = () => {
//creates a blob file from the audiochunks data
const audioBlob = new Blob(audioChunks, { type: mimeType });
//creates a playable URL from the blob file.
const audioUrl = URL.createObjectURL(audioBlob);
setAudio(audioUrl);
setAudioBlob(audioBlob);
setAudioChunks([]);
const file = new File([audioBlob], fileName, { type: mimeType });
setAudioFile(file);
};
};
return (
<div className=" container mx-auto flex w-full flex-col">
<h2>Audio Recorder</h2>
<main>
<div>
{!permission ? (
<button
onClick={() => void getMicrophonePermission()}
type="button"
>
Get Microphone
</button>
) : null}
{permission && recordingStatus === "inactive" ? (
<button onClick={startRecording} type="button">
Start Recording
</button>
) : null}
{recordingStatus === "recording" ? (
<button onClick={stopRecording} type="button">
Stop Recording
</button>
) : null}
</div>
{audio ? (
<div className="audio-container">
<audio src={audio} controls></audio>
<a download href={audio}>
Download Recording
</a>
</div>
) : null}
<button onClick={handelSendFile}>Send File</button>
</main>
{text ? <p className=" mt-4 text-lg">{text}</p> : null}
</div>
);
};
export default AudioRecorder;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment