让æä»¬æ¥çç Web Audio API å ¥é¨ãæä»¬å°ç®è¦ä»ç»ä¸äºæ¦å¿µï¼ç¶åå¦ä¹ ä¸ä¸ªç®åçå 许æä»¬å è½½é³è½¨ï¼ææ¾æåï¼æ¹åé³éåç«ä½å£°å£°åçé³ç®±ä¾åã
Web Audio API å¹¶ä¸ä¼å代<audio>é³é¢å ç´ ï¼åä¸å¦è¯´å®æ¯<audio>çè¡¥å æ´å¥½ï¼å°±å¥½æ¯å¦<canvas>ä¸<img>å ±åçå ³ç³»ãä½ ä½¿ç¨æ¥å®ç°é³é¢çæ¹å¼åå³äºä½ çä½¿ç¨æ åµãå¦æä½ åªæ¯æ³æ§å¶ä¸ä¸ªç®åçé³è½¨çææ¾ï¼<audio>æè®¸æ¯ä¸ä¸ªæ´å¥½æ´å¿«çéæ©ãå¦æä½ æ³å®ç°æ´å¤å¤æçé³é¢å¤çï¼ä»¥åææ¾ï¼Web Audio API æä¾äºæ´å¤çä¼å¿ä»¥åæ§å¶ã
Web Audio API çä¸ä¸ªå¼ºå¤§ä¹å¤å¨äºï¼å®æ²¡æä»»ä½ä¸¥æ ¼ç声é³å¼å«æ§å¶ãæ¯å¦è¯´ï¼å¨å䏿¶é´å®æ²¡æå¼å« 32 æ 64 ç声é³çéå¶ãå¦æä½ çå¤ç卿§è½å¥½çè¯ï¼å䏿¶é´ææ¾ 1000 å¤ç声é³ä¸å¡é¡¿ä¹æ¯æå¯è½çãè¿å åæ¾ç¤ºçæ£çè¿æ¥ï¼è¦ç¥éå å¹´åä¸é«é¢ç声å¡ä» è½å¤çå°é¨åçè´è½½ã
ä¾åæä»¬çé³ç®±çèµ·æ¥åè¿æ ·ï¼
注æå¸¦æææ¾æé®çå¤å¤ç£å¸¦å¡åº§ï¼åç¨äºæ¹åé³éåç«ä½å£°å£°åç平移æ»åãæä»¬å¯ä»¥ä½¿å ¶æ´å¤æï¼ä½è¿æ¯è¯¥é¶æ®µè¿è¡ç®åå¦ä¹ ççæ³éæ©ã
æ¥çæç» demo 代ç here on Codepenï¼æè å¨ GitHub æ¥çæºä»£ç on GitHubã
æµè§å¨æ¯æç°ä»£æµè§å¨ç Web Audio API 对ç大夿°åè½é½æå¾å¥½çæ¯æãAPI æå¾å¤çåè½ï¼å æ¤è¦è·å¾æ´åç¡®çä¿¡æ¯ï¼ä½ å¿ é¡»æ£æ¥æ¯ä¸ªåè页é¢åºé¨çæµè§å¨å ¼å®¹è¡¨ã
é³é¢å¾Web Audio API ä¸çææå 容齿¯åºäºé³é¢å¾çæ¦å¿µï¼é³é¢å¾ç±èç¹ç»æã
Web Audio API å¨ audio contextï¼é³é¢ä¸ä¸æï¼ å å¤çé³é¢ï¼èä¸è¢«è®¾è®¡ä¸ºå 许模ååè·¯ç±ãåºæ¬çé³é¢æä½æ¯åºäº audio nodes è¿è¡çï¼é³é¢èç¹è¿æ¥èµ·æ¥å½¢æä¸ä¸ªé³é¢è·¯ç±å¾ãä½ æ¥æè¾å ¥èç¹ï¼ä½ è¦æä½ç声鳿ºï¼æ ¹æ®è®¾è®¡éè¦è¢«ä¿®æ¹çèç¹ï¼åè¾åºèç¹ï¼ç®çå°ï¼ï¼å®ä»¬å è®¸ä½ ä¿åæè å¬åè¿äºå£°é³ã
æ¯ææ¥æä¸åééå¸å±çå¤ä¸ªçé³é¢æºï¼å³ä½¿æ¯å¨å个ä¸ä¸æãå 为模åå设计ï¼ä½ å¯ä»¥åå»ºå ·æå¨æææç夿çé³é¢åè½ã
é³é¢ä¸ä¸æä¸ºäºè½éè¿ Web Audio API æ§è¡ä»»ä½æä½ï¼æä»¬éè¦å建é³é¢ä¸ä¸æå®ä¾ãè¿è½è®©æä»¬è®¿é® API ææçç¹æ§ååè½ã
// for legacy browsers
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioContext = new AudioContext();
æä»¥å½æä»¬è¿æ ·åæ¶ä¼åçä»ä¹ï¼ä¸ºæä»¬èªå¨å建ä¸ä¸ª BaseAudioContext
å¹¶èªå¨æ©å±å°å¨çº¿é³é¢ä¸ä¸æãæä»¬å¸æå¦æ¤ï¼å 为æä»¬æ³è¦ææ¾å¨çº¿å£°é³ã
夿³¨ï¼ å¦æä½ åªæ¯æ³å¤çé³é¢æ°æ®ï¼ä¸¾ä¸ªä¾åï¼ç¼ååæµå¼ä¼ è¾è䏿æ¾å®ï¼ä½ å¯è½æ³è¦èèå建ä¸ä¸ª OfflineAudioContext
ã
ç°å¨ï¼éè¦éè¿æä»¬å建çé³é¢ä¸ä¸æææ¾ä¸äºå£°é³ãWeb Audio API 䏿å ç§æ¹æ³å¯ä»¥å®ç°è¿ä¸ç¹ã让æä»¬éè¿ä¸ä¸ªç®åçæ¹æ³å¼å§ â å 为æä»¬æä¸ä¸ªé³ç®±ï¼æä»¬å¯è½æ³ææ¾ä¸é¦å®æ´çææ²ãæ¤å¤ï¼ä¸ºäºä¾¿äºè®¿é®ï¼æä»¬å¯ä»¥å¨å¨ DOM 䏿´é²è¯¥é³è½¨ãæä»¬å°ä½¿ç¨ <audio>
å
ç´ å¨é¡µé¢ä¸æ´é²è¿é¦ææ²ã
<audio src="myCoolTrack.mp3" type="audio/mpeg"></audio>
夿³¨ï¼ å¦æä½ è¦å è½½ç声鳿件ä¿çå¨å
¶ä»åä¸ï¼åéè¦ä½¿ç¨ crossorigin
屿§ï¼æ¥ç Cross Origin Resource Sharing (CORS) å徿´å¤ä¿¡æ¯ã
为äºä½¿ç¨ Web Audio API çä¼ç§ç¹æ§ï¼æä»¬éè¦ä»è¯¥å
ç´ ä¸è·åæºå¹¶å°å
¶ä¼ å
¥æä»¬å建çä¸ä¸æä¸ã幸è¿çæ¯ï¼æä¸ä¸ªæ¹æ³å¯ä»¥è®©æä»¬åå°è¿ä¸ç¹ â AudioContext.createMediaElementSource
:
// get the audio element
const audioElement = document.querySelector("audio");
// pass it into the audio context
const track = audioContext.createMediaElementSource(audioElement);
夿³¨ï¼ ä¸é¢ç <audio>
å
ç´ å¨ DOM ä¸ä»£è¡¨äºä¸ä¸ªHTMLMediaElement
ç±»åçå¯¹è±¡ï¼æ¥æå
¶èªèº«çä¸ç»åè½ãè¿ä¸åé½å°ä¿æä¸åãæä»¬åªæ¯è®© Web Audio API è½å¤è®¿é®å°å£°é³ã
å½å¨ç½é¡µä¸ææ¾å£°é³æ¶ï¼è®©ç¨æ·è½æ§å¶å®æ¯å¾éè¦çãæ ¹æ®ä½¿ç¨åºæ¯ï¼ææ æ°çé项å¯ç¨ï¼ä½è¿æä»¬å°æä¾ææ¾/æå声é³ï¼æ¹åé³è½¨é³éåä»å·¦å°å³å¹³ç§»å£°é³çåè½ã
éè¿ JavaScript ä»£ç æ§å¶å£°é³ä¼åå°æµè§å¨çèªå¨ææ¾çç¥çå½±å (autoplay support policies)ï¼å æ¤å¨æªç»ç¨æ·ï¼æç½ååï¼è®¸å¯çæ åµä¸èæ¬å¯¹å£°é³çæ§å¶ä¼è¢«é»æ¢ãæµè§å¨çèªå¨ææ¾çç¥éå¸¸è¦æ±æ¾å¼æéæè ç¨æ·ä¸é¡µé¢äº§çäºå¨åï¼æå è®¸èæ¬è§¦åé³é¢ææ¾ã
è¿äºç¹æ®çè¦æ±åºæ¬ä¸æ¯å 为æå¤ç声é³å¯è½ä¼ææ°å°ç¨æ·ï¼ä»¤äººåç¦ï¼å¹¶ä¸å¯è½å¯¼è´æ éç¢é®é¢ãä½ å¯ä»¥å¨æç« åªä½ä¸ Web é³é¢ API èªå¨ææ¾æå äºè§£æ´å¤ç¸å ³ä¿¡æ¯ã
å 为æä»¬çèæ¬æ£ååºç¨æ·è¾å ¥ï¼ä¾å¦ï¼ç¹å»ææ¾æé®ï¼è¿è¡ææ¾é³é¢ï¼æä»¬ç¶æè¯å¥½ä¸åºè¯¥æ²¡æèªå¨ææ¾é»æ¢çé®é¢ãæä»¥ï¼è®©æä»¬ççæä»¬çææ¾åæååè½ãæä»¬æä¸ä¸ªå½é³é¢ææ¾æ¶å为æåæé®çææ¾æé®ï¼
<button data-playing="false" role="switch" aria-checked="false">
<span>Play/Pause</span>
</button>
卿们å¯ä»¥ææ¾é³é¢åæä»¬éè¦å°æä»¬çé³é¢å¾ä»é³é¢æº/è¾å ¥èç¹è¿æ¥å°ç®çå°ã
æä»¬å·²ç»éè¿æé³é¢å
ç´ ä¼ å
¥ API çæä¸ä¸ªè¾å
¥èç¹ãå¨å¤§å¤æ°æ
åµä¸ï¼ä½ ä¸éè¦çæä¸ä¸ªè¾åºèç¹ï¼ä½ åªéè¦å°å
¶ä»èç¹è¿æ¥å°å¯ä»¥ä¸ºä½ å¤çè¿ç§æ
åµç BaseAudioContext.destination
ï¼
track.connect(audioContext.destination);
å¯è§åè¿äºèç¹çä¸ä¸ªå¥½æ¹æ³æ¯ç»å¶é³é¢å¾å½¢ä»¥ä¾¿å¯è§åå®ãè¿æ¯æä»¬å½åçé³é¢å¾ï¼
ç°å¨æä»¬å¯ä»¥æ·»å ææ¾åæååè½ã
// select our play button
const playButton = document.querySelector("button");
playButton.addEventListener(
"click",
function () {
// check if context is in suspended state (autoplay policy)
if (audioContext.state === "suspended") {
audioContext.resume();
}
// play or pause track depending on state
if (this.dataset.playing === "false") {
audioElement.play();
this.dataset.playing = "true";
} else if (this.dataset.playing === "true") {
audioElement.pause();
this.dataset.playing = "false";
}
},
false,
);
æä»¬ä¹éè¦èèå°å½é³é¢ææ¾å®æ¯ååä»ä¹ãæä»¬ç HTMLMediaElement
䏿¦ææ¾å®æ¯ä¼è§¦åä¸ä¸ª ended
äºä»¶ï¼æä»¥æä»¬å¯ä»¥çå¬å®å¹¶è¿è¡ç¸åºä»£ç ï¼
audioElement.addEventListener(
"ended",
() => {
playButton.dataset.playing = "false";
},
false,
);
å
³äº Web Audio ç¼è¾å¨
Firefox æä¸ä¸ªå为 Web Audio editor çå·¥å ·ãå¨å ¶ä¸è¿è¡é³é¢å¾çä»»ä½é¡µé¢ä¸ï¼ä½ å¯ä»¥æå¼å¼åè å·¥å ·ï¼ä½¿ç¨ Web Audio é项塿¥çé³é¢å¾ï¼å¯æ¥çæ¯ä¸ªèç¹çå¯ç¨å±æ§ï¼å¹¶å¯ä»¥ä¿®æ¹è¿äºå±æ§æ¥æ¥ç伿ä»ä¹ææã
夿³¨ï¼ Web Audio ç¼è¾å¨é»è®¤ä¸æ¯å¼å¯çï¼ä½ éè¦æå¼ Firefox developer tools 设置ï¼éä¸ Default Developer Tools é¨åä¸ç Web Audio å¤éæ¡æ¥æ¾ç¤ºå®ã
ä¿®æ¹å£°é³è®©æä»¬æ·±å
¥ç ç©¶ä¸äºåºæ¬çä¿®æ¹èç¹ä»¥æ¹åæä»¬ç声é³ãè¿å°±æ¯ Web Audio API çæ£å¼å§æ´¾ä¸ç¨åºçå°æ¹ãé¦å
ï¼è®©æä»¬æ¹åé³éãè¿å¯ä»¥éè¿ GainNode
å®ç°ï¼å®è¡¨ç¤ºæä»¬ç声波æå¤å¤§ã
ä½¿ç¨ Web Audio API å¯ä»¥éè¿ 2 ä¸ªæ¹æ³å建èç¹ãä½ å¯ä»¥ä½¿ç¨ä¸ä¸ææ¬èº«ç工忹æ³ï¼ä¾å¦ï¼ audioContext.createGain()
ï¼æè
éè¿èç¹çæé 彿°ï¼ä¾å¦ï¼ new GainNode()
ï¼ï¼æä»¬å°ä½¿ç¨å·¥åæ¹æ³ï¼
const gainNode = audioContext.createGain();
ç°å¨æä»¬éè¦å¨åå é³é¢å¾åºç¡ä¸æ´æ°é³é¢å¾ï¼æä»¥è¾å ¥è¿æ¥å°å¢çï¼ç¶åå¢çèç¹è¿æ¥å°ç®æ ï¼
track.connect(gainNode).connect(audioContext.destination);
è¿ä¼è®©æä»¬çé³é¢å¾çèµ·æ¥å¦ä¸ï¼
é»è®¤å¢ç为 1ï¼è¿ä½¿å½åé³éä¿æä¸åãå¢çå¯ä»¥è®¾ç½®çæå°å¼çº¦-3.4028235E38
ï¼æå¤§çº¦3.4028235E38
ãè¿éæä»¬å°å
许é³ç®±å¢çå¯ä»¥è®¾ç½®å° 2ï¼2 åçåé³éï¼åéä½å° 0ï¼è¿å¯ä»¥ææçéé³ï¼ã
让æä»¬ç»ç¨æ·è¿æ ·çæ§å¶ â æä»¬å°ä¼ä½¿ç¨ range input ï¼
<input type="range" id="volume" min="0" max="2" value="1" step="0.01">
夿³¨ï¼ èå´è¾å ¥ (Range Input) æ¯æ´æ°é³é¢èç¹å¼é常æ¹ä¾¿çè¾å ¥ç±»åãä½ å¯ä»¥æå®ç¹å®çèå´å¼åæ¶ç´æ¥å°å®ä»¬ä½ä¸ºé³é¢åæ°ä¸èµ·ä½¿ç¨ã
æä»¥å½ç¨æ·æ´æ¹è¾å ¥èç¹å¼æ¶ï¼è·åæ¤è¾å ¥å¼å¹¶æ´æ°å¢çå¼ï¼
const volumeControl = document.querySelector("#volume");
volumeControl.addEventListener(
"input",
function () {
gainNode.gain.value = this.value;
},
false,
);
夿³¨ï¼ èç¹å¯¹è±¡çå¼ï¼ä¾å¦ï¼ GainNode.gain
ï¼ä¸æ¯ç®åå¼ï¼å®ä»¬å®é
䏿¯ AudioParam
ç±»å对象 â è¿äºè¢«ç§°ä¸ºåæ°ãè¿ä¹æ¯ä¸ºä»ä¹æä»¬éè¦è®¾ç½® GainNode.gain
ç value
屿§ï¼è䏿¯ç´æ¥è®¾ç½® gain
çå¼ãè¿ä½¿å¾å®ä»¬æ´å ççµæ´»ï¼å
è®¸ä¼ å
¥ä¸ç³»åç¹å®çå¼ä»¥å¨ä¾å¦ä¸æ®µæ¶é´å
æ¹åã
好çï¼ç°å¨ç¨æ·å¯ä»¥æ´æ°é³é¢çé³éï¼å¦æä½ è¦å¢å éé³åè½ï¼å¢çèç¹æ¯å¯ä½¿ç¨çå®ç¾èç¹ã
为åºç¨ç¨åºå¢å ç«ä½å£°å¹³ç§»è®©æä»¬æ·»å å¦ä¸ä¸ªä¿®æ¹é¶æ®µæ¥ç»ä¹ æä»¬ååå¦è¿çã
å¦æç¨æ·æ¥æç«ä½å£°åè½ï¼å¯ç¨ StereoPannerNode
èç¹æ¹å左峿¬å£°å¨ç平衡ã
夿³¨ï¼ StereoPannerNode
ç¨äºä½ åªæ³ä»å·¦å°å³è¿è¡ç«ä½å£°å¹³ç§»çç®åæ
åµãè¿æä¸ä¸ª PannerNode
ï¼å®å
许对 3D ç©ºé´æå£°é³ç©ºé´åè¿è¡å¤§éæ§å¶ä»¥å建æ´å¤æçææãè¿å¨æ¸¸æå 3D åºç¨ç¨åºä¸çæå°é¸é£è¿å¤´é¡¶æè
æ¥èªç¨æ·èº«åç声é³ã
为äºä½¿å ¶å¯è§åï¼æä»¬å°ä½¿æä»¬çé³é¢å¾å¦ä¸ï¼
è¿æ¬¡è®©æä»¬ä½¿ç¨æé 彿°æ¥çæèç¹ãå½æä»¬è¿æ ·åï¼æä»¬éè¦ä¼ å ¥ä¸ä¸æå该ç¹å®èç¹å¯è½éç¨çä»»ä½é项ï¼
const pannerOptions = { pan: 0 };
const panner = new StereoPannerNode(audioContext, pannerOptions);
夿³¨ï¼ ç®åçæèç¹çæé 彿°ä¸æ¯æ¯ä¸ªæµè§å¨é½æ¯æçãæ§å·¥å彿°æ¯ææ´ä¸ºå¹¿æ³ã
è¿éæä»¬çèå´ä» -1ï¼æå·¦è¾¹ï¼å 1ï¼æå³è¾¹ï¼ã忬¡è®©æä»¬ä½¿ç¨èå´ç±»åç input æ¥æ¹åè¿ä¸ªåæ°ï¼
<input type="range" id="panner" min="-1" max="1" value="0" step="0.01">
䏿们ä¹å䏿 ·ï¼æä»¬ä½¿ç¨æ¥èªè¿ä¸ª input ç弿¥è°æ´æä»¬ç panner çå¼ï¼
const pannerControl = document.querySelector("#panner");
pannerControl.addEventListener(
"input",
function () {
panner.pan.value = this.value;
},
false,
);
让æä»¬åæ¬¡è°æ´æä»¬çé³é¢å¾ï¼å°ææèç¹è¿æ¥å¨ä¸èµ·ï¼
track.connect(gainNode).connect(panner).connect(audioContext.destination);
å©ä¸è¦åçå°±æ¯è¯è¯è¿ä¸ªåºç¨ç¨åºï¼æ¥ç Codepen ä¸çæç»æ¼ç¤ºã
æè¦å¥½çï¼æä»¬æ¥æä¸ä¸ªé³ç®±ææ¾æä»¬çâç£å¸¦âï¼æä»¬å¯ä»¥è°æ´é³éåç«ä½å£°å£°åï¼ç»æä»¬æä¾äºä¸ä¸ªç¸å½åºæ¬çå·¥ä½é³é¢å¾è¡¨ã
è¿ææäºå¼å§åä½ çç½ç«æ Web åºç¨æ·»å é³é¢æéçå¾å°çå 个åºç¡ç¥è¯ãWeb Audio API è¿æå¾å¤åè½ï¼ä½ä¸æ¦ä½ ææ¡äºèç¹çæ¦å¿µåå°é³é¢èç¹å¾èç³»å¨ä¸èµ·ï¼æä»¬å¯ä»¥ç»§ç»ç ç©¶æ´å 夿çåè½ã
æ´å¤ä¾åè¿æå ¶ä»ç¤ºä¾å¯ä»¥äºè§£æå ³ Web Audio API çæ´å¤ä¿¡æ¯ã
Voice-change-O-matic æ¯ä¸ä¸ªæè¶£çè¯é³æçºµå¨åé³é¢å¯è§å web åºç¨ç¨åºï¼å è®¸ä½ éæ©ä¸åçææåå¯è§åã该åºç¨ç¨åºç¸å½å级ï¼ä½å®æ¼ç¤ºäºåæ¶ä½¿ç¨å¤ä¸ª Web Audio API ç¹æ§ï¼è¿è¡ Voice-change-O-matic liveï¼ã
å¦ä¸ä¸ªä¸é¨ç¨äºæ¼ç¤º Web Audio API çä¾åæ¯ Violent Thereminï¼ä¸ä¸ªå è®¸ä½ éè¿ç§»å¨é¼ æ æ¥æ¹åå®çé³è°é³éçç®åçåºç¨ç¨åºãå®è¿æä¾äºä¸ä¸ªè¿·å¹»çç¯å ç§ï¼æ¥ç Violent Theremin æºä»£ç ï¼
å¦åé æä»¬ç webaudio-examples repo 以è·åæ´å¤ç¤ºä¾ã
注ï¼ä»¥ä¸ä¸ºæ§ææ¡£ï¼å è¾å®æ´ï¼æ¤å¤æä¸å é¤ï¼æ¹ä¾¿å¼åè æ¥çã åºç¡æ¦å¿µå¤æ³¨ï¼ å¾å¤ç代ç ç¢çæ¥èªäºè¿ä¸ªä¾å Violent Theremin example.
Web Audio API å å«å¨é³é¢ä¸ä¸æçå¤çé³é¢æä½ï¼ä»¥å已被设计å 许模ååè·¯ç±ãåºæ¬é³é¢æä½å¯éè¿é³é¢èç¹è¿è¡ï¼è¿äºèç¹è¿æ¥å¨ä¸èµ·ï¼ç»æä¸ä¸ªé³é¢çè·¯ç±è¡¨ãå¤ä¸ªé³æºââ带æä¸åç±»åçé¢éé ç½®ââçè³å¯ä»¥è¢«ä¸ä¸ªä¸ä¸ææ¯æãè¿ä¸ªæ¨¡å设计æä¾äºåé 带æå¨æææç夿é³é¢åè½ççµæ´»æ§ã
é³é¢èç¹éè¿è¾å ¥ä¸è¾åºè¿è¡è¿æ¥ï¼å½¢æä¸ä¸ªé¾ï¼ä»ä¸ä¸ªæå¤ä¸ªæºåºåï¼éè¿ä¸ä¸ªææ´å¤çèç¹ï¼æç»å°è¾åºç»ç«¯ï¼ä½ ä¹å¯ä»¥ä¸æä¾è¾åºç»ç«¯ï¼æ¢å¥è¯è¯´ï¼å¦æåªæ¯æ³ä½¿ä¸äºé³é¢æ°æ®å¯è§åï¼ãä¸ä¸ªç®åç»å ¸ç web Audio ç工使µç¨å¦ä¸ï¼
æå»ºé³é¢ä¸ä¸æ AudioContext 对象ï¼
å¨ AudioContext 对象å ï¼æå»ºé³æºï¼æ¯å¦<audio>ï¼oscillatorï¼stream
æå»ºææèç¹ effectNodeï¼æ¯å¦æ··åï¼åäºé¶æ»¤æ³¢å¨ï¼å£°ç¸ï¼åéå¨
éæ©æç»çé³é¢ç®çå°ï¼æ¯å¦è¯´ä½ çç³»ç»æ¬å£°å¨
è¿æ¥æºå°ææï¼ææå°è¾åºç»ç«¯
é¦å ï¼ä½ éè¦æå»ºä¸ä¸ª AudioContext å®ä¾ï¼æ¥å建ä¸ä¸ªé³é¢å¾ãæç®åçæ¹æ³å°±åè¿æ ·ï¼
var audioCtx = new AudioContext();
夿³¨ï¼ åæ ·ä¸ä¸ªææ¡£æ¯å¯ä»¥åå¨å¤ä¸ª audioContext 对象çï¼ä½æ¯æ¯è¾æµªè´¹ã
ç¶èï¼æä¾ä¸ä¸ªçæ¬åç¼å¯¹äº webkit/Blink æµè§å¨æ¯å¾éè¦çï¼å¯¹äº Firefox(æ¡é¢ç/ææºç/OS ç) æ¯ä¸éè¦çãå¦ä¸ï¼
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
夿³¨ï¼ å½å建ä¸ä¸ªæ°ç conText 对象æ¶ï¼å¦æä½ ä¸æç¤º window 对象ï¼Safari 伿 æã
å建 AudioSourceç°å¨æä»¬æäº AudioContextï¼å¯ä»¥ç¨è¿ä¸ªæ¥åå¾å¤äºã第ä¸ä»¶æä»¬éè¦åçäºæ¯ç©é³ä¹ãé³é¢å¯ä»¥æ¥èªäºå¤æ ·çå°æ¹ï¼
OscillatorNode
æ¯å©ç¨AudioContext.createOscillator
æ¹æ³æ¥æå»ºãAudioContext.createBuffer()
, AudioContext.createBufferSource()
, 以å AudioContext.decodeAudioData()
.<video>
æè
<audio>
: å¯ä»¥ç AudioContext.createMediaElementSource()
.MediaStream
ä¾å¦æ¥èªäºæå头æéº¦å
é£ãå¯ä»¥çAudioContext.createMediaStreamSource()
.对äºè¿äºç¹æ®çä¾åï¼æä»¬å°ä¼ä¸ºæä»¬çæºæå»ºä¸ä¸ª oscillator æ¥æä¾ç®åçé³è°ï¼ä»¥å gain node æ¥æ§å¶é³é¢é³éï¼
var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
夿³¨ï¼ 为äºç´æ¥ææ¾ä¸ä¸ªé³ä¹æä»¶ï¼ä½ é常éè¿ XHR æ¥å è½½æä»¶ï¼éè¿ Buffer æ¥è§£ç ï¼å建 BufferSource. çè¿ä¸ª ä¾åæ¥èªäº Voice-change-O-matic.
夿³¨ï¼ Scott Michaud å·²ç»åäºä¸ä¸ªæç¨çåºæ¥å è½½åè§£ç ä¸ä¸ªæå¤ä¸ªé³é¢å®ä¾ï¼è¢«ç§°ä¸º AudioSampleLoader. è¿ä¸ªå¯ä»¥å¸®å©ç®å XHR/buffering çå¤çæä½ã
è¿æ¥è¾å ¥è¾åºä¸ºäºéè¿ä½ çæ¬å£°å¨æ¥å®é è¾åºé³è´¨ï¼ä½ éè¦å°å®ä»¬è¿æ¥èµ·æ¥ãè¿ä¸ªè¢«ç§°ä¸ºèç¹è¿æ¥æ¹æ³ï¼èç¹æ¥èªäºå¾å¤å¯è·å¾çä¸åèç¹ç±»åãä½ æ³è¦è¿æ¥çèç¹é½æä¾äºè¿ä¸ªæ¹æ³ã
ä½ ç设å¤çé»è®¤è¾åºç»æï¼é常æ¯ä½ çè®¾å¤æ¬å£°å¨ï¼ï¼éè¿AudioContext.destination
æ¥å
许è¿å
¥ã为äºè¿æ¥ oscillatorï¼gain node 以åè¾åºç«¯ï¼å¦ä»¥ä¸è¿ç¨ï¼
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
ä¸ä¸ªæ´å¤æçä¾åï¼ï¼æ¯å¦ Voice-change-O-matic), ä½ å¯ä»¥é¾æ¥å¾å¤ä½ æ³è¦çèç¹å¨ä¸èµ·ï¼ä¾å¦ï¼
source = audioCtx.createMediaStreamSource(stream);
source.connect(analyser);
analyser.connect(distortion);
distortion.connect(biquadFilter);
biquadFilter.connect(convolver);
convolver.connect(gainNode);
gainNode.connect(audioCtx.destination);
è¿ä¸ªå°ä¼åé ä¸ä¸ªå¦ä¸é³é¢èç¹å¾ï¼
ä½ ä¹å¯ä»¥é¾æ¥å¤ä¸ªèç¹å°ä¸ä¸ªèç¹ï¼æ¯å¦è¯´ä½ æ³è¦æ··åå¤ä¸ªé³é¢æºå¨ä¸èµ·ï¼å°±è®©å®ä»¬é½éè¿ä¸ä¸ªææèç¹ï¼æ¯å¦ gain nodeã
ææ¾é³ä¹å设置é³è°ç°å¨ audio èç¹å¾å·²ç»å»ºç«ï¼æä»¬å¯ä»¥è®¾ç½®å±æ§å¼åè°ç¨é³é¢èç¹çæ¹æ³æ¥è°èæ³è¦ç鳿ãå¨è¿ä¸ªç®åçä¾åï¼æä»¬å¯ä»¥è®¾ç½®ç¹æ®çé³è°ï¼ä»¥èµ«å ¹ä¸ºåä½ï¼è®¾ç½®ä¸ºç¹æ®ç±»åï¼ä»¥åæç¤ºé³ä¹ææ¾ï¼
oscillator.type = "sine"; // sine wave â other values are 'square', 'sawtooth', 'triangle' and 'custom'
oscillator.frequency.value = 2500; // value in hertz
oscillator.start();
卿们ç Violent Theremin ä¾åï¼è®¾å®äºä¸ä¸ªæå¤§ gain 以å frequencyï¼é¢çï¼å¼ï¼
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var maxFreq = 6000;
var maxVol = 1;
var initialFreq = 3000;
var initialVol = 0.5;
// set options for the oscillator
oscillator.type = "sine"; // sine wave â other values are 'square', 'sawtooth', 'triangle' and 'custom'
oscillator.frequency.value = initialFreq; // value in hertz
oscillator.start();
gainNode.gain.value = initialVol;
ç¶åæä»¬è®¾ç½®äºä¸ä¸ª frequency çæ°çå¼ï¼ä»¥å设置æ¯ä¸ªæ¶é´é¼ æ çç§»å¨ï¼åºäºç®åçé¼ æ åæ å¼ä½ä¸º frequency å gain çæå¤§å¼ç¾åæ¯ã
// Mouse pointer coordinates
var CurX;
var CurY;
// Get new mouse pointer coordinates when mouse is moved
// then set new gain and pitch values
document.onmousemove = updatePage;
function updatePage(e) {
CurX = window.Event
? e.pageX
: event.clientX +
(document.documentElement.scrollLeft
? document.documentElement.scrollLeft
: document.body.scrollLeft);
CurY = window.Event
? e.pageY
: event.clientY +
(document.documentElement.scrollTop
? document.documentElement.scrollTop
: document.body.scrollTop);
oscillator.frequency.value = (CurX / WIDTH) * maxFreq;
gainNode.gain.value = (CurY / HEIGHT) * maxVol;
canvasDraw();
}
ç®åç canvas å¯è§å
æ¯æ¬¡é¼ æ çç§»å¨ï¼canvasDraw() æ¹æ³ä¼è¢«è°ç¨ï¼é¼ æ åççä½ç½®ä¼ç»åºä¸ä¸ªå¤ååç»æçå°ç°ï¼å®ç大å°ä»¥åé¢è²ä¼åºäº frequency/gain çå¼ã
function random(number1, number2) {
var randomNo =
number1 + (Math.floor(Math.random() * (number2 - number1)) + 1);
return randomNo;
}
var canvas = document.querySelector(".canvas");
canvas.width = WIDTH;
canvas.height = HEIGHT;
var canvasCtx = canvas.getContext("2d");
function canvasDraw() {
rX = CurX;
rY = CurY;
rC = Math.floor((gainNode.gain.value / maxVol) * 30);
canvasCtx.globalAlpha = 0.2;
for (i = 1; i <= 15; i = i + 2) {
canvasCtx.beginPath();
canvasCtx.fillStyle =
"rgb(" +
100 +
i * 10 +
"," +
Math.floor((gainNode.gain.value / maxVol) * 255) +
"," +
Math.floor((oscillator.frequency.value / maxFreq) * 255) +
")";
canvasCtx.arc(
rX + random(0, 50),
rY + random(0, 50),
rC / 2 + i,
(Math.PI / 180) * 0,
(Math.PI / 180) * 360,
false,
);
canvasCtx.fill();
canvasCtx.closePath();
}
}
theremin çéé³
å½é鳿é®ç¹å»ï¼ä»¥ä¸æ¹æ³ä¼è¢«è°ç¨ï¼disconnect æ¹æ³ï¼å°åæ gain node ä¸ destination èç¹ç龿¥ï¼ææé»æ¢äºèç¹å¾ç龿¥ï¼æä»¥æ²¡æå£°é³ä¼è¢«äº§çã忬¡ç¹å»ææç¸åã
var mute = document.querySelector(".mute");
mute.onclick = function () {
if (mute.id == "") {
gainNode.disconnect(audioCtx.destination);
mute.id = "activated";
mute.innerHTML = "Unmute";
} else {
gainNode.connect(audioCtx.destination);
mute.id = "";
mute.innerHTML = "Mute";
}
};
å
¶ä»èç¹éæ©
è¿éæè®¸å¤éè¿ Web Audio API æ¥æå»ºçèç¹ï¼ä¸ä¸ªå¥½æ¶æ¯å°±æ¯ï¼æ»ä½æ¥è¯´ï¼æ£å¦æä»¬æè§ï¼ä»ä»¬ç¨åä¸ç§æ¹æ³å·¥ä½ï¼æå»ºèç¹ï¼è¿æ¥å°å¾è¡¨çå¦ä¸ä¸ªèç¹ï¼ç¶åå¤çèç¹å±æ§ä»¥åæ¹æ³æ¥ä½ç¨äºä½ æ³è¦ç鳿ºã
æä»¬å¹¶ä¸å¸æéè¿ææå¯è·å¾çææçï¼ä½ å¯ä»¥å¨Web_Audio_API
ä¸åçåèæ¥å£æ¾å°å¦ä½ä½¿ç¨æ¯ä¸ä¸ªç详述ãæä»¬ç°å¨æ¥æµè§ä¸ä¸åç设置ã
å©ç¨ AudioContext.createWaveShaper
æ¹æ³ï¼ä½ å¯ä»¥æå»ºä¸ä¸ª wave shaper node:
var distortion = audioCtx.createWaveShaper();
è¿ä¸ªå¯¹è±¡ä¸å®ä¼æ°å¦åçå®ä¹ wave shapeï¼ä¸ä¸ªè¢«åºç¨äºåºç¡å£°é³æ³¢æ¥åé ææ²çææãè¿äºæ³¢å¹¶ä¸å¥½è¢«è®¡ç®ï¼æå¥½çå¼å§æ¹æ³æ¯æç´¢ web ç®æ³ãæ¯å¦ï¼æä»¬å¯ä»¥ä» Stack Overflow æ¾å°ï¼
function makeDistortionCurve(amount) {
var k = typeof amount === "number" ? amount : 50,
n_samples = 44100,
curve = new Float32Array(n_samples),
deg = Math.PI / 180,
i = 0,
x;
for (; i < n_samples; ++i) {
x = (i * 2) / n_samples - 1;
curve[i] = ((3 + k) * x * 20 * deg) / (Math.PI + k * Math.abs(x));
}
return curve;
}
å¨ Voice-change-O-matic çæ¼ç¤ºä¸ï¼æä»¬è¿æ¥å° audio å¾è¡¨ä¸ç ditortion èç¹ï¼å½éè¦çæ¶åå¯ä»¥è¿ç¨ï¼
source.connect(analyser);
analyser.connect(distortion);
distortion.connect(biquadFilter);
...
distortion.curve = makeDistortionCurve(400);
Biquad filter
biquad filter æ¥æå¾å¤å¯éæ©çæ¹æ³ï¼éè¿ AudioContext.createBiquadFilter
æ¹æ³æ¥æå»ºï¼
var biquadFilter = audioCtx.createBiquadFilter();
å¨ Voice-change-o-matic çæ¼ç¤ºä¸ï¼è¿ç¨çå¶å®é项æ¯âlowshelfâè¿æ»¤å¨ï¼å®æä¾äºä½é³çåºæ¬å¢å¹ æ¹æ³ï¼
biquadFilter.type = "lowshelf";
biquadFilter.frequency.value = 1000;
biquadFilter.gain.value = 25;
æä»¬å¨è¿é详述äºè¿æ»¤å¨çç±»åï¼é¢çå¼ï¼å¢å¹ å¼ãå¨ lowshelf è¿æ»¤å¨æ åµï¼ææçæå®é¢çæ¥æ 25 åè´çå¢å¹ å¼ã
Web Audio API çå ¶ä»Web Audio API å¯ä»¥åä¸ä» ä» é³é¢å¯è§ååä¸ä¸åï¼å¦ panningï¼çäºæ ãæä»¬å°ä¼å¨ä¹åçæç« æ¶åå ¶ä»çæ´å¤å 容ã
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