Last active
November 15, 2024 07:09
-
-
Save cassidoo/dd1190c248d60c723de14fe9ee32f450 to your computer and use it in GitHub Desktop.
A simple React microphone component, recording audio and showing the blob in the browser, styled with Tailwind.
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
"use client"; | |
import { useState, useEffect, useRef } from "react"; | |
function SimpleRecordButton() { | |
const [isRecording, setIsRecording] = useState(false); | |
const [audioStream, setAudioStream] = useState(null); | |
const [mediaRecorder, setMediaRecorder] = useState(null); | |
const [audioBlob, setAudioBlob] = useState(null); | |
const [recordingTime, setRecordingTime] = useState(0); | |
const timerRef = useRef(null); | |
const RECORDING_MAX_DURATION = 240; // 4 minutes in seconds | |
useEffect(() => { | |
if (!audioStream) { | |
navigator.mediaDevices | |
.getUserMedia({ audio: true }) | |
.then((stream) => { | |
setAudioStream(stream); | |
const mediaRecorder = new MediaRecorder(stream); | |
setMediaRecorder(mediaRecorder); | |
let audio; | |
mediaRecorder.ondataavailable = (event) => { | |
if (event.data.size > 0) { | |
audio = [event.data]; | |
} | |
}; | |
mediaRecorder.onstop = (event) => { | |
const b = new Blob(audio, { type: "audio/wav" }); | |
setAudioBlob(b); | |
console.log("audioBlob", b); | |
}; | |
}) | |
.catch((error) => { | |
console.error("Error accessing microphone:", error); | |
}); | |
} | |
return () => { | |
if (timerRef.current) { | |
clearInterval(timerRef.current); | |
} | |
}; | |
}, [audioStream]); | |
const handleToggleRecording = (event) => { | |
event.preventDefault(); | |
if (isRecording) { | |
stopRecording(); | |
} else { | |
startRecording(); | |
} | |
}; | |
const startRecording = () => { | |
mediaRecorder.start(); | |
setIsRecording(true); | |
setRecordingTime(0); | |
setAudioBlob(null); | |
timerRef.current = setInterval(() => { | |
setRecordingTime((prevTime) => { | |
if (prevTime >= RECORDING_MAX_DURATION - 1) { | |
stopRecording(); | |
return RECORDING_MAX_DURATION; | |
} | |
return prevTime + 1; | |
}); | |
}, 1000); | |
}; | |
const stopRecording = () => { | |
mediaRecorder.stop(); | |
setIsRecording(false); | |
if (timerRef.current) { | |
clearInterval(timerRef.current); | |
} | |
}; | |
const formatTime = (seconds) => { | |
const minutes = Math.floor(seconds / 60); | |
const remainingSeconds = seconds % 60; | |
return `${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`; | |
}; | |
return ( | |
<div> | |
<button | |
onClick={handleToggleRecording} | |
className={`bg-red-400 hover:opacity-80 text-white font-bold py-2 px-4 rounded`} | |
> | |
{isRecording ? ( | |
<> | |
<span className={`mr-3 ${isRecording && "animate-pulse"}`}>●</span>{" "} | |
Stop Recording | |
</> | |
) : audioBlob ? ( | |
"Redo recording" | |
) : ( | |
"Start Recording" | |
)} | |
</button> | |
<div> | |
{isRecording && ( | |
<div> | |
<p>Recording...</p> | |
<p>Time: {formatTime(recordingTime)}</p> | |
</div> | |
)} | |
</div> | |
{audioBlob && ( | |
<> | |
<div>Preview recording before submitting:</div> | |
<audio controls> | |
<source src={URL.createObjectURL(audioBlob)} type="audio/wav" /> | |
</audio> | |
</> | |
)} | |
</div> | |
); | |
} | |
export default SimpleRecordButton; |
@cassidoo I wasn't aware 😮! I feel a bit dumb about making the comment now... Anyway, I think I will start to do the same thing going forward to accommodate people with various disabilities 👍 Thanks for letting me on with this configuration
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@TimotejKovacka
You can change how tabs are rendered in your personal GitHub settings!
https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/managing-your-tab-size-rendering-preference
I prefer using tabs instead of spaces for accessibility reasons. With a screen reader, someone going through the code will hear every individual space, which gets to be a lot when you have some deeply nested things. A screen reader reading out the tab characters is much more manageable!