-
[REACT] 데시벨측정 자동녹음종료기능 custom hookJava Script & Type Script/Favorites 2023. 6. 25. 16:22728x90
이건 녹음 중에 사용자가 말을 멈춘 것이 감지되면 녹음을 자동으로 종료하는 기능이다.
하루하고 반나절을 걸려서 만들었다😭 분명 더 효율적인 방법이 있을 것 같은데 다른 사람들이 올려놓은 코드 이해도 못하고 갖다 쓰기 싫어서 그냥 만들었다.
1. 가장 먼저, 처음엔 컴포넌트로 recordWithDecibel을 만들어 데시벨이 잘 측정되는지를 확인했다. 신기한 것은? 여기서 mediaRecorder를 쓰지 않음 리액트의 기능만으로 구현됨
2. 녹음시작 후 데시벨이 잘 측정되는것이 확인된 후, return아래 jsx를 다 떼고 컴포넌트에서 커스텀 훅으로 만들었다.
이것이 바로 그 커스텀 훅.
// useRecordWithDecibel.ts import React, { useEffect, useRef, useState } from "react"; const useRecordWithDecibel = () => { const [decibel, setDecibel] = useState<number>(0); const mediaStreamRef = useRef<MediaStream | null>(null); const audioContextRef = useRef<AudioContext | null>(null); const analyserRef = useRef<AnalyserNode | null>(null); const animationFrameId = useRef<number | null>(null); const startMeasuringDecibel = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaStreamRef.current = stream; audioContextRef.current = new AudioContext(); const sourceNode = audioContextRef.current.createMediaStreamSource(stream); analyserRef.current = audioContextRef.current.createAnalyser(); sourceNode.connect(analyserRef.current); updateDecibel(); } catch (error) { console.error("Failed to start recording:", error); } }; const stopMeasuringDecibel = () => { mediaStreamRef.current?.getTracks().forEach((track) => track.stop()); audioContextRef.current?.close(); cancelAnimationFrame(animationFrameId.current!); }; const updateDecibel = () => { if (!analyserRef.current) return; const bufferLength = analyserRef.current.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); analyserRef.current.getByteFrequencyData(dataArray); const sum = dataArray.reduce((acc, value) => acc + value); const average = sum / bufferLength; const decibel = 20 * Math.log10(average); setDecibel(Number(decibel.toFixed(2))); animationFrameId.current = requestAnimationFrame(updateDecibel); }; return { startMeasuringDecibel, stopMeasuringDecibel, decibel }; }; export default useRecordWithDecibel;
이 훅은 데시벨을 측정하는 기능만 수행한다. 자동으로 멈추게하기 위해선 녹음시작함수에 startMeasuringDecibel함수를 끼워넣고, 녹음중단버튼에 stopMeasringDecibel을 넣은 후, decibel을 콘솔로 찍으면 말하는 데시벨에 따라 -infinite~ 약 50까지 찍히는걸 알 수 있다.
3. 데시벨을 측정해서 이제 어떻게 할것이냐?
initial값을 0으로 갖는 bucket이라는 상태를 하나 만든다.
const [bucket, setBucket] = React.useState<number>(0);
여러번 녹음테스트를 해본 결과, 아무 말이 없을 때의 데시벨은 -infinite 부터 주변 소음이 있을 경우 약 10까지 올라간다. 말하는 음성이 있으면 20~60까지 정도로 측정된다.
useEffect의 의존성배열에는 decibel을 넣고, 말하고 있지 않는 수준의 데시벨일 때 bucket-=1, 말하고있는 수준의 데시벨일 땐 bucket+=1을 한다.
녹음을 시작한 최초 5초동안은 false값을 갖다가 5초 뒤 true가 되는 checking이라는 상태를 하나 만들어 checking이 true일 때, bucket값을 확인해 0이하일 경우 녹음을 중단, 0보다 크면 녹음을 계속한다.
추가적인 설정으로는,
bucket은 최대 100까지만 올라간다. 그 이상일 땐 증가하지 않음 (이 최댓값에 대한 제한이 없으면 긴 문장을 말할 때 너무 높은 수준으로 bucket값이 올라가서 말하지 않는 순간동안 숫자를 0까지 내리는데 굉장히 오래걸림. 짧은 단어든 긴 문장이든 발음을 하다가 5초가량 말이 없으면 동일하게 녹음을 중단해줘야 함 )
React.useEffect(() => { const updateBucket = (value: number) => { setBucket((prev) => (prev += value)); }; if (isRecording === "recording" && decibel > -50) { if (bucket < 100 && decibel > 15) { updateBucket(1); // console.log("1추가"); } else if (decibel < 10) { updateBucket(-1); // console.log("1빼기"); } // console.log(bucket); } if (checking === true) { if (bucket < 0) { sendRecordandGetRes(); } } }, [decibel]);
저 주석을 다 풀어서 테스트해보면,
모든 시점을 분리해보았을 때
1) 녹음 시작
startMeasuringDecibel();intervalId = setInterval(() => {checkIfRecording();}, 5000);녹음 시작과 동시에 데시벨이 측정된다. 말을 하면 "1추가"가 계속 찍히고 말을 멈추면 "1빼기"가 계속 찍힌다.
말을 하는동안 bucket은 0에서부터 계속 증가하다가 100에서 멈춘다. 말을 멈추면 100에서 빠른 속도로 내려간다.
2) 5초 뒤, checking이 true가 된다. 이제부터 bucket이 0미만이 되면 녹음은 멈춘다.
녹음 이후 말을 계속 안하고있었으면 바로 checking이 true가 되는 이 순간 녹음이 즉시 종료된다.
말을 하고있었으면, bucket은 0이상 100미만이었을거고 말을 멈추면 약 5초동안 빠르게 bucket값이 하강하다가 -1이 되면 종료한다.
3) 녹음 종료
stopMeasuringDecibel();setBucket(0);if (intervalId) {clearInterval(intervalId);}여러 시행착오 겪어보며 고생해서 만든 기능이다.
다음 프로젝트에서도 많이 써먹을 수 있었으면 좋겠당.ㅎ
728x90'Java Script & Type Script > Favorites' 카테고리의 다른 글
[React] 리액트 스토리북 도입하기 (Feat. Atomic pattern) (1) 2024.01.06 [TS] 엔터쳐서 실행하기 유틸함수 만들기 (0) 2023.07.04 [React] 네이버 자동로그인 따라하기 (0) 2023.06.19 [Library] React-slick, Apex-charts 라이브러리 이용법 -리액트로 캐러셀 구현, 도넛그래프, 반원그래프, 오각형그래프, 막대그래프 그리기 (0) 2023.06.15 [React] 리팩토링 - 컴포넌트 역할 분리 (0) 2023.04.20