javascript, web audio api, html5 관련 질문입니다.

DJuno의 이미지

안녕하세요, 개발입문자입니다. 새로운 음악서비스 관련 웹프로젝트를 과감히 시작하고
관련 분야를 공부하고 있습니다.
기본적으로 음악을 재생하고 가공하는 기능을 html5 와 web audio api 를 활용해 구현하는 것이 1차 목표입니다.
구글링을 통해 샘플 페이지와 튜토리얼을 찾아 해보고 있습니다만 잘 동작하지 않네요.

제가 이해한 과정을 설명해드리겠습니다.

web audio api는 html5 webkit 기반으로 동작하는 오디오 관련 api로
단순한 재생을 넘어 시퀀싱, 오실레이터, 믹서, 이펙터에 이르기까지
사운드를 가공하는 다양한 기능을 제공합니다.
web audio api는 세밀한 동작을 위하여
html5 의 audio 태그처럼 간단한 하나의 태그나 함수로 구성되지 않고
적어도 2개의 모듈을 사용하여 음을 재생하는데,
입력 모듈과 출력 모듈을 사용하여 둘을 연결하는 식으로 구동하게 됩니다.
여기에 볼륨 조절, 이펙트 적용과 같은 중간 모듈이 개입될 수 있습니다.

예를 들어 볼륨 컨트롤이 가능한 소리 재생기를 하나 구성하는 것은
BufferSource(입력) - Gain(볼륨조절) - Destination(출력) 의 3단 구성을 갖게 되는 것입니다.

소스 코드는 기본적으로 다음의 과정을 거쳐 사용됩니다.

1. AudioContext() 객체 선언
2. XMLHttpRequest() 이용해 오디오 소스를 'arraybuffer' 타입으로 받아 저장하기 -> AudioBuffer 생성
3. createBufferSource() 로 입력 모듈 생성, AudioBuffer 집어넣기
4. destination 생성(출력단), BufferSource와 destination 이 연결됨
5. start() 로 재생

다음은 제가 예제로부터 복붙하고 작성한, html 의 헤더에 들어가는 자바스크립트 코드입니다.

<script type="text/javascript">
var context;
var dogBarkingBuffer = null;
window.addEventListener('load', init, false);

function init() {
try {
// Fix up for prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
//context = new AudioContext();
context = new webkitAudioContext();
} catch (e) {
alert('Web Audio API is not supported in this browser');
}
}

function loadDogSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';

// Decode asynchronously
request.onload = function() {
context.decodeAudioData(
request.response,
function(buffer) {
dogBarkingBuffer = buffer;
}, onError
);
}
request.send();
}


// Fix up prefixing
function playSound(buffer) {
var source = context.createBufferSource(); // creates a sound source
source.buffer = buffer; // tell the source which sound to play
source.connect(context.destination); // connect the source to the context's destination (the speakers)
source.start(0); // play the source now
// note: on older systems, may have to use deprecated noteOn(time);
}
init();
loadDogSound('sound/EQa1.wav');
playSound(dogBarkingBuffer);

</script>

저는 이 소스로 마지막에 초기화, 파일 로드로 버퍼 생성, 소리 재생 함수를 차례로 실행시켜
결과적으로 sound/EQa1.wav 파일 재생을 의도했지만
이 소스 코드는 동작하지 않았습니다.

웹 디버깅을 통해 제가 발견한 에러는 2가지인데,
하나는 loadDogSound() 함수 안에서 발견되는 OnError 가 구현되지 않았다는 내용이고
다른 하나는 playSound() 함수에 집어넣어 전달한 dogBarkingBuffer가 AudioBuffer 타입이 아니라는 내용입니다.

전자는 실제로 코드 어디에 OnError 구현 내용이 없으므로 정 필요하면 만들면 되기에 마이너한 문제라 생각합니다. 다만 정확히 어디에 어떻게 구현해야 하는지 모르는 것도 문제이긴 합니다.

그러나 가장 큰 문제는 dogBarkingBuffer가 AudioBuffer로 인식되지 않았다는 점입니다.
해석해보면 맨 처음 변수가 null 로 초기화된 이후 의도한 대로 사운드 버퍼를 받지 못했다는 것임을 알 수 있습니다.
의도한 장소는 loadDogSound() 함수 내 decodeAudioData() 함수 내의 function() 입니다.
여기서 buffer를 받아내도록 되어있습니다만, 받아내서 가지고 오지 못한 것으로 보입니다.
저는 dogBarkingBuffer 를 바깥에 전역으로 var 선언하고 내부에서 사용했습니다.
여기에 어떤 다른 방법을 찾아 적용해봐도 내부에서 생성되고 전달된 AudioBuffer를 밖으로 가져오지 못합니다.
이걸 꺼내와야 다음 playSound() 함수에 넘겨줘서 음을 재생할 수 있을텐데..

저는 자바스크립트를 체계적으로 공부하지 않아서 비어있는 부분이 많습니다.
지금같은 경우도 onload() 함수, OnError 처리 개념, 지역 변수 전역 변수의 참조 권환 관계 등등 모호하고 모르는 부분이 너무 많습니다.
찾아보며 해결하기에는 벽에 부딪혔다는 느낌입니다.
도움 좀 꼭 부탁드립니다.
감사합니다.

simminjo의 이미지

