Created
March 16, 2023 09:05
-
-
Save maciekChmura/a1c9cfb45f32b72ea6c2b4a4dcfe898f to your computer and use it in GitHub Desktop.
React AudioRecorder - speech to text with Whisper API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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