본문 바로가기

자바스크립트/DOM 이론

Web Audio API 사용법, 페이드아웃 적용하기

웹에서 단순히 배경 음악 재생 정도로 그치지 않고 소리에 다양한 효과를 주려면 Web Audio API를 사용해야 한다.

 

어떻게 돌아가는지 알려면 아래 페이지에서 앞 부분을 읽어 보자.

https://www.w3.org/TR/webaudio/

 

위 페이지에서 오디오의 signal flow라 할 수 있는 라우트를 어떻게 처리하는지 이해하고

각 메소드가 어떤 역할을 하는지 훑어 보자.

 

필요한 메소드가 무엇인지 파악했다면 메소드를 어떻게 사용하는지 구글에 검색해보자.

MDN에 예제가 잘 나와 있다.

 

여기서는 Web Audio API를 이용해서 mp3 파일을 어떻게 불러오고 재생하는지와

간단한 오디오 효과로서 페이드아웃을 적용하는 방법에 대해서 다루겠다.

 

오디오를 불러오고 재생하는 방법은 다음 영상에서 잘 설명해준다.

공식 문서도 좀 이랬으면..

https://www.youtube.com/watch?v=3NgVlAscdcA

 

위 코드를 변수 이름만 좀 바꿔서 가져왔다.

 

const context = new AudioContext();

let audio;

 

fetch('./audio.mp3')

    .then(data => data.arrayBuffer())

    .then(arrayBuffer => context.decodeAudioData(arrayBuffer))

    .then(decodedAudio => {

        audio = decodedAudio // audio 변수에 mp3 파일을 불러온다.

    });

 

function play() {

    const source = context.createBufferSource(); // ①

    source.buffer = audio;

    source.connect(context.destination); // 버퍼에 추가한 오디오를 최종 output인 destination에 연결한다.

    source.start(context.currentTime); // 재생

}

 

이렇게 재생할 때마다 버퍼 소스를 만들어야 하는데 이 과정에 리소스가 아주 적게 들어간다고 어느 블로그에서 봤던 것 같다.

 

이제 play 함수를 호출하면 audio.mp3 파일이 재생된다.

 

오디오 파일을 재생하다가 멈추면 클릭 또는 틱이라는 노이즈가 발생한다.

이는 파형이 진행중이다가 갑자기 소리가 사라져 파형이 절벽처럼 되는 바람에 생긴다.

파형을 떡 썰듯 자르는 것과 같다.

 

때문에 0점으로 서서히 볼륨을 내려주는 방법을 써야 자연스럽게 소리가 사라지고 이를 페이드아웃이라고 한다.

Web Audio API를 이용하지 않고도 setInterval 메소드를 써서 ms 단위로 점점 0점에 이르게 할 수도 있지만

소리 파일은 ms 보다도 더 정교하게 기록돼 있기 때문에

마치 픽셀이 살짝 깨진 그림 파일이 거슬리는 것처럼, 거슬리는 작은 노이즈가 생긴다.

페이드아웃을 완벽히 구현하려면 Web Audio API를 써야 한다.

(물론 소리 파일을 재생 중에 멈출 일이 없다면 페이드아웃은 필요없다.)

 

페이드아웃을 적용하려면 볼륨을 제어해줄 gainNode를 만들어야 한다.

const gainNode = context.createGain(); // context로 생성한다.

gainNode.gain.setValueAtTime(0.5, context.currentTime); // 초기 볼륨을 설정한다.

 

위의 예제 중 play 함수를 보면 재생시 오디오 소스의 아웃풋을 바로 destination에 연결해 재생하고 있다.

둘 사이에 gainNode를 넣는다.

오디오 소스의 아웃풋이 gainNode로 들어가고 gainNode의 아웃풋이 destination으로 가게 하자.

source.connect(gainNode);

gainNode.connect(context.destination);

 

이제 gainNode를 이용해서 볼륨을 조절할 수 있다.

기존에처럼 뚝뚝 끊어지지 않고 선형으로, 미세하게 볼륨을 조절하기 위해서는 linearRampToValueAtTime이라는 메소드를 쓴다.

exponentialRampToValueAtTime라는 메소드도 쓸 수 있는데 어떻게 쓰는지 몰라서인지 노이즈가 생겨서 버렸다.

 

재생 중 특정 시간에 이 메소드를 호출해서 소리를 자연스럽게 줄일 수 있다.

gainNode.gain.linearRampToValueAtTime(0, context.currentTime + 2); // 2초 동안 페이드아웃이 된다!

 

소리가 사라졌으니 재생을 멈추는 건 센스

페이드아웃보다 먼저 끝나지 않도록 0.1 초만큼 여유 시간을 줬다.

source.stop(context.currentTime + 2 + 0.1);

 

이를 바탕으로 play 함수와 fadeout 함수를 구현한다면 이렇게 된다.

 

let source;

let gainNode;

 

function play() {

    source = context.createBufferSource(); 

 

    source.buffer = audio; // 불러온 오디오를 버퍼에 넣는다.

   

    gainNode = context.createGain(); // gainNode를 생성한다.

    gainNode.gain.setValueAtTime(0.5, context.currentTime); // 초기 볼륨을 설정한다.

 

    source.connect(gainNode); // 오디오 소스를 gainNode에 연결

    gainNode.connect(context.destination); // gainNode를 최종 아웃풋에 연결

    source.start(context.currentTime); // 재생

}

 

function fadeout() {

    gainNode.gain.setValueAtTime(0.5, context.currentTime); // 초기 볼륨과 맞춰놓고 페이드아웃에 넘어간다.

    gainNode.gain.linearRampToValueAtTime(0, context.currentTime + 2); // 2초 동안 페이드아웃이 된다.

    source.stop(context.currentTime + 2 + 0.1); // 페이드아웃이 끝나고 0.1초 후에 재생을 종료한다.

}

 

이상의 코드를 사용하여 버튼을 눌러 play 함수와 fadeout 함수를 실행할 수 있는

간단한 웹 앱을 만들었으니 아래 깃허브 링크에서 참고 바란다.

github.com/ipf-gwkim/web-audio-API-play-and-fadeout