Web Audio APIì ê°ì¥ í¥ë¯¸ë¡ì´ ê¸°ë¥ ì¤ íëë 주íì, íí, ê·¸ë¦¬ê³ ë¤ë¥¸ ë°ì´í°ë¤ì ì¤ëì¤ ìì¤ë¡ë¶í° ì¶ì¶í ì ìë ë¥ë ¥ì¸ë°, ì´ë ê·¸ë¦¬ê³ ëì ìê°ííë ë° ì¬ì©ë ì ììµëë¤. ì´ ê¸ì ì´ë»ê² ìê°í를 íëì§ ì¤ëª íê³ , 기ì´ì ì¸ ì¬ì© ë°©ë²ì ë ê° ì ê³µí©ëë¤.
ì°¸ê³ : 모ë ì½ëì ìë ìì 를 Voice-change-O-matic ë°ëª¨ìì ì°¾ì ì ììµëë¤.
기본 ê°ë ì¤ëì¤ ìì¤ìì ë°ì´í°ë¥¼ ì¶ì¶íë ¤ë©´, ì¬ë¬ë¶ì AnalyserNode
ê° íìíë°, ì´ë BaseAudioContext.createAnalyser
를 ì¬ì©íì¬ ìì±ë©ëë¤. ì를 ë¤ìë©´:
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();
ê·¸ë¦¬ê³ ëì ì´ ë ¸ëë ìì¤ì 목ì ì§ ì¬ì´ì ì´ë¤ ì§ì ìì ì¬ë¬ë¶ì ì¤ëì¤ ìì¤ì ì°ê²°ëëë°, ì를 ë¤ìë©´:
source = audioCtx.createMediaStreamSource(stream);
source.connect(analyser);
analyser.connect(distortion);
distortion.connect(audioCtx.destination);
ì°¸ê³ : ì ë ¥ì´ ìì¤ì ì§ì ì ì¼ë¡ë í¹ì ë¤ë¥¸ ë ¸ë를 ê²½ì íë ì°ê²°ëì´ ìë í, ì¬ë¬ë¶ì analyserì ì¶ë ¥ì ë¤ë¥¸ ë ¸ëì ì´ê²ì´ ìëíëë¡ ì°ê²°í íìê° ììµëë¤.
analyser ë
¸ëë ê·¸ë¦¬ê³ ëì, ì¬ë¬ë¶ì´ AnalyserNode.fftSize
ìì± ê°ì ëª
ìí ê²ì ë°ë¼, ê³ ì í¸ë¦¬ì ë³í (fft) ì ì¬ì©íì¬ í¹ì í 주íì ëë©ì¸ì ìë ì¤ëì¤ ë°ì´í°ë¥¼ 캡ì³í©ëë¤. (ë§ì½ ê°ì´ ëª
ìëì´ ìì§ ìì¼ë©´, 기본ê°ì 2048ì
ëë¤.)
ì°¸ê³ : ì¬ë¬ë¶ì ëí AnalyserNode.minDecibels
ê³¼ AnalyserNode.maxDecibels
ì ì¬ì©íì¬ fft ë°ì´í° ì¤ì¼ì¼ë§ ë²ìì ëí´ ìµìì ìµë power ê°ì ëª
ìí ì ìê³ , AnalyserNode.smoothingTimeConstant
를 ì¬ì©íì¬ ê°ê° ë¤ë¥¸ ë°ì´í° averaging ìì를 ëª
ìí ì ììµëë¤. ì´ê²ë¤ì ì´ë»ê² ì¬ì©íëì§ì ëí´ìë ê° íì´ì§ë¥¼ ì½ì´ ë³´ì¸ì.
ë°ì´í°ë¥¼ 캡ì³í기 ìí´ìë, ì¬ë¬ë¶ì 주íì ë°ì´í°ë¥¼ 캡ì³í기 ìí´ AnalyserNode.getFloatFrequencyData()
ì AnalyserNode.getByteFrequencyData()
ë©ìë를 ì¬ì©í íìê° ìê³ , íí ë°ì´í°ë¥¼ 캡ì³í기 ìí´ìë AnalyserNode.getByteTimeDomainData()
ì AnalyserNode.getFloatTimeDomainData()
를 ì¬ì©í íìê° ììµëë¤.
ì´ ë©ìëë¤ì ë°ì´í°ë¥¼ ëª
ìë ë°°ì´ì ë³µì¬íë¯ë¡, ì¬ë¬ë¶ì ë©ìë를 í¸ì¶í기 ì ì ë°ì´í°ë¥¼ ë°ê¸° ìí ìë¡ì´ ë°°ì´ì ë§ë¤ íìê° ììµëë¤. 첫ë²ì§¸ ë©ìëë 32ë¹í¸ ë¶ë ì«ì를 ë§ë¤ê³ , ëë²ì§¸ì ì¸ë²ì§¸ë 8ë¹í¸ unsigned ì ì를 ë§ë¤ê¸° ë문ì, íì¤ JavaScript ë°°ì´ì ì´ë¥¼ ìíí ì ììµëë¤ â ì¬ë¬ë¶ì´ ì´ë¤ ë°ì´í°ë¥¼ ë¤ë£¨ëëì ë°ë¼, Float32Array
ë Uint8Array
ë°°ì´ì ì¬ì©í íìê° ììµëë¤.
ê·¸ëì ì를 ë¤ìë©´, ì°ë¦¬ê° 2048ì fft ì¬ì´ì¦ë¥¼ ë¤ë£¨ê³ ìë¤ê³ í´ ë´
ìë¤. ì°ë¦¬ë fftì ì ë°ì¸ AnalyserNode.frequencyBinCount
ê°ì ë°ííê³ , ê·¸ë¦¬ê³ ëì Uint8Arrayì ê¸¸ì´ ì¸ìë¡ì frequencyBinCountì í¨ê» Uint8Array()ì í¸ì¶í©ëë¤ â ì´ê²ì ì¼ë§ë ë§ì ë°ì´í° í¬ì¸í¸ë¥¼ ì°ë¦¬ê° ê·¸ fft ì¬ì´ì¦ì ëí´ ìì§í ê²ì¸ì§ë¥¼ ëíë
ëë¤.
analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
ì¤ì ë¡ ë°ì´í°ë¥¼ ì»ê³ ë°°ì´ì ë³µì¬í기 ìí´ì, ì°ë¦¬ë ê·¸ë¦¬ê³ ëì ì°ë¦¬ê° ìíë ë°ì´í° ìì§ ë©ìë를 ê·¸ê²ì ì¸ìë¡ì ì ë¬ë ë°°ì´ê³¼ í¨ê» í¸ì¶í©ëë¤. ì를 ë¤ìë©´:
analyser.getByteTimeDomainData(dataArray);
ì°ë¦¬ë ì´ì ìê°ì¼ë¡ ê·¸ ìê°ì ëí´ ë°°ì´ ìì 캡ì³ë ì¤ëì¤ ë°ì´í°ë¥¼ ê°ì§ê³ ìê³ , ì°ë¦¬ê° ìíë ëë¡ ìê°í를 ì§íí ì ìëë°, ì를 ë¤ìë©´ ì´ê²ì HTML5 <canvas>
ì ê·¸ë ¤ë³¼ ì ììµëë¤.
ëªëª 구체ì ì¸ ìì 를 ì´í´ë´ ìë¤.
íí/ì¤ì¤ë¡ì¤ì½í ìì±í기ì¤ì¤ë¡ì¤ì½í ìê°í를 ë§ë¤ê¸° ìí´ìë (Voice-change-O-matic ë´ì ì본 ì½ëì ëí´ Soledad Penadésê» ì¸ì¬ë¥¼ ë³´ë ëë¤), ì°ë¦¬ë ì°ì ë²í¼ë¥¼ ì¤ì í기 ìí´ ì´ì ì¹ì ìì ì¤ëª ë íì¤ í¨í´ì ë°ë¼ì¼ í©ëë¤.
analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
ë¤ìì¼ë¡, ì°ë¦¬ë ìë¡ì´ ìê°í ëì¤íë ì´ë¥¼ ì¤ë¹í기 ìí´ ìºë²ì¤ ìì ì´ì ì ê·¸ë ¤ì§ ê²ì ì§ìëë¤.
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
ì°ë¦¬ë ì´ì draw()
í¨ì를 ì ìí©ëë¤:
ì¬ê¸°ì, ì°ë¦¬ë ëë¡ì í¨ìê° í ë² ììëë©´ ê³ì ë°ë³µëëë¡ requestAnimationFrame()
ì ì¬ì©í©ëë¤.
var drawVisual = requestAnimationFrame(draw);
ë¤ìì¼ë¡, ì°ë¦¬ë ìê° ëë©ì¸ ë°ì´í°ë¥¼ ë°°ì´ì ë³µì¬í©ëë¤.
analyser.getByteTimeDomainData(dataArray);
ë¤ìì¼ë¡, ììí기 ìí´ ìºë²ì¤ë¥¼ ë¨ìì¼ë¡ ì±ìëë¤.
canvasCtx.fillStyle = "rgb(200, 200, 200)";
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
ì°ë¦¬ê° 그릴 íëì ëí´ ì ì 굵기ì ììì ì¤ì íê³ , ê²½ë¡ë¥¼ 그리기 ììí©ëë¤.
canvasCtx.lineWidth = 2;
canvasCtx.strokeStyle = "rgb(0, 0, 0)";
canvasCtx.beginPath();
ìºë²ì¤ ëë¹ë¥¼ (ìì ì ìë ë°ì ê°ì´ FrequencyBinCountì ëì¼í) ë°°ì´ ê¸¸ì´ë¡ ëëì¼ë¡ì¨ ê·¸ë ¤ì§ ì ì ê° ë¶ë¶ì ëë¹ë¥¼ ê²°ì íê³ , ì ì ê° ë¶ë¶ì 그리기 ìí´ ì´ëí ìì¹ë¥¼ ì ìí기 ìí´ x ë³ì를 ì ìí©ëë¤.
var sliceWidth = (WIDTH * 1.0) / bufferLength;
var x = 0;
ì´ì ë°°ì´ë¡ë¶í°ì ë°ì´í° í¬ì¸í¸ ê°ì 기ë°í í¹ì í ëì´ììì ë²í¼ì ê° ë¶ë¶ì ëí´ íëì ìì ë¶ë¶ì ìì¹ë¥¼ ì ìíê³ , ë¤ì íë ë¶ë¶ì´ ê·¸ë ¤ì§ ì§ì ê¹ì§ ì ì ì´ëíë ë°ë³µë¬¸ì ì¤íí©ëë¤.
for (var i = 0; i < bufferLength; i++) {
var v = dataArray[i] / 128.0;
var y = (v * HEIGHT) / 2;
if (i === 0) {
canvasCtx.moveTo(x, y);
} else {
canvasCtx.lineTo(x, y);
}
x += sliceWidth;
}
ë§ì§ë§ì¼ë¡, ì°ë¦¬ë ìºë²ì¤ì ì°ì¸¡ ì¤ììì ì ì ëë´ê³ , ì°ë¦¬ê° ì ìí íì 그립ëë¤:
canvasCtx.lineTo(canvas.width, canvas.height/2);
canvasCtx.stroke();
};
ì½ëì ì´ ì¹ì
ì ë§ì§ë§ìì, ì°ë¦¬ë ì ì²´ ê³¼ì ì ììí기 ìí´ draw()
í¨ì를 í¸ì¶í©ëë¤:
ì´ê²ì ì´ë¹ ì ì°¨ë¡ ê°±ì ëë ë©ì§ íí ëì¤íë ì´ë¥¼ ë³´ì¬ì¤ëë¤:
주íì ë§ë ê·¸ëí ìì±í기ì ìí ë ë¤ë¥¸ ë©ì§ ìì ì¬ì´ë ìê°íë Winamp ì¤íì¼ì 주íì ë§ë ê·¸ëí ì¤ íëì ëë¤. ì°ë¦¬ë Voice-change-O-maticìì ì´ì© ê°ë¥í íë를 ê°ì§ê³ ììµëë¤; ì´ë»ê² ì´ê²ì´ ìì±ëìëì§ë¥¼ ì´í´ë´ ìë¤.
ì°ì , ì°ë¦¬ë analyserì ë°ì´í° ë°°ì´ì ë¤ì ì¤ë¹íê³ , clearRect()
ë¡ íì¬ ìºë²ì¤ ëì¤íë ì´ë¥¼ ì§ìëë¤. ì´ì ê³¼ ë¤ë¥¸ ì ì¼í ì ì fft ì¬ì´ì¦ë¥¼ ëì± ìê² ì¤ì íë¤ë ê²ì
ëë¤. ì´ê²ì ê·¸ëíìì ê°ê°ì ë°ê° ìì ê°ë¥ë³´ë¤ë ì¤ì ë¡ ë§ë기ì²ë¼ ë³´ì´ê¸°ì ì¶©ë¶í í¬ëë¡ í기 ìí¨ì
ëë¤.
analyser.fftSize = 256;
var bufferLength = analyser.frequencyBinCount;
console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
ë¤ìì¼ë¡, ì°ë¦¬ë íìë ë°ì´í°ê° ê³ì ì
ë°ì´í¸ëëë¡ requestAnimationFrame()
ì¼ë¡ ë°ë³µì ë¤ì ì¤ì íê³ , ê° ì ëë©ì´ì
íë ìì ëì¤íë ì´ë¥¼ ì§ì°ë©°, draw()
í¨ì를 ìììíµëë¤.
function draw() {
drawVisual = requestAnimationFrame(draw);
analyser.getByteFrequencyData(dataArray);
canvasCtx.fillStyle = 'rgb(0, 0, 0)';
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
ì´ì ì°ë¦¬ë barWidth
를 ë§ë기ì ì (ë²í¼ 길ì´)ë¡ ëë ì§ ìºë²ì¤ì íê³¼ ëì¼íê² ì¤ì í©ëë¤. ê·¸ë¬ë, ì°ë¦¬ë ëí ê·¸ íì 2.5를 ê³±íëë°, ìëíë©´ ì°ë¦¬ê° ë§¤ì¼ ë£ë ì리ë¤ì ëë¶ë¶ì í¹ì í ë®ì 주íì ë²ìì ìì¼ë¯ë¡ ëë¶ë¶ì 주íìë¤ì ì´ë¤ ì¤ëì¤ë ê°ì§ê³ ìì§ ìì ì±ë¡ ëëìì¤ê¸° ë문ì
ëë¤. ì°ë¦¬ë ìë§ì ë¹ì´ìë ë§ë기ë¤ì íìí기 ìíì§ ìì¼ë¯ë¡, ì°ë¦¬ë ëë ·í ëì´ì ì주 íìë ë§ë기ë¤ì ì®ê²¨ ê·¸ê²ë¤ì´ ìºë²ì¤ ëì¤íë ì´ë¥¼ ì±ì°ëë¡ í©ëë¤.
ì°ë¦¬ë ëí barHeight
ë³ìì, íì¬ ë§ë기를 그리기 ìí´ ì¤í¬ë¦°ì´ ì¼ë§ë ë©ë¦¬ ê°ë¡ì§ë¬ ë¨ì´ì ¸ ìëì§ë¥¼ 기ë¡í기 ìí´ x
ë³ì를 ì¤ì í©ëë¤.
var barWidth = (WIDTH / bufferLength) * 2.5;
var barHeight;
var x = 0;
ì´ì ê³¼ ê°ì´, ì°ë¦¬ë ì´ì for ë°ë³µë¬¸ì ììí´ dataArray
ì ê° ê°ì ìíí©ëë¤. ê° ê°ì ëí´, ì°ë¦¬ë barHeight
를 ë°°ì´ ê°ê³¼ ê°ê² ë§ë¤ê³ , ì±ì¸ ìê¹ì barHeight
ì 기ë°í´ ì¤ì íê³ (ëììë¡ ë°ìµëë¤), ë§ë기를 ìºë²ì¤ë¥¼ x
í½ì
ê°ë¡ì§ë¥¸ ê³³ì 그리ëë°, ì´ë íì´ barWidth
ì´ê³ ëì´ë barHeight/2
ì
ëë¤. (ì°ë¦¬ë ê²°êµ ë§ë기ë¤ì´ ìºë²ì¤ì ë ì ë§ëë¡ ê° ë§ë기ë¤ì ë°ì ë¡ ìë¥´ê¸°ë¡ ê²°ì íìµëë¤)
ì¤ëª
ì íìë¡ íë íëì ê°ì ì°ë¦¬ê° ê° ë§ë기를 그리ë ìì§ ì¤íì
ìì¹ì¸ HEIGHT-barHeight/2
ì
ëë¤. ì ê° ì´ê²ì í ì´ì ë, ë§ì½ ì°ë¦¬ê° ìì§ ìì¹ë¥¼ 0ì¼ë¡ ì¤ì íë¤ë©´ ê° ë§ëê¸°ê° ìºë²ì¤ì ìììë¶í° ìëë¡ íì´ëìì í
ë°, ì ê° ìí ê²ì ì´ê² ìëë¼ ë§ë기ë¤ì´ ìëììë¶í° ìë¡ íì´ëì¤ê¸°ë¥¼ ìí기 ë문ì
ëë¤. ê·¸ë¬ë¯ë¡, ì°ë¦¬ë ëì ë§¤ë² ìì§ ìì¹ë¥¼ ìºë²ì¤ì ëì´ ë§ì´ëì¤ barHeight/2
를 íì¬, ê° ë§ëê¸°ê° ìºë²ì¤ì ìë ëì¤ììë¶í°, ìëìì ìºë²ì¤ì ë°ì í¥í´ ê·¸ë ¤ì§ ê²ì
ëë¤.
for(var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i]/2;
canvasCtx.fillStyle = 'rgb(' + (barHeight+100) + ',50,50)';
canvasCtx.fillRect(x,HEIGHT-barHeight/2,barWidth,barHeight);
x += barWidth + 1;
}
};
ë¤ì, ì½ëì ë§ì§ë§ìì ì°ë¦¬ë ì ì²´ ê³¼ì ì ìì§ì´ê² ë§ë¤ê¸° ìí´ draw() í¨ì를 í¸ì¶í©ëë¤.
ì´ ì½ëë ë¤ìê³¼ ê°ì 결과를 ë³´ì¬ì¤ëë¤:
ì°¸ê³ : ì´ ê¸ìì ì¸ê¸ë ìì ë¤ì AnalyserNode.getByteFrequencyData()
ì AnalyserNode.getByteTimeDomainData()
ì ì¬ì©ì ë³´ì¬ì£¼ììµëë¤. AnalyserNode.getFloatFrequencyData()
ì AnalyserNode.getFloatTimeDomainData()
를 ë³´ì¬ì£¼ë ìë ìì ì ëí´ìë, Voice-change-O-matic-float-data ë°ëª¨ (ìì¤ ì½ë ë íì¸í ì ììµëë¤) 를 ì°¸ê³ íì¸ì â ì´ê²ì unsigned ë°ì´í¸ ë°ì´í°ê° ìëë¼, Float ë°ì´í°ë¥¼ ì¬ì©íë¤ë ê²ì ì ì¸íë©´, ì´ê²ì ì íí ìë Voice-change-O-maticì ëê°ìµëë¤.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4