실전 프로젝트 → 녹음, 녹음 파일 재생 + 보이스 페이지..?

실전 프로젝트 → 녹음, 녹음 파일 재생 + 보이스 페이지..?

오늘 한 일

1. 어제 각자 진행한 부분 develop branch에 merge 함

2. API에 맞춰서 api.js 수정하고 instanceRecord.js 추가함

3. 녹음 기능 맡아서 진행 (어제 연습 했던 거 참고해서 구현 하면 될 것 같다!)

기능 구현

1. src/pages/Voice.js를 구현 하던 중 버블 차트와 음성 녹음 컴포넌트를 분리 하는 게 좋을 것 같다는 생각이 들었다.

→ src/components/Bubble.js와 src/components/AudioRecord.js를 새로 만들어서 구현 하였다.

2. Bubble.js

1) 다른 사용자가 녹음한 파일을 들을 경우 ▶ → ■로 변경 해주기 위해서 아래 코드를 작성 하였다.

document.getElementById(index+"item").innerHTML = "■";

// ** 사용자가 누른 div만 정해진 색상으로 활성화 ** const changeColor3 = (index) => { if (checkCircle === "") { // 전에 눌렀던 게 없을 때 let x = document.getElementById(index); x.style.backgroundColor = "purple"; checkCircle = x; } else if (parseInt(checkCircle.id) === index) { console.log("눌렀던 거 또 누름"); } else { // 전에 눌렀던 음성이 있으면 다시 비활성화 checkCircle.style.backgroundColor = "grey"; let x = document.getElementById(index); checkCircle = x; // 바꿔치기 checkCircle.style.backgroundColor = "purple"; // 정해진 색상으로만 활성화 되게 } document.getElementById(index+"item").innerHTML = "■"; }; // ============================================================= ▶

https://ponyozzang.tistory.com/685

2) 새로고침 버튼을 만들어 줬다.

{window.location.reload()}}>새로고침

3) 백엔드 분들이 audio API 완성 해주시면 버블을 눌렀을 때 음성이 나오는 지 확인 해봐야 한다.

더보기 더보기 import React from "react"; import { useSelector } from "react-redux"; const Bubble = (props) => { const style = { area: [], gap: [] }; // div들을 담고 있는 객체 let checkCircle = ""; // 현재 재생 중인 음성의 index가 무엇인 지 담는다. const voiceInfo = useSelector((state) => state.voice.voiceInfo); for (let i = 0; i < 10; i++) { style.area.push(Math.ceil(Math.random() * (100 - 40) + 40)); // 동그라미 style.gap.push(Math.ceil(Math.random() * (10 - 5) + 5)); // 간격 } // ** 사용자가 누른 div만 정해진 색상으로 활성화 ** const changeColor3 = (index, voice) => { if (checkCircle === "") { // 전에 눌렀던 게 없을 때 let x = document.getElementById(index); x.style.backgroundColor = "purple"; checkCircle = x; } else if (parseInt(checkCircle.id) === index) { console.log("눌렀던 거 또 누름"); } else { // 전에 눌렀던 음성이 있으면 다시 비활성화 checkCircle.style.backgroundColor = "grey"; let x = document.getElementById(index); checkCircle = x; // 바꿔치기 checkCircle.style.backgroundColor = "purple"; // 정해진 색상으로만 활성화 되게 } document.getElementById(index + "item").innerHTML = "■"; play(voice); }; const play = (voice) => { const audio = new Audio(URL.createObjectURL(voice)); audio.loop = false; audio.volume = 1; audio.play(); }; return ( {window.location.reload()}}>새로고침 {style.area.map((item, index) => { return ( { changeColor3(index, voiceInfo[index]); }} > ▶ ); })} ); }; export default Bubble;

3. AudioRecord.js

: script 부분에서 오디오 부분과 타이머 부분이 나뉜다.

1) 오디오 부분

