JJH's Blog

hamburger

i-hate-scale(1)-메트로놈

2023-05-17

위 문서는 i-hate-scales.online 사이트 중 메트로놈 기능을 만드는 과정을 기록한 문서입니다
메트로놈 기능을 구현 하는데 있어서 많은 아이디어와 시행착오가 있었다
메트로놈은 일정한 박자로 음을 연주해 박자감각을 키우는데 도움을 주는데
막상 이 기능을 직접 구현하는데는 많은 어려움이 있었다

접근방법

결론부터 말하자면 Web Audio API를 사용하면 된다
Web Audio API는 기존 Audio Element를 확장한 개념이라고 생각하면 된다
Audio Element에서는 조작하기 힘든 여러가지 이펙트를 줄 수 있으며
더 정확한 연주가 가능해 웹게임에도 사용된다고 한다
이펙트로는 Gain 이나 다이나믹 컴프레서와 같은 다양한 이펙트를 줄 수 있다
notion-img
Web Audio API는 그래프로 구현 되어 있어서
버퍼소스(원본 음성) 노드에 gain 노드 등 여러 노드들을 연결 시켜 결과물(destination) 을 출력 할 수
있게 되는 것이다
더 자세한 Web Audio의 설명은 해당 링크에서 볼 수 있다

접근 방법

먼저 메트로놈이 뭔지 생각해보자
메트로놈은 일정한 속도로 음악의 빠르기를 나타내는 기계로
BPM 단위를 사용 하는데 이는 1분당 몇번의 비트를 치는지를 나타낸다
즉 60BPM은 60초에 60번의 비트가 재생되는거고
초당 1번씩 비트를 친다는 의미이다
즉 60 / BPM 초당 한번씩 비트를 재생하면 훌륭한 메트로놈이 완성된다는 의미이다

Web Audio Class 작성

먼저 첫번째 비트와 나머지 비트를 구분지어주기 위해 BeatCell 클래스를 만들어 준다
constructor 에서는 오디오 컨텍스트의 destination(outputNode : 재생시켜주는 역할을 한다)과
audioBuffer를 받아 객체에 저장 해주고
addSchedule에서는 오디오가 재생될 시간 매개 변수인 start를 받고
오디오 버퍼를 버퍼 소스로 만들어 메모리에 저장한다 버퍼 소스는
일반적으로 audio를 재생하는 방법보다 더 정확하고 빠르게 재생된다
그리고 생성된 버퍼 소스를 audio node 와 연결해 재생 시간과 중지 시간을
start 와 stop 함수를 통해 명시 해준다
우리는 이제 60 / BPM 시간만큼의 스케줄을 주기적으로 추가시켜주면
메트로놈을 완성 할 수 있게 된다

메인 코드

실제 코드를 살펴보자
깃허브 코드를 받아 실행시켜주면 실제 잘 작동 되는 메트로놈을 볼 수 있다
코드가 상당히 긴데 중요한 부분은 딱 2가지이다
decodeAudioFiles 함수와
beatInterval 함수 이 두가지다

decodeAudioFiles

이 함수에서는 로컬 오디오 파일을 버퍼 파일로 만들어 준다
이 과정을 거쳐야 온전히 Web Audio API를 사용 할 수 있고
안해도 사용은 가능하나 기능사용이 제한적이다

beatInterval

제일 중요한 핵심 함수 인데 이벤트 루프로 인해 불안정한 setInterval을 사용한다 하더라고
이 함수와 같은 방법을 쓰면 보다 정확한 메트로놈을 만들 수 있다
먼저 interval 값은 100으로 설정 하였는데 최대 300BPM(60 / 300 = 0.2) 까지만 설정 할것이기에
0.1초당 한번씩 interval을 실행 하기로 했다
nextNoteTime 은 노트가 재생될 시간이다
0이면 0초에 비트를 재생하고 1이면 1초에 비트를 재생한다
audioContext.currentTime 으로 먼저 nextNoteTime를 초기화 해주는데
이는 audioContext가 실행되자마자 currentTime이 갱신되기에
메트로놈이 처음 실행될때 audioContext.currentTime 과
nextNoteTime 을 맞춰주는 과정이다
이후 반복문을 통해 nextNoteTime 와 audioContext.currentTime 을 비교해
nextNoteTime 이 더 작은경우 스케줄을 추가 하고 nextNoteTime을 갱신하다
다시 정리해 보자면
BPM이 300이라고 했을때 60/300 이니 0.2가 나오게 되고
nextNoteTime 이 0 이고 audioContext.currentTime 이 0 + 0.1 일때
반복문이 참이 되니 0.2초에 스케줄이 등록되고
nextNoteTime 은 0.2가 된다 이 후 audioContext.currentTime 가 0.2가 되었을때
다시 다음 비트가 스케줄에 등록되는 방식이다
이를 통해 훌륭한 메트로놈을 만들 수 있게 되었다

마치며

메트로놈을 만들기 위해 2-3일 정도 고민을 했었고