
원본 출처 : https://curryyou.tistory.com/451
[자바스크립트] Web Audio API 기본 원리와 예제 코드
# Web Audio API란? Web Audio API란 오디오 데이터를 입력받아, 각종 처리 및 분석을 수행할 수 있게 해주는 API이다. Web Audio API에서 다룰 수 있는 소리 데이터는 아래의 4가지가 있다. 1) Oscillator: 주파수,
curryyou.tistory.com
자바스크립트에서 미디어를 다루는 주요 API 4가지
1. Media Capture and Streams API (=Meida Stream API)
- 마이크, 카메라 등을 이용해 들어오는 (Media Stream: 오디오, 비디오, 화면 등) 데이터를 다룬다.
2. MediaStream Recording API
- 미디어 스트림(Media Stream: 오디오, 비디오, 화면 등) 데이터를 녹음/녹화 한다.
3. Web Audio API
- 오디오 데이터를 처리/분석하는 API이다.
- 오디오 데이터로 사용될 수 있는 음원은 MediaStream(마이크 소리 등), 오디오 파일(mp3 등), 컴퓨터로 직접 만든 소리(Oscillator) 등이 있다.
4. Web RTC
- Web Real-Time Communication의 약자로, 브라우저 간에 데이터를 주고 받을 수 있게 해준다.
Web Audio API란?
Web Audio API란 오디오 데이터를 입력받아, 각종 처리 및 분석을 수행할 수 있게 해주는 API이다.
Web Audio API에서 다룰 수 있는 소리 데이터는 아래의 4가지가 있다.
1) Oscillator: 주파수, 볼륨, 파형을 계산해 만들어내는 소리
2) AudioBuffer: 짧은 오디오 데이터
3) MediaElement: mp3, wav 등의 오디오 파일
4) MediaStream: 마이크 등을 통해 들어오는 스트림 객체
Web Audio API가 필요한 이유?
HTML에서 <audio> 태그를 이용하면, 오디오를 재생/정지하고 볼륨을 조정할 수 있다.
물론 오디오의 재생위치를 조정하고, 재생완료 등의 이벤트 처리도 가능하다.
하지만 딱 거기까지다. 이게 끝이다. 그 이상은 없다.
만약 사운드 아티스트들이 하는 작업을 웹에서도 수행하고 싶다면 어떻게 해야할까?
Javascript의 Web Audio API가 정답이다.
Web Audio API는 오디오를 재생/정지하는데 그치지 않고,
오디오 음원 자체를 컨트롤하고 분석할 수 있는 방법을 제공해 준다.
단순 음원 재생의 경우엔, <audio>엘리먼트를 사용하는게 성능상 가장 좋지만,
다양한 사운드 작업 및 분석이 필요하다면 Web Audio API를 사용해야 한다.
예를 들어 Web Audio API를 사용하면,
음원에 다양한 이펙트 효과를 줄 수 있으며,
음원의 주파수 데이터 취득을 통한 Pitch(높낮이), Gain(볼륨) 등의 계산도 가능하다.
특히 마이크 등을 통해 스트림으로 들어오는 소리에 대한 분석도 가능하다는 장점이 있다.
Web Audio API의 기본 원리
Web Audio API를 통해 오디오를 다루는 원리는 간단하다.
1)음원 "소스"를 입력받아, 2) 오디오 관련 "작업"들을 하고, 3) 작업 결과물을 "목적지"(스피커 등)로 출력한다.
▶ 음원 입력(소스) -> 설정/분석(작업) -> 스피커 출력(목적지)