const [stream, setStream] = useState(); const [media, setMedia] = useState(); const [onRec, setOnRec] = useState(true); const [source, setSource] = useState(); const [analyser, setAnalyser] = useState(); const [audioUrl, setAudioUrl] = useState(); const [sound, setSound] = useState(); // ----------------------------------------- const onRecAudio = () => { setCount(0); // 재녹음 시 타이머 초기화 start(); // 타이머 시작 setDisabled(true); // 음원정보를 담은 노드를 생성하거나 음원을 실행또는 디코딩 시키는 일을 한다 const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); // 자바스크립트를 통해 음원의 진행상태에 직접접근에 사용된다. const analyser = audioCtx.createScriptProcessor(0, 1, 1); setAnalyser(analyser); function makeSound(stream) { // 내 컴퓨터의 마이크나 다른 소스를 통해 발생한 오디오 스트림의 정보를 보여준다. const source = audioCtx.createMediaStreamSource(stream); setSource(source); source.connect(analyser); analyser.connect(audioCtx.destination); } // 마이크 사용 권한 획득 navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => { const mediaRecorder = new MediaRecorder(stream); mediaRecorder.start(); setStream(stream); setMedia(mediaRecorder); makeSound(stream); analyser.onaudioprocess = function (e) { // 3분(180초) 지나면 자동으로 음성 저장 및 녹음 중지 if (e.playbackTime > 180) { stream.getAudioTracks().forEach(function (track) { track.stop(); }); mediaRecorder.stop(); // 메서드가 호출 된 노드 연결 해제 analyser.disconnect(); audioCtx.createMediaStreamSource(stream).disconnect(); mediaRecorder.ondataavailable = function (e) { setAudioUrl(e.data); setOnRec(true); }; } else { setOnRec(false); } }; }); }; // 사용자가 음성 녹음을 중지 했을 때 const offRecAudio = () => { end(); // dataavailable 이벤트로 Blob 데이터에 대한 응답을 받을 수 있음 media.ondataavailable = function (e) { setAudioUrl(e.data); setOnRec(true); }; // 모든 트랙에서 stop()을 호출해 오디오 스트림을 정지 stream.getAudioTracks().forEach(function (track) { track.stop(); }); // 미디어 캡처 중지 media.stop(); // 메서드가 호출 된 노드 연결 해제 analyser.disconnect(); source.disconnect(); if (audioUrl) { URL.createObjectURL(audioUrl); // 출력된 링크에서 녹음된 오디오 확인 가능 } // File 생성자를 사용해 파일로 변환 const sound = new File([audioUrl], "soundBlob", { lastModified: new Date().getTime(), type: "audio", }); setDisabled(false); setSound(sound); // File 정보 출력 }; const play = () => { const audio = new Audio(URL.createObjectURL(audioUrl)); audio.loop = false; audio.volume = 1; audio.play(); }; const dispatch = useDispatch(); const send = () => { setCount(0); dispatch(voiceActions.voiceRecordDB(sound)); }; // ----------------------------------------- 재생 녹음 전송

2) 타이머 부분

const [count, setCount] = useState(0); const [currentMinutes, setCurrentMinutes] = useState(0); const [currentSeconds, setCurrentSeconds] = useState(0); const intervalRef = useRef(null); // ----------------------------------------- const start = () => { intervalRef.current = setInterval(async () => { setCount((c) => c + 1); }, 1000); }; const end = () => { clearInterval(intervalRef.current); }; const timer = () => { const checkMinutes = Math.floor(count / 60); const minutes = checkMinutes & 60; const seconds = count % 60; setCurrentMinutes(minutes); setCurrentSeconds(seconds); }; // count의 변화에 따라 timer 함수 렌더링 useEffect(timer, [count]); // ----------------------------------------- {currentMinutes < 10 ? `0${currentMinutes}` : currentMinutes} :{" "} {currentSeconds < 10 ? `0${currentSeconds}` : currentSeconds}

실행 화면

from http://eundol1113.tistory.com/323 by ccl(A) rewrite - 2021-12-28 13:01:06