본문 바로가기
개발(Development)/Media(오디오&비디오 개발)

[자바스크립트] Web Audio API 기본 원리와 예제 코드

by 카레유 2022. 1. 10.

# 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는 소리에 대한 이해가 선행되지 않으면 사용하기 어려운 API들이 많지만,

기본적인 효과 및 분석 작업은 일반 개발자들도 접근할 수 있다고 생각한다.

 

그래서 정리해본다.

 

Web Audio API!

 

소리에 문외한이었던 나는 몇 일을 고생했지만,

이렇게 정리해두면 많은 분들의 시간이 절약되고 이해를 도울 수 있다는 생각으로 정리해둔다.

 

※ 소리의 기본 원리에 대해서는 아래 글들을 참고하자!

소리란? 소리의 기초 원리 (feat. 마이크와 스피커의 작동 원리)

소리의 진폭, 주기, 주파수, 파형 (볼륨, 음정, 음색의 결정 원리)

오디오 데이터 추출 및 변환: ADC, 샘플링, 샘플레이트, 채널

오디오 파형 분석(FFT: 고속 푸리에 변환): Time Domain Data to Frequency Data

 

※ 스트리밍, 버퍼링, 파일 등에 대해서는 아래 글을 참고하자!

스트리밍(Streaming)이란: 스트림(Stream), 버퍼(Buffer) 원리

자바스크립트 버퍼(Buffer): ArrayBuffer, TypedArray 파헤치기!

자바스크립트 File API 파헤치기: Blob, File, FileReader, FileList, BlobURL

 

 자바스크립트가 미디어를 다루는 방식에 대해서는 아래 글을 참고하자!

자바스크립트 Media Capture and Streams API: MediaStream, MediaStreamTrack

자바스트립트 Media Stream Recording API: MediaRecorder

[자바스크립트] MediaStream 예제 코드: 마이크, 카메라, audio, video, canvas

[자바스크립트] 마이크 음성 소리 녹음 방법 : MediaRecorder

[자바스크립트] 카메라 영상 녹화 방법 (+무음 비디오 촬영)

[자바스크립트] audio/video 태그에서 재생 중인 소리/영상 녹음 방법

 

 HTML Audio 엘리먼트 객체 기본 사용방법에 대해서는 아래 글을 참고하자!

HTML/JS: Audio 객체 음악 파일 제어 및 사운드 재생/정지 방법

Javascript: Audio 사운드 하나를 연속하여 반복/중복 재생하는 방법

 

 

물론 위의 내용들을 몰라도 아래의 글을 충분히 이해할 수 있을 것이다.

 

자! 그럼 시작해보자!


# Web Audio API의 기본 원리

Web Audio API를 통해 오디오를 다루는 원리는 간단하다.

1)음원 "소스"를 입력받아, 2) 오디오 관련 "작업"들을 하고, 3) 작업 결과물을 "목적지"(스피커 등)로 출력한다.

▶ 음원 입력(소스) -> 설정/분석(작업) -> 스피커 출력(목적지)

 

출처: MDN

 

이를 위해 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);

 


※ 참고: 소스 Node 상속구조 

 

AudioNode

ㄴ MediaElementAudioSourceNode

 MediaStreamAudioSourceNode

 AudioScheduledSourceNode 
    ㄴ OscillatorNode

    ㄴ AudioBufferSourceNode

 

1) AudioNode 만 상속 받는 MediaElementAudioSourceNode, MediaStreamAudioSourceNode

- AudioNode에는 start(), stop()가 정의되어 있지 않기 때문에, Audio, Video 객체나 HTML audio, video태그를 통해서만 간접적으로 음원 재생을 컨트롤 할 수 있다.

- Audio, Video 객체를 통해 재생이 관리되므로, 여러 번 재생이 가능하다.

 

2) AudioNode > AudioScheduledSourceNode 순으로 상속받는 OscillatorNode, AudioBufferSourceNode

- AudioScheduledSourceNode에 정의된 메서드인 start(), stop()를 상속받아 직접 음원의 재생/정지를 컨트롤 할 수 있다.

- AudioScheduledSourceNode 타입의 소스Node 객체는 단 1회만 재생되고, 가비지컬렉터에 의해 제거 된다.


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한 객체를 반환하므로, 체인식으로 계속 직렬 연결해나가면 된다.


여기까지 Web Audio API의 기본에 대해 정리해 보았다.

다음 글 부터는 각 Node들을 예제 코드와 함께 정리해볼 예정이다.

 

[자바스크립트] Oscillator Node 사용 방법 & 예제코드

 

 

댓글