본문 바로가기
웹 오디오 프로그래밍

[Web Audio API #14] 녹음하기 - 마이크 찾기

by 영바이트 2021. 8. 6.

이전 포스팅들에서 서버로 부터 음원을 전달받고 다양한 처리를 거쳐 사용자의 스피커를 통해 재생하는 과정에 대해 살펴보았다. 이제 거꾸로 사용자의 디바이스에서 마이크를 통해 녹음한 음원을 서버로 전달하는 방법을 알아보자.

 

사용자가 음원을 만드는 방법에는 여러 가지가 있겠지만 마이크를 통해 신호를 얻은 후 음원으로 가공하는 방법이 널리 사용된다. 사용자 기기에 연결되어 있는 음성 입력장치에 무엇이 있는지 알아보는 방법부터 살펴보자.

 

★ 사용자 기기의 비디오, 오디오 입력장치에 접근하기 위해서는 반드시 HTTPS와 같은 보안 연결을 사용해야 한다. 그렇지 않으면 관련된 기능을 사용할 수 없다.

 

사용자 기기에 연결되어 있는 디바이스들을 navigator.mediaDevices 객체를 통해 얻을 수 있다. 아래와 같이 mediaDevices 객체의 enumerateDevices() 메서드를 호출하면 기기에 연결되어 있는 미디어 장치들을 얻을 수 있다.

navigator.mediaDevices.enumerateDevices().then((devices) => {
    // devices = 장치 정보가 들어있는 MediaDeviceInfo 배열
}).catch((error) => {
    // 오류 처리
});

 

기기에 연결되어 있는 오디오 디바이스들의 리스트를 얻으려 한다면 아래와 같이 할 수 있다.

const getConnectedDevices = (type, callback) => {
    navigator.mediaDevices.enumerateDevices().then((devices) => {
        var filtered = devices.filter((device) => {
            return device.kind == type;
        });
        callback(filtered);
    }).catch((error) => {
        console.log('[Error] enumerateDevices(): ', error);
    });
}

const displayAudioDeviceList = (audioDevices) => {
    if(audioDevices){
        audioDevices.forEach((audioDevice) => {
            console.log('audioDevice.label: ', audioDevice.label);
            console.log('audioDevice.ID: ', audioDevice.deviceId);
        });
    }
}

if(navigator.mediaDevices){
    getConnectedDevices('audioinput', displayAudioDeviceList);
}

 

이어지는 포스팅들에서 계속해서 살펴볼 navigator.mediaDevices 객체는 카메라, 마이크 등 연결된 미디어 장치에 접근할 수 있는 인터페이스를 제공한다. 즉 미디어 스트림을 제공하는 하드웨어를 사용할 수 있도록 해준다. 위 예제 코드의 enumerateDevices() 메서드는 이들 미디어 장치 리스트(배열)가 담긴 프로미스promise를 반환한다.

 

컴퓨터에 연결된 오디오 입력 장치들을 웹 페이지의 드롭다운 메뉴에 아래와 같은 방법으로 표시할 수 있다.

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="device_selector_div">
        <label for="device_selector">Devices</label>
        <select name="devices" id="device_selector">
        </select>
    </div>
</body>
<script>
    window.onload = () => {
        const updateAudioDeviceList = (audioDevices) => {
            const listElement = document.querySelector('#device_selector');
            listElement.innerHTML = '';
            if(audioDevices){
                audioDevices.map((audioDevice) => {
                    const audioDeviceOption = document.createElement('option');
                    audioDeviceOption.label = audioDevice.label;
                    audioDeviceOption.value = audioDevice.deviceId;
                    return audioDeviceOption;
                }).forEach((audioDeviceOption) => {listElement.add(audioDeviceOption)});
            }
        }

        const getConnectedDevices = (type, callback) => {
            navigator.mediaDevices.enumerateDevices().then((devices) => {
                var filtered = devices.filter((device) => {
                    return device.kind == type;
                });
                callback(filtered);
            }).catch((error) => {
                console.log('[Error] enumerateDevices(): ', error);
            });
        }

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

            getConnectedDevices('audioinput', displayAudioDeviceList);

            // 장치가 새로 추가되거나 제거되면 아래 이벤트 핸들러가 호출된다.
            navigator.mediaDevices.addEventListener('devicechange', (event) => {
                console.log('onDevicechange');
                getConnectedDevices('audioinput', updateAudioDeviceList);
            });
        }
    }
</script>
</html>

 

 

 

댓글