이를 위해 Web Audio API는 "소스", "작업", "목적지" 역할을 하는 객체들을 각각 제공하는데,
이 객체들은 모두 AudioNode를 상속받으면서 각자의 역할을 추가로 구현해둔 녀석들이다.
그리고 이 모든 Node들은 AudioContext라는 객체를 통해 생성/참조 된다.
그래서 Web Audio API를 다루는 작업의 시작은 AudioContext를 생성하는데서 시작된다.
1. AudioContext를 생성하고,
2. AudioContext를 통해 "소스", "작업", "목적지" 역할을 하는 AudioNode들을 만들어 필요한 일을 하고,
3. 소스, 작업, 목적지 노드들을 "연결"하여 최종 출력하는 방식이다.
*참고: AudioContext는 window의 프로퍼티이므로 전역에서 사용할 수 있다.
다시 한번 정리하면,
소스 노드를 생성해 음원을 입력하고,
작업 노드를 생성해 오디오 관련 작업을 수행하고,
소스노드 ~ 작업노드 ~ 목적지노드를 연결해 출력해주면 된다.
* 이런 노드들의 연결을 "오디오 그래프(Audio Graph)" 라고 한다.
각 노드들의 "생성"은 AudioContext가 제공하는 createXXX() 메서드를 통해 수행하고,
각 노드들의 "연결"은 각 노드들이 보유한 connect() 메서드를 통해 수행한다.
* 각 노드들은 생성자를 통해서도 생성할 수 있다.
Web Audio API 작업 순서
Web Audio API 작업 순서는 아래와 같다.
1. AudioContext 객체 생성
2. "소스 Node" 생성 및 음원 입력
3. "작업 Node" 생성 및 작업 수행
4. "목적지 Node" 까지 연결(오디오 그래프)
*소스Node, 작업Node, 목적지Node라는 단어는 편의를 위해 사용하는 명칭으로, 공식 명칭은 아니다.
거두 절미하고 간단한 예제코드 부터 살펴보자.
개발자는 코드를 보는게 더 빠를 때가 많으니까.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>재생 시작</button>
</body>
<script>
// 1. AudioContext 생성
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
// 2. 오디오 소스(OscillatorNode) 생성: 특정 주파수의 음을 재생하는 소스 노드
const oscillatorNode = audioContext.createOscillator();
// 3. 볼륨 설정을 위한 오디오 노드(GainNode) 생성
const gainNode = audioContext.createGain();
gainNode.gain.value = 0.5 // 볼륨 설정 (0~1)
// 4. 오디오 그래프 연결
oscillatorNode.connect(gainNode).connect(audioContext.destination);
// 클릭 이벤트 처리
document.querySelector("button").onclick = (event)=>{
// AudioContext 가동
audioContext.resume();
// 재생 시작
oscillatorNode.start();
}
</script>
</html>
그럼 각 단계를 하나씩 자세히 살펴보자.
1. AudioContext 객체 생성
- AudionContext는 window의 프로퍼티이므로, new AudioContext()로 생성 가능하다.
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
* 크로스 브라우저 호환성을 고려해 webkitAudioContext도 추가해준다.
2. "소스 Node" 생성 및 음원 입력
- 입력되는 소스의 타입(파일, 버퍼, 스트림 등)에 따라 사용해야 하는 "소스 Node" 들이 각각 다르다.
- 각 소스Node들은 AudioContext가 제공하는 팩토리 메서드나 각자의 생성자로 객체를 생성할 수 있다.
* 단, 일부 브라우저의 경우 생성자 방식이 지원되지 않는 경우가 있으니 주의!
1) OscillatorNode: 수학적으로 계산해 만들어내는 특정 주파수의 소리(440Hz의 사인파 등)
const oscillatorNode = new OscillatorNode(audioContext); // 생성자
const oscillatorNode = audioContext.createOscillator(); // 팩토리 메서드
2) AudioBufferSourceNode: AudioBuffer객체 (메모리에 올려서 사용하는 짧은 오디오 데이터)
const AudioBufferSourceNode = new AudioBufferSourceNode(audioContext);
const AudioBufferSourceNode = audioContext.createBufferSource();
3) MediaElementAudioSourceNode: Audio/Video객체 (mp3, wav 등의 사운드/비디오 파일)
const MediaElementAudioSourceNode = new MediaElementAudioSourceNode(audioContext);
const MediaElementAudioSourceNode = audioContext.createMediaElementSource(audio);
4) MediaStreamAudioSourceNode: MediStream객체 (마이크 소리, WebRTC 등의 스트림)
const MediaStreamAudioSourceNode = new MediaStreamAudioSourceNode(audioContext);
const MediaStreamAudioSourceNode = audioContext.createMediaStreamSource(stream);
3. "작업 Node" 생성 및 작업 수행
- 작업 종류에 따라 다양한 "작업 Node" 들이 있으며,
- 여기서는 볼륨 조정, 오디오 데이터 분석들을 위한 GainNode, AnalyserNode만 정리한다.
* 이 외에도 BiquadFilterNode, ConvolverNode 등이 다양한 "작업 노드"들이 있다.
* 단, 일부 브라우저의 경우 생성자 방식이 지원되지 않는 경우가 있으니 주의!
1) GainNode: 볼륨 관련 노드
const gainNode = new GainNode(audioContext, {gain: 0.5}); // 생성자 방식
const gainNode = audioContext.createGain(); // 팩토리 메서드 방식
gainNode.gain.value = 0.5 // 볼륨 조정: 0~1
2) AnalyserNode: 음원 데이터 분석 노드
- Time Domain, Frequnecy 오디오 데이터를 확인할 수 있는 노드이다.
- 이 정보를 이용하면 오디오의 볼륨 크기(Gain), 음 높낮이(Pitch) 등을 계산해낼 수 있다.
- AnaylserNode는 음원 분석 용도이므로, 목적지(Destination)를 연결해주지 않아도 상관 없다.
const analyserNode = new AnalyserNode(audioContext); // 생성자 방식
const analyserNode = audioContext.createAnalyser(); // 팩토리 메서드 방식
▶ Time Domain Data를 취득하려면 아래의 메서드를 사용한다.
analyserNode.getByteTimeDomainData(array: Uint8Array)
analyserNode.getFloatTimeDomainData(array: Float32Array)
=> 특정 시간 동안의 진폭(amplitude)값들을 인자로 들어온 배열에 복사한다.
▶ Frequency Data를 취득하려면 아래의 메서드를 사용한다.
analyserNode.getByteFrequencyData(array: Uint8Array)
analyserNode.getFloatFrequencyData(array: Float32Array)
=> 특정 시점에 분석한 Frequency별 진폭(amplitude)값들을 인자로 들어온 배열인자에 복사한다.
이에 대해서는 진폭, 주파수, fft 등에 대한 기초이해가 필요하다.
4. "목적지 Node" 까지 연결(오디오 그래프)
- 소스노드 ~ 작업노드 ~ 목적지노드 로 연결을 해주어야 "소스"에 "작업"된 최종 결과물이 "목적지"로 출력된다.
- "목적지노드"는 AudioContext.destination 으로 참조할 수 있으며,
- "연결"은 각 AudioNode가 보유한 connect()메서드를 통해 해준다.
sourceNode.connect(gainNode).connect(analyserNode).connect(audioContext.destination);
* 노드.Connect(파라미터)는 "노드"에다가 "파라미터"까지 connect한 객체를 반환하므로, 체인식으로 계속 직렬 연결해나가면 된다.
'STUDY > JavaScript' 카테고리의 다른 글
[JS] XMLHttpRequest (1) | 2023.04.19 |
---|---|
[JS] URL생성자 기초 정리 (0) | 2023.04.12 |
[JS] Media Stream API (0) | 2023.04.03 |
[JS] audio 태그 제어하기 (0) | 2023.03.30 |
[JS] 정규표현식 공백 체크하기 (0) | 2023.03.24 |