본문 바로가기
개발언어/자바스크립트

[자바스크립트] 마이크 녹음 테스트

by 창용이랑 2022. 2. 9.
728x90

원본출처 : https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder

관련소스 깃헙 : https://github.com/mdn/web-dictaphone/

 

GitHub - mdn/web-dictaphone: A sample MDN app that uses getUserMedia and MediaRecorder API for recording audio snippets, and The

A sample MDN app that uses getUserMedia and MediaRecorder API for recording audio snippets, and The Web Audio API for visualizations. - GitHub - mdn/web-dictaphone: A sample MDN app that uses getUs...

github.com

 

 

 

자바스크립트에서 마이크를 사용하려면 먼저 사용자로부터 마이크 사용 권한을 획득해야 합니다.

여기서 사용자가 허용 버튼을 눌러야 이후 녹음 작업이 진행이 됩니다.

 

이 부분은 navigator.mediaDevices.getUserMedia(constraints).then(...)을 사용합니다.

if (navigator.mediaDevices) {
    const constraints = {
        audio: true
    }
    navigator.mediaDevices.getUserMedia(constraints)
        .then(stream => {
                .................
           
        })
        .catch(err => {
            console.log('The following error occurred: ' + err)
        })
}

마이크 소리를 실시간으로 들으려는 경우 오디오 컨텍스트를 사용하며, 마이크 소리를 녹음한 경우 저장하려는 경우 new MediaRecorder(stream) 을 사용합니다.

미디어리코더에서 .start()를 하면 녹음을 시작하고, .stop()을 하면 녹음이 종료됩니다.

아래 코드는 녹음이 진행되는 동안 데이터를 배열에 저장하는 작업입니다.

mediaRecorder.ondataavailable = e => {
    chunks.push(e.data)
}

이 배열을 블롭 데이터로 변환한 후 <audio>  src에 적용하면 녹음 파일이 재생됩니다.

const audio = document.createElement('audio')
...
audio.controls = true
const blob = new Blob(chunks, {
    'type': 'audio/ogg codecs=opus'
})
...
const audioURL = URL.createObjectURL(blob)
audio.src = audioURL

이것을 종합한 예제 코드는 아래와 같습니다.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>마이크 테스트</title>
</head>

<body>
    <input type=checkbox id="chk-hear-mic"><label for="chk-hear-mic">마이크 소리 듣기</label>
    <button id="record">녹음</button>
    <button id="stop">정지</button>
    <div id="sound-clips"></div>
    <script>
        const record = document.getElementById("record")
        const stop = document.getElementById("stop")
        const soundClips = document.getElementById("sound-clips")
        const chkHearMic = document.getElementById("chk-hear-mic")

        const audioCtx = new(window.AudioContext || window.webkitAudioContext)() // 오디오 컨텍스트 정의

        const analyser = audioCtx.createAnalyser()
        //        const distortion = audioCtx.createWaveShaper()
        //        const gainNode = audioCtx.createGain()
        //        const biquadFilter = audioCtx.createBiquadFilter()

        function makeSound(stream) {
            const source = audioCtx.createMediaStreamSource(stream)

            source.connect(analyser)
            //            analyser.connect(distortion)
            //            distortion.connect(biquadFilter)
            //            biquadFilter.connect(gainNode)
            //            gainNode.connect(audioCtx.destination) // connecting the different audio graph nodes together
            analyser.connect(audioCtx.destination)

        }

        if (navigator.mediaDevices) {
            console.log('getUserMedia supported.')

            const constraints = {
                audio: true
            }
            let chunks = []

            navigator.mediaDevices.getUserMedia(constraints)
                .then(stream => {

                    const mediaRecorder = new MediaRecorder(stream)
                    
                    chkHearMic.onchange = e => {
                        if(e.target.checked == true) {
                            audioCtx.resume()
                            makeSound(stream)
                        } else {
                            audioCtx.suspend()
                        }
                    }
                    
                    record.onclick = () => {
                        mediaRecorder.start()
                        console.log(mediaRecorder.state)
                        console.log("recorder started")
                        record.style.background = "red"
                        record.style.color = "black"
                    }

                    stop.onclick = () => {
                        mediaRecorder.stop()
                        console.log(mediaRecorder.state)
                        console.log("recorder stopped")
                        record.style.background = ""
                        record.style.color = ""
                    }

                    mediaRecorder.onstop = e => {
                        console.log("data available after MediaRecorder.stop() called.")

                        const clipName = prompt("오디오 파일 제목을 입력하세요.", new Date())

                        const clipContainer = document.createElement('article')
                        const clipLabel = document.createElement('p')
                        const audio = document.createElement('audio')
                        const deleteButton = document.createElement('button')

                        clipContainer.classList.add('clip')
                        audio.setAttribute('controls', '')
                        deleteButton.innerHTML = "삭제"
                        clipLabel.innerHTML = clipName

                        clipContainer.appendChild(audio)
                        clipContainer.appendChild(clipLabel)
                        clipContainer.appendChild(deleteButton)
                        soundClips.appendChild(clipContainer)

                        audio.controls = true
                        const blob = new Blob(chunks, {
                            'type': 'audio/ogg codecs=opus'
                        })
                        chunks = []
                        const audioURL = URL.createObjectURL(blob)
                        audio.src = audioURL
                        console.log("recorder stopped")

                        deleteButton.onclick = e => {
                            evtTgt = e.target
                            evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode)
                        }
                    }

                    mediaRecorder.ondataavailable = e => {
                        chunks.push(e.data)
                    }
                })
                .catch(err => {
                    console.log('The following error occurred: ' + err)
                })
        }
    </script>
</body></html>