코드로 보면 loadDogsound는 URL을 넘겨받고 ajax request를 통해 해당 파일을 오픈하는데
넘겨주신 경로는 url경로가 아니라 로컬경로입니다. 해당 동작이 당연히 동작할리가 없지요.
GET방식의 Request를 처리해줄 무언가가 있어야 하는데 로컬에 파일에 접근하려고 하니 response가 없을거고 당연히 null 됨으로 인해서
후속 process들은 오류를 뱉는게 아닌가 싶습니다.

---------------------------------------------------------------
Opensource에 기여하는 것이 꿈입니다.
내가 만든 코드를 모두가 사용할 때 까지~

DJuno의 이미지

우연히 이름이 url이었지만 로컬 경로는 의도된 것입니다. 테스트용으로 구현된 코드고 실제 파일이 로컬 경로에 존재합니다.

DJuno

simminjo의 이미지

그 의미가 아니라 xmlhttprequest 자체가 URL로 GET, POST 방식으로 REQUEST하는 내부 메소드인데
그 메소드의 인자로 로컬 파일 위치가 나와버리니 RESPONSE가 안온다는 의미로 쓴 댓글입니다.

파일이 존재하고 안하고는 상관없이 해당 XMLHttpRequest 콜은 Fail 할것으로 보여서요.

좀 더 자세히 쓰자면 http프로토콜로 request를 요청하므로 HTTP웹서버가 해당 요청에 대해 반응하게 됩니다.
해당 RESPONSE가 왔을시에, 내부 콜백 함수가 동작하게 될거구요. 콜백함수가 호출되어야 리소스를 넘겨받아서 사운드가 재생이 될것으로 보입니다.

인자이름이 URL 이어서가 아니라 해당 메소드가 웹으로 요청을 하는 메소드이기 때문에 드린 말씀입니다.
XMLHttpRequest 이 녀석에 대해 찾아보심이 좋을듯 해 보이네요.

결론 : wav파일의 위치가 "로컬"이어서 동작하지 않는 코드로 보입니다.

---------------------------------------------------------------
Opensource에 기여하는 것이 꿈입니다.
내가 만든 코드를 모두가 사용할 때 까지~

DJuno의 이미지

좋은 답변 감사합니다. 말씀 참고하여 프로젝트를 살펴봤는데
저는 이 프로젝트를 이클립스+아파치 톰캣 서버 환경에서 테스트하고 있고,
url 변수에 넘어가는 경로는 상대경로를 쓰고 있기 때문에 실제로 파일을 재생할 때는 http 서버 위 경로에서 음악 파일을 가져옵니다.
때문에 실제 재생에는 문제가 없음을 확인했습니다.

현재 제가 당면한 문제는
내부 함수 안 dogBarkingBuffer = buffer; 라인에서 buffer를 함수 밖으로 가져올 수 없다는 것입니다.
이 buffer에 디코딩된 사운드가 담기게 되기 때문에 가지고 나와서 play 시키든 뭐든 해야 하는데
전역변수로 당연히 접근이 가능할 줄 알았는데 안되더군요.

저의 목적은 이 버퍼를 마음대로 밖으로 가지고 나오는 것입니다.
현재 제공되어 있는 예제 프로그램은 load와 play 를 함 수 하나만 호출해서 몽땅 처리해 버리는 구조로 작성되어 있는데
그래서는 제대로 사용할 수가 없죠..load와 play는 독립된 함수로 동작해야 프로그래머가 원하는대로 사용할 수 있습니다.
이펙트나 볼륨 조절 등등도 load와 play 사이에 이루어져야 하고요.
때문에 load 과정 중 핵심인 저 버퍼를 밖으로 가져오거나 인자로 전달할 필요가 있습니다.

DJuno

simminjo의 이미지

댓글이 길어져서 새로운 댓글로 대신합니다.

해당 buffer는 콜백함수의 인자로 response의 데이터로 실려오는 부분입니다.
말씀하시는 부분과 같이 처리를 하려면 디코드되어 들어오는 데이터를 외부의 리스트에 추가해서 넣어주는 방법으로 처리를 하면 될것처럼 보입니다.
(여기서 외부라는건 전역변수를 의미합니다. 로컬변수 말구요..^^)

말씀하신대로 로컬상대경로를 썼을시 http웹서버가 해당 파일을 response 해준다는 전제로 해석을 해보면
buffer의 값을 dogBarkingBuffer로 넣기전에 play가 되는게 아닌가 싶습니다.

주석으로 써놓으신대로 해당 액션은 async하기때문에 연속동작으로 발생하지 않습니다.
즉 request이후 play가 바로 실행 되고, 그 이후에 response가 왔을 가능성이 있습니다.

해당 데이터가 도착했을시에( on response시에 ) 플래그를 on 시키시고 데이터를 dogBarkingBuffer에 저장합니다. 이후에 play 액션은 타이머 이벤트로 플래그가 on 될때까지 검사한 이후, 플래그가 on 되었을시에 play시키고 종료하게 하시면 문제가 안생기지 않을까 싶습니다.

더 좋은 테크닉들이 있겠으나, 단순히 동작을 간단히 확인 해보시려면 이렇게 하시면 말씀하신 버퍼채워지는 문제는 해결되실것으로 보입니다. ( 타이밍 문제로 보입니다. )

---------------------------------------------------------------
Opensource에 기여하는 것이 꿈입니다.
내가 만든 코드를 모두가 사용할 때 까지~

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.