 Hi everyone이 아니라 안녕하세요. Chrome browser 웹 오디오 팀의 태클리드를 맡고 있는 최홍찬이라고 합니다. 이 비디오는 2021년 모국어의 날을 기념하기 위해 특별히 한글로 제작된 튜토리얼입니다. 오늘 만들어볼 웹앱은 웹 오디오 API를 이용한 드럼 머신입니다. 본격적으로 시작하기 전에 내용의 나안 이도와 목표를 분명하게 해보죠. 우선 이 짧은 튜토리얼은 웹 오디오에 대한 경험이 없는 웹 개발자 분들을 위해 만들어졌습니다. 그리고 오늘 설명할 내용의 사용자 인터페이스 디자인은 포함되어 있지 않습니다. 먼저 이 튜토리얼의 최종 결과물을 한번 살펴보도록 하죠. 보시다시피 아주 단순한 드럼 머신으로 키보드를 눌러 드럼 소리를 내는 것입니다. 이 기본적인 엔진만 잘 다져놓는다면 이 위에 미디를 연결한다거나 혹은 잘 디자인 된 사용자 인터페이스를 얹어서 프로그레스브 웹 앱으로 만들 수도 있겠죠. 웹 오디오 API를 사용하면 신지사이저단 샘플러 등 다양한 형태의 전자학기를 만들 수 있지만 오늘 우리의 포커스는 오디오 샘플을 이용한 드럼 머신입니다. 혹시 오디오 샘플이라는 말이 생소하시다면 여기서 샘플이라 하면 어떤 소리가 녹음된 짧은 디지털 오디오 파일로 생각하시면 됩니다. 다시 말해 드럼 샘플은 드럼 소리가 녹음된 오디오 파일이 되겠죠. 저는 오늘 12개의 드럼 소리가 담긴 샘플과 잔향 효과를 위해 임펄스 응답 샘플 하나를 사용할 겁니다. 파일명이 드럼으로 시작하는 파일들은 드럼 샘플 드리고 그리고 아이알로 시작하는 파일이 임펄스 응답 파일입니다. 자 그럼 본격적인 코딩에 앞서 웹 오디오 API의 기본적인 개념에 대해서 잠깐 언급하고 넘어가겠습니다. 웹 오디오 API는 미디어 알레멘트로는 불가능한 복잡하고 인트랙티브한 오디오 기능을 구현하고자 할 때 사용할 수 있습니다. 대표적 적용 사례로는 웹 게임, 화상회의, 뮤직 프로덕션 및 VR, AR을 위한 입체음향 효과 등이 있죠. 그리고 웹 오디오 API는 그래프에 기반한 오디오 프로그래밍 API입니다. 마치 레고와 흡사한 면이 있죠. 우선 오디오 누드라고 불리우는 다양한 요소들이 있고 그리고 이 요소들을 서로 연결해서 그래프를 만듭니다. 이러한 과정을 통해 신 디사이저 샘플러, 레코러, 이콜라이저, 에코, 디스토션과 같은 다양한 음영 처리 장치를 구현할 수 있습니다. 일단 가장 간단한 사인 파영의 소리를 내보도록 하죠. 아주 단순한 HTML 파일에서 시작을 해보겠습니다. 하나의 버튼과 하나의 스크립트 파일이면 충분합니다. 이 예제에서는 3가지의 오디오 누드를 사용해 보겠습니다. 오실레이터, 개인, 그리고 데스티니에이션 누드를 직렬로 연결하겠습니다. 웹 오디오에는 3종류의 오디오 누드가 있는데요. 바로 source node, processing node, 데스티니에이션 누드가 그것입니다. source node는 소리를 만들고, processing 소리를 처리하고 데스티니에이션은 만들어진 오디오를 추적하는 일을 합니다. 그럼 코드를 한번 살펴보죠. 우선 오디오 컨텍스트 오브젝트를 만들어야 됩니다. 오디오 컨텍스트는 모든 웹 오디오 기능의 모체가 된 오브젝트로 다양한 오디오 누드를 만들어내는 일을 비롯하여 오디오 재생을 시작하거나 멈출 수 있습니다. 그리고 오실레이터와 개인 node를 만들고 있고요. 마지막으로는 source node가 샘플을 재생할 수 있도록 Start method를 호출합니다. 그리고 가장 중요한 포인트, 브라우즈 탭이 소리를 재생할 수 있기 위해서는 반드시 사용자의 입력, 유저 제스처가 필요합니다. 그래서 버튼을 하나 만들고 이 버튼이 눌러졌을 때 오디오 컨텍스트가 재생될 수 있도록 합니다. 자, 가장 간단한 예지를 살펴봤으니 이제 본격적인 드럼 머신 만들기로 들어가 보죠. 우선 drum cell이라는 이름의 클래스를 하나 만들겠습니다. 이 클래스가 하는 일은 할당된 오디오 버퍼 오브젝트를 가지고 오디오 버퍼 source node와 개인 node를 연결해서 소리를 내는 겁니다. 오디오 버퍼 오브젝트는 디지털 오디오 데이터, 즉 오디오 파일을 읽어드려서 메모리에 저장하는 기능을 합니다. 이렇게 저장된 버퍼 오브젝트는 버퍼 source node를 사용해서 재생할 수 있죠. 여기서 웹 오디오 장점에 하나 버퍼 source와 source node를 사용하면 오디오 element을 사용하여 오디오 파일을 재생하는 것보다 훨씬 빠르고 정확하게 재생할 수 있습니다. 따라서 게임 사운드나 악기 등을 제작할 때 제격이라고 할 수 있죠. 웹 오디오의 또 다른 장점은 오디오 node를 필요에 따라서 자유롭게 생성하거나 소멸할 수 있으며 사용자가 node의 소멸을 직접 관리할 필요가 없다는 것입니다. 예를 들면 source node를 만들어서 오디오 버퍼를 재생하고 그리고 이후 node에 대한 참조가 모두 사라지고 소리의 재생이 끝나면 자연스럽게 시스템에 의해 가비지 컬렉션이 됩니다. 즉 사용자가 소리가 언제 끝나는지 그런 것들을 신경 쓰지 않아도 됩니다. 앞서 보신 다이어그램을 함수로 만들어보면 대략 이런 모양이 될 겁니다. 이전 예제와 달라진 것은 버퍼를 건네 받아서 버퍼 source를 생성한다는 것 뿐입니다. 버퍼 source를 만들고 오디오 node에 연결하고 그리고 데스네이션으로 연결되고 있습니다. 마지막으로는 샘플을 재생하기 위해 start method를 호출하고 있고요. 자 이제 소리를 재생한 최소한의 구조를 마련했으니 이를 바탕으로 드럼 셀클래스를 한번 만들어 보죠. 일단 분리성을 확보하기 위해 오디오 output node와 오디오 버퍼를 변수로 하는 컨스트럭터를 만들고 object 내에 이 변수들을 저장해 두도록 합니다. 샘플을 재생할 때마다 이 두 가지는 반드시 필요하니까요. 참고로 모든 오디오 node들은 해당 오디오 컨텍스트에 대한 참조를 갖고 있으니 output 컨텍스트를 가져와 local object에 저장해 두겠습니다. play sample method는 앞서 봤던 함수와 거의 다를 바가 없습니다. dist라는 변수의 의조나요 저장된 object들을 참조하고 있다는 건 말거든요. 자 이제 그럼 한번 테스트를 해봐야 할 텐데요. 여기서 조금 문제가 있네요. 바로 재생할 오디오 버퍼가 없습니다. 이 오디오 버퍼를 누군가는 만들어져서 건네줘야 되겠죠. 그럼 이제 이 오디오 파일드를 로딩해서 오디오 버퍼를 만들어 주는 코드를 작성해 보도록 하겠습니다. 아마 1, 2년 전쯤 파일을 불러드리는 코드를 작성하려면 당연히 서버의 파일을 넣어두고 fetch를 사용하여 가져오는 방식을 사용했겠죠. 하지만 오늘은 조금 다른 방법을 사용해 보도록 하겠습니다. 프로젝트 후구의 일환으로 새롭게 개발된 file system access API를 사용해 local drive에 있는 파일들을 직접 로드해 보도록 하겠습니다. 앞서 보여드렸던 대로 이 샘플들은 제 local drive에 한 디렉토리에 있습니다. 우선 디렉토리 피커를 사용해서 아까 준비해둔 샘플들이 있는 디렉토리를 열어서 모든 파일을 한꺼번에 로드할 수 있도록 합니다. 그다음 디렉토리 안에 파일들을 차례로 access해서 파일명이 드럼으로 시작되면서 확장자가 mp3인 파일들만 걸러내겠습니다. 파일 이름이 적합한 경우 그 파일에 대한 핸들을 사용해 파일의 컨텐츠를 불러드려옵니다. 이 파일의 컨텐츠를 직접 오디오 버퍼 오젝트에 집어넣을 수는 없습니다. 여기서 일단 일반적인 데이터 컨테이너인 array buffer 오젝트를 추출한 뒤에 그 array buffer를 오디오 컨텍스트에 Decode Audio Data Method을 통한 Decoding, 즉 변환 과정을 거쳐야 오디오 버퍼를 얻을 수 있습니다. 이렇게 얻어진 오디오 버퍼를 사용해 아까 디자인에 둔 드럼슬 오젝트를 만들고 맵 데이터 구조에 하나하나 집어넣으면 되겠습니다. 그런데 이 파일을 가져오는 루틴은 유틸리티 함수로 만들어두면 나중에 유용할 것 같습니다. 그래서 그 부분을 따로 빼내서 정리를 해본다면 대략 이런 모양이 되겠죠. 그럼 이 유틸리티 함수를 사용해 방금 작성한 맵 빌드 함수를 조금 더 덜 복잡하게 만들어봤습니다. 이제 키보드 입력을 받는 부분을 해결을 해보겠습니다. 이제 만들어 볼 간단할 유틸리티 함수는 이전에 만들어둔 드럼셀 맵과 키 입력을 연결시킬 때 사용합니다. 일단 아까 만들어둔 드럼셀 맵에서 드럼셀 오브직트들만 배열로 추출을 하겠습니다. 그리고 하나의 키코드와 하나의 드럼셀 오브직트를 1대1로 맵핑하기 위해 또 다른 맵을 하나 만들겠습니다. 그 다음 스트링 하나를 잘게 쭉해서 배열로 만듭니다. 이 알파벳 키드를 바로 드럼 머신을 연주할 때 사용할 계획인데요. 그런데 이 랜드만 스트링이 어디서 왔는지 궁금하시겠죠. 키를 이렇게 배치를 하면 기존의 드럼 머신 및 샘플러딩의 패드 레이아웃과 비슷하게 가져갈 수 있겠죠. 그럼 마지막으로 키 바인딩 정보를 담은 맵을 사용해 키다운 이벤트 헤널러를 작성해 보겠습니다. 어떤 키 입력이 들어오면 그 키가 맵을 존재하는지 살펴보고 있다면 맵에 저장된 드럼셀을 불러와 플레이 샘플 맷호드를 호출하면 됩니다. 지금까지 드럼 샘플을 재생하는 부분과 키 입력을 처리하는 부분을 제작해 봤습니다. 그럼 이제부터는 한 단계 나아가서 드럼 샘플에 다양한 음향 효과를 넣어보도록 하죠. 우선 드럼 사운드에 빠질 수 없는 것 중 하나가 바로 컴프레션입니다. 이는 원래 소리가 가진 다이나믹 레인지 가장 조용한 부분과 가장 소리가 큰 부분의 차이를 의도적으로 줄인 뒤에 소리의 전체적인 음향을 증가시켜서 조금 더 방역 있는 소리가 날 수 있도록 합니다. 컴프레서의 대표적인 설정들에 대해 간단히 설명을 좀 해보죠. Threshold는 컴프레션이 시작될 음향입니다. 이 입력 음량이 이 파라미터보다 크면 컴프레서가 동작해서 출력 음량을 줄이게 됩니다. 음량을 얼마나 줄일 것인지는 레이쉬어에 의해 결정됩니다. 이 두 파라미터의 적절한 조합으로 소리가 가진 다이나믹 레인지를 제어를 할 수가 있죠. 마지막으로 메이크업 게이는 압축된 다이나믹 레인지를 보상해주는 역할을 합니다. 이 파라미터를 사용해 압축된 만큼 소리를 올려주면 꽤 방역 있는 소리가 나오게 되죠. 이 밖에도 니, 어텍, 리니스튼 다양한 파라미터가 있지만 오늘 이 튜토리얼에서는 다루지 않도록 하겠습니다. 이제 이 추가적인 음향 효과들을 보다 앞으로 적용하기 위해 오디오 그래프를 조금 바꿔보겠습니다. 여러 소리들이 하나로 합쳐지는 경로를 흔히 버스라고 부르고요. 그리고 우리가 사용하는 컴프레서는 그 버스의 길목에 직접 삽입이 되어야 합니다. 그런데 이런 방법을 왜 사용하는지 궁금하실 겁니다. 여러 가지 이유가 있지만요. 가장 중요한 것은 웹 오디오 api의 다이나믹 컴프레서 노드는 꽤 CPU 소모가 높다는 점입니다. 그리고 크롬의 컴프레서 디자인 제공하는 음향 처리의 질이 높은 편이라서요. 드럼 샘플의 개별적으로 적용하기 보다는 드럼 머신 전체의 최종 출력에 적용하는 소위 말하는 버스 이펙트로 사용하는 것이 보다 효과적입니다. 뿐만 아니라 모든 소리가 하나의 컴프레서를 통하도록 함으로써 전체적인 음색의 일관성도 추구할 수 있습니다. 자 그럼 다음으로 잔향 효과에 대해서 이야기를 해보도록 하죠. 큰 강당이나 콘서트 홀 등에 가서 박수를 치면 그 공간에 울림들을 들을 수 있습니다. 이 공간에 울림을 바로 잔향이라고 하죠. 잔향 효과를 구현하는 방법은 다양하지만 웹 오디오가 제공하는 가장 손쉬운 방법은 컴퍼보라고 불리우는 오디오 노드를 사용하는 것입니다. 재미있는 점은 이 컴퍼보 오디오 노드 자체로는 아무런 효과를 내지 못하구요. 바로 임퍼스 응답이라고 불리우는 오디오 파일을 오디오 버퍼로 불러들여서 컴퍼버에 할당을 해주어야 합니다. 임퍼스 응답 말이 좀 어려운데요. 좀 단순하게 설명을 하자면요. 어떤 공간 안에 가서 박수를 치면 그 공간에 울림 소리가 있겠죠. 그 울림 소리의 특성을 갖고 있는 것이 바로 이 임퍼스 응답입니다. 이렇게 녹음된 파일에는 그 공간에 특성이 잘 녹아 들어가 있겠죠. 공간 자체가 갖고 있는 고유한 음색이라든가 혹은 울림의 길고 짧음이라든가 이러한 특성을 그대로 살려서 컴퍼버 노드를 사용해서 그 공간의 잔향을 디지털로 재현할 수 있습니다. 이 컴퍼버 역시 CPU를 많이 소모하는 편이기 때문에 이 효과를 모든 버퍼버 소스에 추가하는 것은 그다지 바람직하지 않습니다. 그래서 모든 소리가 집결하는 버스 뒤에 추가하는 것이 가장 일반적인 방법입니다. 하지만 이전에 컴퍼버와는 조금 다른 방식으로 연결을 해보겠습니다. 컴퍼버에서 출력된 소리를 두 군데로 동시에 연결합니다. 하나는 데스트네이션 그리고 다른 하나는 컴퍼버로 그리고 컴퍼버의 출력에 개인 노드를 연결해서 잔향의 소리를 조절할 수 있도록 합니다. 이렇게 하면 컴퍼버의 출력과 컴퍼버의 출력 간의 비율을 조절할 수 있게 되겠죠. 그럼 이 디자인을 코드로 한번 작성해보죠. 우선은 컴퍼버를 만들고요. 그다음에 컴퍼버를 Impulse 응답 파일을 불러들인 후 만듭니다. 아까 만들어둔 파일로 Utility를 사용하면 간단하게 해결되겠죠. 최종적으로는 Compressor Node를 리터나요. 이 Node가 메인버스에 입력이 되도록 하겠습니다. 이제 모든 부분이 완성되었으니 지금까지 작성한 코드를 한자리에 모아보도록 하겠습니다. Drum cell class, file loading method, key binding 및 메인버스 생성 이 모든 걸 연결하는 코드를 작성해보면 이런 모양이 되겠죠. 지금까지 만든 함수들을 사실상 차례들을 부르기만 하면 되는 겁니다. 그리고 이 초기와 함수가 페이지 및 UI가 완전히 준비된 후에 실행될 수 있도록 몇 가지 장치를 해보겠습니다. 지금 보여드릴 코드들은 모두 Global Scope입니다. 일단 Audio Context를 하나 만들고요. 이 Audio Context는 사용자 제스처와 연결이 되어 있지 않기 때문에 생성된 뒤에도 소리가 나지 않게 됩니다. 그래서 페이지가 완전히 Loading에 완료된 후에 Audio 기능을 사용할 수 있게끔 일단 Windows Load Event Listener를 사용하겠습니다. 늘 보는 버튼 핸들링 루틴입니다. Audio Context이 페이지가 완전히 준비되기 전에 사용자가 버튼을 클릭한 일 없도록 미리 B 활성화를 해두고요. 그리고 준비가 끝났을 때 버튼이 활성화되도록 하겠습니다. 그리고 클릭 이벤트로 드럼 머신이 시작될 수 있도록 Listener를 준비해둡니다. 아까 작성해둔 드럼 머신 측이와 함수를 이 안에서 부르면 되겠고요. 이 부분은 조금 추가적인 설명이 필요할 것 같습니다. 아까 언급했던 것처럼 Audio Context는 현재 정지되어 있는 상태입니다. 그래서 버튼 클릭 이벤트에서 Rhythm Method을 불러야 Audio Context가 정상적으로 Audio Stream을 재생하기 시작합니다. 그리고 사용자가 실수로 이 버튼을 다시 누를 수 없도록 버튼을 B 활성화 시키겠습니다. 경우에 따라서는 이 버튼을 Audio 기능의 토글로도 바꿀 수 있겠죠. 자, 이제 모든 준비가 끝났으니 직접 연주를 한번 해보겠습니다. 오늘 이 튜토리얼을 통해 드럼 머신의 깊초적인 구조를 웹 오디오의 다른 기능을 사용하면 훨씬 더 재미있는 효과들을 쉽게 적용해볼 수 있습니다. 그럼 이제 중급 반의 내용을 한번 살펴보도록 하죠. 바이콧 필터는 우리가 흔히 접할 수 있는 파라메트릭 이콜라이저를 만드는 데 사용되는 기본적인 빌딩 벌럭입니다. 소리의 음색을 자유롭게 동적으로 변경할 수 있죠. 그리고 웹 오디오가 다양한 형태의 필터 타입을 제공하고 있고요. 정교한 파라메터에 제어도 가능합니다. 웨이브 쉐이퍼라는 이펙트를 사용하면 의도적으로 외국시켜서 멋 음색으로 변화시킬 수도 있죠. 마치 락이나 메탈 음악 장르에 사용되는 디스토션 기타가 그 좋은 예가 되겠습니다. 다른 멋진 이펙트 중에 하나는 3D 효과를 낼 수 있는 패너노드입니다. 이 노드의 HLTF 모드를 사용하면 소스에 3천 원 음장 효과를 줄 수 있습니다. 여기서 주의할 점은 HLTF를 이용한 효과의 특성상 헤드폰이나 이어폰을 쓸 때 그 효과를 극대화할 수 있다는 점입니다. 예를 들면 드럼 머신의 도젝트들에게 패너를 각각 추가하여 각 음원들이 서로 다른 공간적 위치에서 들릴 수 있도록 할 수 있습니다. 중급반의 마지막 내용은 MIDI 컨트롤러의 연결입니다. 오늘의 드럼 머신은 키보드로 연주할 수밖에 없다는 큰 단점이 있죠. 하지만 혹시 제 뒤에 보이는 이런 MIDI 장비들을 보여하고 계시다면 이를 쉽게 크롬과 연결하여 드럼 머신을 재생할 수 있습니다. 이제 튜토리어를 마무리하며 몇 가지 말씀을 덧붙이고 싶습니다. 웨이브 오디오가 세상에 어느덧 10년이라는 시간이 지났습니다. 길다면 긴 10년이라는 시간 동안 웨이브 오디오 API를 중심으로 다양한 비즈니스와 개발자 생태계가 유기적으로 성장하였고요. 이제는 웹게임, 비디오 컨트롤, 뮤직 프로덕션, 그리고 실험적인 예술 매치에 이르기까지 다양한 분야에서 그 소리를 들을 수 있습니다. 웨이브 오디오 API는 W3C 사나의 오디오 워킹 그룹의 주도로 HTML5의 표준의 일환으로 개발이 되었고요. 그리고 2021년 5월에 version one recommendation, 즉 스펙 문서 개발 과정의 최종 단계로 그 표준화 작업이 완료될 예정입니다. 따라서 표준은 이제 완성화되었고 현재 모든 브라우저들이 대등한 구현을 제공하고 있습니다. 오늘 이 짧은 드러머신 튜토리얼은 여기서 끝을 맺지만 웹 오디오의 가능성은 누군부진합니다. 특히 지난 3, 4년간 첫 번째 버전의 보완을 위해 도입된 오디오 워클래스는 웹 어센블리와 결합하여 브라우저를 위한 오디오 소프트웨어를 제작할 수 있는 그런 획기적인 발판을 마련하였습니다. 이 내용은 구글 아이오 2019년에 소닉 붐이라는 제목이 프레젠테이션을 통해 소개된 바가 있으니까요. 관심 있는 분들은 한번 살펴보시기 바랍니다. 마지막으로 오늘 드러머신의 코디 예제는 바로 이 링크에서 확인하실 수 있습니다. 이 비디오를 지금까지 경청해주신