[KR] Speed Watermelon 개발 일지 2 - 오디오
Audio Asset Management
게임에서 오디오는 중요한 요소 중 하나입니다. 게임에서는 효과음, 배경음악 등 다양한 오디오를 사용합니다. Unity, Unreal Engine, Godot 등의 게임 엔진을 사용하면 게임 오디오를 쉽게 관리할 수 있는 것으로 알고 있습니다. 하지만, 상대적으로 마이너한 웹 게임에서는 오디오 관리에 있어 Best Practice가 없습니다. 게임에서 오디오를 관리하기 위해서는 재생, 루프, 일시정지와 같은 당연한 기능은 물론 다음과 같은 요소들을 고려해야 합니다.
- 호환성을 위해 다양한 포맷 지원
- 오디오 로딩 (Preloading)
- 오디오 풀링 (Audio Pooling)
- sprite 지원
- 볼륨 조절
- 오디오 페이드인/페이드아웃
- 오디오 믹싱
그리고 웹에선 가장 중요한 부분이 Autoplay policy입니다. 웹사이트에 접속했을 때, 자동으로 오디오가 재생되는 것을 방지하는 정책입니다. 오디오를 재생하기 위해서는 반드시 사용자의 인터랙션을 필요로 합니다. 한번 사용자의 인터랙션으로 오디오 재생에 성공했으면, 그 이후에는 오디오를 자동으로 재생할 수 있습니다. 자세한 내용은 Google Developers Blog 참고
처음에는 아무것도 모르고 포맷, preloading, pooling 등 필요한 부분을 하나씩 개발하다 생각보다 많은 기능이 필요함을 느끼게 되어 자바스크립트 오디오 라이브러리로 유명한 Howler.js를 사용했습니다. Howler.js는 웹 오디오 라이브러리로, Web Audio API를 이용하여 오디오를 재생할 수 있습니다. (Fallback으로 HTML5 Audio를 사용). Autoplay 역시 해결해 주는 기능을 제공합니다.
웹 오디오 재생
일단 오디오를 브라우저에서 플레이하기 위해서는 2가지 방법이 있습니다.
1. <audio> 태그
자바스크립트로 다음과 같이 사용할 수 있습니다.
const audio = new Audio('audio.mp3');
audio.play();
2. Web Audio API
Web Audio API는 <audio>
태그보다 더 많은 기능을 제공합니다. 예를 들어, 오디오를 믹싱하거나, 오디오를 실시간으로 생성할 수 있습니다. Low level API이기 때문에, 더 많은 기능을 제공하지만, 사용하기에는 더 복잡하기 때문에 라이브러리를 사용하는 것이 나을 수 있습니다.
Web Audio API는 Audio 태그에 비해 퍼포먼스가 좋다는 장점도 있습니다. Audio 태그는 환경에 따라 재생시 레이턴시가 발생하는 문제도 있습니다.
// Web Audio API 예시
const audioContext = new AudioContext();
const source = audioContext.createBufferSource();
const response = await fetch('audio.mp3');
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start();
문제점
iOS Safari에서는 Web Audio API를 ringer channel로 분류하고 있습니다. 따라서, 기기가 음소거(Mute) 상태일 때, 오디오가 재생되지 않습니다. 이는 사용자 경험에 큰 문제가 될 수 있습니다. 보통 기기를 음소거 모드로 설정하더라도, 미디어 콘텐츠는 따로 볼륨이 있기 때문에 사용자가 음소거 모드로 설정했을 때도 오디오가 재생되어야 합니다.
audio 태그의 경우 정상적으로 미디어로 분류가 되어 음소거 모드에서도 재생이 됩니다. 하지만 audio 태그는 또 다른 문제가 있습니다. iOS Safari는 audio 태그의 볼륨을 무시합니다. 기기의 볼륨 하나로 전체 볼륨을 조절하라는 의미로 만든 정책이라고 합니다. 이는 결국 오디오별로 볼륨을 조절할 수 없다는 의미입니다. 게임에서는 효과음과 배경음악의 볼륨을 다르게 조절해야 하는 경우가 많은데 이는 audio 태그를 사용할 때 문제로 다가옵니다.
해결책
Ringer channel에서 재생되고 있는 WebAudio를 음소거 상태에서도 재생되게 하기 위해서는 media channel로 분류가 되어야 하는데요. 커뮤니티에서 사람들이 찾아낸 해결 방법이 있습니다.
WebAudio가 재생될 때 html audio를 동시에 재생하면 WebAudio가 media channel에서 재생되게 됩니다. 이를 활용해 소리가 없는 audio 태그를 만들어 놓고, WebAudio가 재생되고 있는 동안에는 이를 계속 재생하는 것입니다.
약간 우회해서 해결하는 방법이라 완벽하지는 않지만, 현재로서는 이 방법 외에는 없는 것으로 알고 있습니다.
참고: unmute.js, unmute-ios-audio, howler.js issue #753