본문 바로가기
웹 오디오 프로그래밍

[Web Audio API #4] 재생-멈춤-재생

by 영바이트 2021. 6. 28.

 

웹 브라우저 혹은 웹 어플리케이션에서 오디오를 재생하기 위해 오디오 그래프audio graph를 구성하고 오디오 그래프 소스 노드의 start(), stop() 메서드를 호출해서 오디오를 재생하고 정지시킨다.

// decode 대상 buffer는 서버로 부터 받은 blob, 즉 파일 buffer를 의미한다.
audioContext.decodeAudioData(buffer, (buffer) => {
	audioBuffer = buffer;
});

//	source - destination(=오디오 출력 장치)으로 이루어진 오디오 그래프
source = audioContext.createBufferSource();
source.buffer = audioBuffer;

source.connect(audioContext.destination);

source.start(0);	//	0은 지금을 의미한다
source.stop();	//	오디오 재생을 멈춘다

 

stop() 메서드를 호출해서 재생을 멈추면 같은 소스 노드의 start() 메서드를 다시 호출할 수 없다(오류가 발생한다).

source.start(0);	//	0은 지금을 의미한다
source.stop();	//	오디오 재생을 멈춘다
source.start(0);	//	오류

 

한 번 stop() 메서드가 호출된 소스 노드의 start() 메서드를 다시 호출 할 수 없는 이유는 소스와 소스 버퍼를 구분하는 오디오 그래프의 특성 때문이다.

audioContext.decodeAudioData(buffer, (buffer) => {
	audioBuffer = buffer;
});

source = audioContext.createBufferSource();
source.buffer = audioBuffer;

 

위 코드에서 audioBuffer(=오디오 데이터)는 여러 번 재사용될 수 있지만 소스 노드 source는 stop() 메서드가 호출되면 다시 start() 메서드를 호출할 수 없다. 그러면 음악을 잠시 멈춘 후 이어서 재생하려면 즉, play - pause - play 패턴을 만드려면 어떻게 해야할까?

 

play - pause - play 패턴을 만들기 위해서는 stop() 메서드 호출 이후에 동일한 오디오 데이터를 사용하는 다른 오디오 소스를 만들어서 사용해야 한다. 그리고 새로 만든 소스 노드의 start() 메서드에 시작할 위치를 인자로 전달해준다.

source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);

startTime = audioContext.currentTime;
source.start(0, elapsedTime);	//	start(시작 시점, 시작 위치)

 

시작 위치(이어서 재생할 위치)인 elapsedTime은 stop() 메서드를 호출할 때 계산해 놓는다.

elapsedTime = (elapsedTime + audioContext.currentTime - startTime)%source.buffer.duration;
source.stop();

 

여기서 audio context의 currentTime 개념이 등장하게 된다. audio context는 오디오 그래프를 포함하는 객체이고 audio context의 상태가 'running'인 동안 audio context의 currentTime은 계속 증가한다. 따라서 오디오의 시작 시점과 멈춤 시점을 이 currentTime을 통해 알 수 있고, '멈춤 시점 - 시작 시점'이 재생한 오디오 시간이므로 이 시간 이후의 오디오 데이터를 재생하도록 하면 잠시 멈춤 효과를 구현할 수 있다. 아래와 같이 그림으로 정리할 수 있다.

 

 

코드의 계산식에서는 재생 - 멈춤 - 재생이 여러 번 반복될 수 있기 때문에 기존 재생 시간인 elapsedTime을 더해 주었고, 여기에 더해 오디오가 계속 반복 재생(loop)될 수 있기 때문에 오디오 데이터의 길이인 source.buffer.duration로  modular 연산 %를 해주었다.

 

오디오 재생 이벤트가 일어날 때 마다 오디오 소스 노드를 새로 만들어야 한다. 이어질 내용에서는 어느 시점에 오디오 그래프가 구성되는지, 그리고 어느 시점에서 이어서 재생한 위치가 계산되는지 코드 전체적인 관점에서 살펴보도록 하겠다.

 

댓글