Web Audio API(Webkit) 기본 사용법
Web Audio API(Webkit) 기본 사용법
지난 7월 7일 NHN 개발자 블로그인 "Hello World"에서 열린 "제2회 Front-End 개발자들을 위한 오픈 세미나" 가 있었습니다.
참석은 하였지만, 개인적인 사정으로 두 번째 섹션까지밖에 들을 수 없어 많이 아쉬웠던 세미나 이기도 합니다.;;ㅠㅠ
아래 작성된 코드는 이날 세미나의 세 번째 섹션인 "Web Audio API"에 대한 소스를 이리저리 찾아보며(구글링을 통해) 작성된 예제 코드입니다.
또한, 코드 작성 시 기술(음향)에 대한 전문적인 지식이 많이 부족하여 소스에 대한 응용이 전무합니다.;;;(API에 대한 한글 문서가 없다는 점과 기반 지식이 부족하여 몇몇 소스는 이해하기도 어려었습니다.;;)
1. 사운드 데이타 요청(Ajax) 시 응답 HTTP Header는 아래와 같습니다.
2. 작성된 소스는 아래와 같으며, 기본적인 API 사용에 관해 관심 있으신 분은 아래 소스를 테스트해보시기 바랍니다.
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
<html xmlns="http://www.w3.org/1999/xhtml">
-
<head id="Head1" runat="server">
-
<script type="text/javascript" src="/sabo/contents/script/jquery.js"></script>
-
<title></title>
-
<script type="text/javascript">
-
//<![CDATA[
-
-
// BufferLoader
-
// Ajax 방식을 통해 사운드 데이터를 바이너리로 저장한다.
-
var BufferLoader = (function () {
-
-
var BufferLoader = function (opt) {
-
-
return new BufferLoader.fn.init(opt);
-
}
-
-
BufferLoader.fn = BufferLoader.prototype = {
-
init: function (opt) {
-
-
this.ctx = new webkitAudioContext();
-
this.urls = [];
-
this.buffers = [];
-
this.callback = function () { ; };
-
this.errcallback = function () { ; };
-
this.loaderCount = 0;
-
-
marge.call(this, opt);
-
-
return this;
-
-
},
-
loader: function () {
-
for (var i = 0, length = this.urls.length; i < length; i++) {
-
load.apply(this, [this.urls[i], this.callback, this.errcallback]);
-
}
-
}
-
}
-
-
function load(url, callback) {
-
-
var xhr = new XMLHttpRequest();
-
xhr.open("get", url, true);
-
// 전송받을 데이터 타입 설정 arraybuffer(바이너리)
-
xhr.responseType = "arraybuffer";
-
-
var that = this;
-
-
xhr.onload = function () {
-
-
that.ctx.decodeAudioData(
-
xhr.response,
-
function (buffer) {
-
-
if (!buffer) return;
-
-
that.buffers.push(buffer);
-
-
if (++that.loaderCount === that.urls.length) {
-
typeof that.callback === 'function' && that.callback.call(that.ctx, that.buffers);
-
}
-
});
-
-
};
-
-
xhr.onerror = function (e) {
-
if (that.loaderCount === that.urls.length) {
-
typeof that.errcallback === 'function' && that.errcallback.call(that.ctx, e);
-
}
-
};
-
-
xhr.send();
-
}
-
-
function marge() {
-
-
var target = this
-
, opts = []
-
, src = null
-
, copy = null;
-
-
for (var i = 0, length = arguments.length; i < length; i++) {
-
-
opts = arguments[i];
-
-
for (var n in opts) {
-
-
src = target[n];
-
copy = opts[n];
-
target[n] = copy;
-
}
-
}
-
-
return target;
-
}
-
-
-
BufferLoader.fn.init.prototype = BufferLoader.prototype;
-
-
return BufferLoader;
-
-
})();
-
-
-
// WebAudio
-
var WebAudio = (function () {
-
-
var that = null;
-
-
var WebAudio = function (opt) {
-
return new WebAudio.fn.init(opt);
-
}
-
-
WebAudio.fn = WebAudio.prototype = {
-
init: function (opt) {
-
-
this.ctx = null;
-
this.buffers = [];
-
-
-
marge.call(this, opt);
-
-
if (that) return that;
-
-
that = this;
-
-
return this;
-
-
},
-
currentSource: null,
-
play: function (type, callback) {
-
-
type = type || '';
-
callback = callback || function () { ; };
-
-
// 현재 플레이 되고 있는 소스 노드를 off 시키고 새로운 소스 노드를 on 시킨다.
-
if (this.currentSource) this.currentSource.noteOff(0);
-
-
context = this.ctx;
-
-
this.currentSource = this.getTypeToSource(context, type);
-
-
var gainNode = context.createGainNode();
-
gainNode.connect(context.destination);
-
-
this.currentSource.connect(gainNode);
-
this.currentSource.noteOn(0);
-
-
callback.call(context);
-
-
return this;
-
-
},
-
currentGainNode: null,
-
crossFadePlay: function (type, volume, callback) {
-
-
type = type || '';
-
volume = volume || 0;
-
callback = callback || function () { ; };
-
-
// 현재 플레이 되고 있는 소스 노드의 사운드 볼륨을 0.5로 설정하여 새롭게 적용되는 소스 노드의 사운드와 겹치게 만든다.
-
if (this.currentGainNode) this.currentGainNode.gain.value = volume;
-
-
context = this.ctx;
-
-
var source = this.getTypeToSource(context, type);
-
-
this.currentGainNode = context.createGainNode();
-
this.currentGainNode.connect(context.destination);
-
-
source.connect(this.currentGainNode);
-
source.noteOn(0);
-
-
callback.call(context);
-
-
return this;
-
-
},
-
getTypeToSource: function (context, type) {
-
-
var source = context.createBufferSource();
-
-
switch (type) {
-
case 'bg1':
-
{
-
source.buffer = this.buffers[0];
-
break;
-
}
-
case 'bg2':
-
{
-
source.buffer = this.buffers[1];
-
break;
-
}
-
case 'bg3':
-
{
-
source.buffer = this.buffers[2];
-
break;
-
}
-
}
-
-
return source;
-
},
-
sourceNormalEach: function (callback) {
-
-
context = this.ctx;
-
buffers = this.buffers || [];
-
callback = callback || function () { ; };
-
-
for (var i = 0, length = buffers.length; i < length; i++) {
-
-
// 사운드 소스 생성
-
var source = context.createBufferSource();
-
source.buffer = buffers[i];
-
-
// 소스를 스피커와 연결
-
source.connect(context.destination);
-
//callback
-
callback.apply(context, [source, i + 1]);
-
}
-
-
return this;
-
},
-
sourceGainNodeEach: function (callback) {
-
-
context = this.ctx;
-
buffers = this.buffers || [];
-
callback = callback || function () { ; };
-
-
for (var i = 0, length = buffers.length; i < length; i++) {
-
-
-
var gainNode = context.createGainNode();
-
// gainNode를 스피커와 연결
-
gainNode.connect(context.destination);
-
-
var source = context.createBufferSource();
-
source.buffer = buffers[i];
-
// 소스를 생성된 gainNode 노드와 연결
-
source.connect(gainNode);
-
-
//callback
-
callback.apply(context, [source, gainNode, i + 1]);
-
}
-
-
return this;
-
},
-
sourceCompressorEach: function (callback) {
-
-
context = this.ctx;
-
buffers = this.buffers || [];
-
callback = callback || function () { ; };
-
-
for (var i = 0, length = buffers.length; i < length; i++) {
-
-
var compressor = context.createDynamicsCompressor();
-
compressor.connect(context.destination);
-
-
var gainNode = context.createGainNode();
-
// gainNode를 Compressor와 연결
-
gainNode.connect(compressor);
-
-
var source = context.createBufferSource();
-
source.buffer = buffers[i];
-
source.connect(gainNode);
-
//callback
-
callback.apply(context, [source, gainNode, compressor, i + 1]);
-
}
-
-
return this;
-
},
-
sourceDelayNodeEach: function (callback) {
-
-
context = this.ctx;
-
buffers = this.buffers || [];
-
callback = callback || function () { ; };
-
-
for (var i = 0, length = buffers.length; i < length; i++) {
-
-
var delayNode = context.createDelayNode();
-
delayNode.connect(context.destination);
-
-
var source = context.createBufferSource();
-
source.buffer = buffers[i];
-
source.connect(delayNode);
-
//callback
-
callback.apply(context, [source, delayNode, i + 1]);
-
}
-
-
return this;
-
},
-
sourceFilterEach: function (callback) {
-
-
context = this.ctx;
-
buffers = this.buffers || [];
-
callback = callback || function () { ; };
-
-
for (var i = 0, length = buffers.length; i < length; i++) {
-
-
// createBiquadFilter를 사용해, 사운드 시그널의 주파수를 필터링해 주파수의 응답을 변경한다.
-
var filter = context.createBiquadFilter();
-
filter.connect(context.destination);
-
-
var source = context.createBufferSource();
-
source.buffer = buffers[i];
-
source.connect(filter);
-
//callback
-
callback.apply(context, [source, filter, i + 1]);
-
}
-
-
return this;
-
},
-
sourcePannerEach: function (callback) {
-
-
context = this.ctx;
-
buffers = this.buffers || [];
-
callback = callback || function () { ; };
-
-
for (var i = 0, length = buffers.length; i < length; i++) {
-
-
// AudioPannerNode를 활용해 위치와 공간을 프로세싱 한다.
-
var panner = context.createPanner();
-
panner.connect(context.destination);
-
-
var source = context.createBufferSource();
-
source.buffer = buffers[i];
-
source.connect(panner);
-
//callback
-
callback.apply(context, [source, panner, i + 1]);
-
}
-
-
return this;
-
},
-
sourceConvolverEach: function (callback) {
-
-
context = this.ctx;
-
buffers = this.buffers || [];
-
callback = callback || function () { ; };
-
-
for (var i = 0, length = buffers.length; i < length; i++) {
-
-
// 동일한 소리를 장소에 따라 다르게 녹음된 데이터를 생성한다.
-
var convolver = context.createConvolver();
-
// 임펄스 데이터
-
convolver.buffer = buffers[i];
-
convolver.connect(context.destination);
-
-
var source = context.createBufferSource();
-
source.buffer = buffers[i];
-
source.connect(convolver);
-
//callback
-
callback.apply(context, [source, convolver, i + 1]);
-
}
-
-
return this;
-
}
-
-
}
-
-
WebAudio.fn.init.prototype = WebAudio.prototype;
-
-
-
function marge() {
-
-
var target = this
-
, opts = []
-
, src = null
-
, copy = null;
-
-
for (var i = 0, length = arguments.length; i < length; i++) {
-
-
opts = arguments[i];
-
-
for (var n in opts) {
-
-
src = target[n];
-
copy = opts[n];
-
target[n] = copy;
-
}
-
}
-
-
return target;
-
}
-
-
return WebAudio;
-
-
})();
-
-
-
function init() {
-
BufferLoader({
-
urls: [
-
'/sabo/bg1.mp3',
-
'/sabo/bg2.mp3',
-
'/sabo/bg3.mp3'
-
],
-
callback: function (buffers) {
-
-
var ctx = this;
-
var Audio = WebAudio({
-
ctx: ctx,
-
buffers: buffers
-
});
-
-
-
$('#BackgroundSoundPlay1').click(function (e) {
-
Audio.play('bg1');
-
return false;
-
});
-
-
$('#BackgroundSoundPlay2').click(function (e) {
-
Audio.play('bg2');
-
return false;
-
});
-
-
$('#BackgroundSoundPlay3').click(function (e) {
-
Audio.play('bg3');
-
return false;
-
});
-
-
$('#BackgroundSoundCrossFadePlay1').click(function (e) {
-
Audio.crossFadePlay('bg1', 0.5);
-
return false;
-
});
-
-
$('#BackgroundSoundCrossFadePlay2').click(function (e) {
-
Audio.crossFadePlay('bg2', 0.5);
-
return false;
-
});
-
-
$('#BackgroundSoundCrossFadePlay3').click(function (e) {
-
Audio.crossFadePlay('bg3', 0.5);
-
return false;
-
});
-
-
$('#sourceNormalEach').click(function (e) {
-
Audio.sourceNormalEach(function (source, i) {
-
-
// 소스 반복 설정
-
//source.loop = true;
-
source.noteOn(0);
-
});
-
-
return false;
-
});
-
-
$('#sourceGainNodeEach').click(function (e) {
-
Audio.sourceGainNodeEach(function (source, gainNode, i) {
-
-
// 재생 볼륨 설정
-
gainNode.gain.value = 0.2 * i;
-
// 소스 반복 설정
-
//source.loop = true;
-
// 재생 시점이 다른 반복 재생
-
source.noteOn(this.currentTime + i);
-
});
-
-
return false;
-
});
-
-
$('#sourceGainNodeScheduleEach').click(function (e) {
-
Audio.sourceGainNodeEach(function (source, gainNode, i) {
-
-
//gainNode.gain.cancelScheduledValues(this.currentTime);
-
-
// 특정 시간까지 값을 리니어하게 변경하도록 스케줄링 한다.
-
// Ramp up and down.
-
gainNode.gain.linearRampToValueAtTime(1.0, (this.currentTime + 0.5));
-
gainNode.gain.linearRampToValueAtTime(0.0, (this.currentTime + 1.5));
-
gainNode.gain.linearRampToValueAtTime(1.0, (this.currentTime + 1.5));
-
gainNode.gain.linearRampToValueAtTime(0.0, (this.currentTime + 2.0));
-
gainNode.gain.linearRampToValueAtTime(1.0, (this.currentTime + 2.5));
-
-
source.noteOn(0);
-
});
-
-
-
-
return false;
-
});
-
-
$('#sourceDelayNodeEach').click(function (e) {
-
Audio.sourceDelayNodeEach(function (source, delayNode, i) {
-
// DelayNode를 사용하면 오디오 시그널의 통과를 지연시킨다.
-
delayNode.delayTime.setValueAtTime(0, this.currentTime);
-
-
source.noteOn(0);
-
});
-
-
return false;
-
});
-
-
-
$('#sourceFilterEach').click(function (e) {
-
Audio.sourceFilterEach(function (source, filter, i) {
-
-
// LowPass
-
filter.type = 0;
-
filter.Q.vlaue = 12.06 + i;
-
filter.frequency.value = 400;
-
-
source.noteOn(0);
-
});
-
-
return false;
-
});
-
-
-
$('#sourcePannerEach').click(function (e) {
-
Audio.sourcePannerEach(function (source, panner, i) {
-
-
panner.coneOuterGain = 0.5;
-
panner.coneOuterAngle = 180;
-
panner.coneinnerAngle = 0;
-
-
panner.setPosition(1, 10, 0);
-
panner.setOrientation(20, 10, 5);
-
panner.setVelocity(0, 1, 0);
-
-
source.noteOn(0);
-
});
-
-
return false;
-
});
-
-
$('#sourceConvolverEach').click(function (e) {
-
Audio.sourceConvolverEach(function (source, convolver, i) {
-
source.noteOn(0);
-
});
-
-
return false;
-
});
-
},
-
errcallback: function (e) {
-
}
-
}).loader();
-
-
}
-
-
window.addEventListener('load', init, false);
-
-
//]]>
-
</script>
-
</head>
-
<body>
-
-
<div>Play</div>
-
<a id="BackgroundSoundPlay1" href="#">BackgroundSoundPlay1</a>
-
<a id="BackgroundSoundPlay2" href="#">BackgroundSoundPlay2</a>
-
<a id="BackgroundSoundPlay3" href="#">BackgroundSoundPlay3</a><br /><br />
-
-
<div>Cross Fade Play</div>
-
<a id="BackgroundSoundCrossFadePlay1" href="#">BackgroundSoundCrossFadePlay1</a>
-
<a id="BackgroundSoundCrossFadePlay2" href="#">BackgroundSoundCrossFadePlay2</a>
-
<a id="BackgroundSoundCrossFadePlay3" href="#">BackgroundSoundCrossFadePlay3</a><br /><br />
-
-
<div>여러 가지 동작</div>
-
<a id="sourceNormalEach" href="#">sourceNormalEach</a><br /><br />
-
<a id="sourceGainNodeEach" href="#">sourceGainNodeEach</a><br /><br />
-
<a id="sourceGainNodeScheduleEach" href="#">sourceGainNodeScheduleEach</a><br /><br />
-
<a id="sourceDelayNodeEach" href="#">sourceDelayNodeEach</a><br /><br />
-
<a id="sourceFilterEach" href="#">sourceFilterEach</a><br /><br />
-
<a id="sourcePannerEach" href="#">sourcePannerEach</a><br /><br />
-
<a id="sourceConvolverEach" href="#">sourceConvolverEach</a><br /><br />
-
</body>
-
</html>
참고 사이트:
제2회 hello world 오픈세미나 Web Audio API-가능성엿보기:
http://www.slideshare.net/deview/2-hello-world-web-aaudioapi
SoftSynth:
http://www.softsynth.com/webaudio/gainramp.php
GETTING STARTED WITH WEB AUDIO API:
http://www.html5rocks.com/en/tutorials/webaudio/intro/