1.Web Audio API 介紹
Web Audio API 提供了在Web上控制音頻的一個非常有效通用的系統 ,這些通用系統通俗的講就是我們可以利用Web Audio API提供的各種方法操作各種源中的聲音,處理聲音,使聲音可視化等。
要使用Web Audio API,我們還是先來簡單的了解一下它的工作流程:
- 創建音頻環境(eg. AudioContext..)
- 在音頻環境里創建源 — 例如
, 振盪器, 流(eg. navigator.getUserMedia/createMediaElementSource..) - 創建效果節點,例如混響、雙二階濾波器、平移、壓縮(Web Audio API 提供一些簡單的濾波器或者延時器等,可以制作一個簡單的混音工具)
- 為音頻選擇一個目地,例如你的系統揚聲器
- 連接源到效果器,以及效果器和目地(分析和可視化eg. AnalyserNode)
其兼容性如下:
桌面端:
| 瀏覽器 | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari (WebKit) |
| 支持版本 | 14 |
23 | 未實現 | 15 | 6 |
移動端:
| 瀏覽器 |
Android |
Chrome |
Firefox Mobile (Gecko) |
Firefox OS |
IE Phone |
Opera Mobile |
Safari Mobile |
|---|---|---|---|---|---|---|---|
| 支持版本 | 未實現 | 28 |
25 | 1.2 | 未實現 | 未實現 | 6 |
2.示例代碼
"talking is cheap , show me the codes."
我知道各位看官並不想聽什么時域頻域變換,什么傅立葉變換,什么web audio api原理,那我就廢話不多說,直接放碼過來了,先看看效果再來給大家解釋。
盡情復制粘貼,然后拿個現代一點的瀏覽器跑一下(用IE的請點右上角紅叉,謝謝)。
注意:audio標簽的src屬性內容請自己在本機找一個瀏覽器支持的聲音源格式。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="Author" contect="GabrielChen">
<meta name="keywords" contect="Web Audio API">
<title>Web Audio API 學習</title>
<script>
var canvas;
var ctx;
var audioContext;
var analyser;
var mic;
function init() {
canvasOne = document.getElementById('canvasOne');
ctx = canvasOne.getContext("2d");
canvasTwo = document.getElementById('canvasTwo');
ctx2 = canvasTwo.getContext("2d");
}
navigator.getMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
navigator.getMedia ( { audio: true }, function (stream) {
audioContext = new (window.AudioContext || window.webkitAudioContext);
mic = audioContext.createMediaStreamSource(stream);
analyser= audioContext.createAnalyser();
analyser.fftSize = 256;
mic.connect(analyser);
drawSpectrum();
},function(){});
function drawSpectrum() {
var WIDTH = canvasOne.width;
var HEIGHT= canvasOne.height;
var array = new Uint8Array(128);
analyser.getByteFrequencyData(array);
ctx.clearRect(0, 0, WIDTH, HEIGHT);
ctx2.clearRect(0, 0, 800, 800);
for ( var i = 0; i < (array.length); i++ ){
var value = array[i];
ctx.fillRect(i*5,HEIGHT-value,3,HEIGHT);
}
for ( var i = 0; i < (array.length); i++ ){
var value = array[i];
ctx2.beginPath();
ctx2.arc(300,300,value,0,360,false);
ctx2.lineWidth=5;
ctx2.strokeStyle="rgba("+value+","+value+",0,0.2)";
ctx2.stroke();//畫空心圓
ctx2.closePath();
}
requestAnimationFrame(drawSpectrum);
};
</script>
<style>
#canvasOne {
border: 1px solid #ddd;
}
</style>
</head>
<body onload="init();">
<h1>從audio源獲取聲音</h1>
<audio src="./Fatbros.ogg" controls="controls" id="audio">你的瀏覽器不支持audio標簽</audio>
<h1>audio讀取聲音</h1>
<canvas id="canvasFormAudio" width="640"></canvas>
<h1>頻域圖模仿</h1>
<canvas id="canvasOne" width="640"></canvas>
<h1>圓形聲波圖</h1>
<canvas id="canvasTwo" width="800" height="800"></canvas>
<script type="text/javascript">
var context1;
var source;
var analyserfa;
var canvasFormAudio;
var ctxfa;
canvasFormAudio = document.getElementById('canvasFormAudio');
ctxfa = canvasFormAudio.getContext("2d");
try {
context1 = new (window.AudioContext || window.webkitAudioContext);
} catch(e) {
throw new Error('The Web Audio API is unavailable');
}
analyserfa=context1.createAnalyser();
window.addEventListener('load', function(e) {
var audio =document.getElementById("audio");
var source = context1.createMediaElementSource(audio);
source.connect(analyserfa);
analyserfa.connect(context1.destination);
drawSpectrumfa();
}, false);
function drawSpectrumfa() {
var WIDTH = canvasFormAudio.width;
var HEIGHT= canvasFormAudio.height;
var array = new Uint8Array(128);
analyserfa.getByteFrequencyData(array);
ctxfa.clearRect(0, 0, WIDTH, HEIGHT);
for ( var i = 0; i < (array.length); i++ ){
var value = array[i];
ctxfa.fillRect(i*5,HEIGHT-value,3,HEIGHT);
}
requestAnimationFrame(drawSpectrumfa);
}
</script>
</body>
</html>
3.代碼分析
我們從body部分入手分析
<body onload="init();"> <h1>從audio源獲取聲音</h1> <audio src="./Fatbros.ogg" controls="controls" id="audio">你的瀏覽器不支持audio標簽</audio> <h1>audio讀取聲音</h1> <canvas id="canvasFormAudio" width="640"></canvas> <h1>頻域圖模仿</h1> <canvas id="canvasOne" width="640"></canvas> <h1>圓形聲波圖</h1> <canvas id="canvasTwo" width="800" height="800"></canvas>
用onload屬性調用初始化函數init(),主要在頁面生成之后初始化一些變量,避免讀不到相關DOM。
function init() {
canvasOne = document.getElementById('canvasOne');
ctx = canvasOne.getContext("2d");
canvasTwo = document.getElementById('canvasTwo');
ctx2 = canvasTwo.getContext("2d");
}
第一塊canvas:從audio源獲取聲音
首先一個audio標簽,在本機選定一個src,設置controls屬性代表瀏覽器顯示播放器控制頁面,設置id為audio。
再設置一個id為"canvasFormAudio"的畫布canvas。
<audio src="./Fatbros.ogg" controls="controls" id="audio">你的瀏覽器不支持audio標簽</audio> <h1>audio讀取聲音</h1> <canvas id="canvasFormAudio" width="640"></canvas>
獲取聲音源以及繪圖
//直接從audio處理音頻源,聲明一些必要的變量
var context1;
var source;
var analyserfa;
var canvasFormAudio;
var ctxfa;
//初始化畫布
canvasFormAudio = document.getElementById('canvasFormAudio');
ctxfa = canvasFormAudio.getContext("2d");
//建立一個音頻環境,因為瀏覽器實現不同,做了一點兼容性處理
try {
context1 = new (window.AudioContext || window.webkitAudioContext);
} catch(e) {
throw new Error('The Web Audio API is unavailable');
}
//建立一個分析器
analyserfa=context1.createAnalyser();
window.addEventListener('load', function(e) {
// 從audio標簽獲取聲音源 source
var audio =document.getElementById("audio");
var source = context1.createMediaElementSource(audio);
source.connect(analyserfa);
analyserfa.connect(context1.destination);
//調用繪圖函數
drawSpectrumfa();
}, false);
//繪圖函數
function drawSpectrumfa() {
var WIDTH = canvasFormAudio.width;
var HEIGHT= canvasFormAudio.height;
var array = new Uint8Array(128);
//復制當前的頻率值到一個無符號數組中
analyserfa.getByteFrequencyData(array);
//clearRect(矩形左上角x坐標,矩形左上角y坐標,清除矩形的寬,清除矩形的高)
ctxfa.clearRect(0, 0, WIDTH, HEIGHT);
//循環生成長條矩形
for ( var i = 0; i < (array.length); i++ ){
var value = array[i];
//fillRect(矩形左上角x坐標,矩形左上角y坐標,矩形寬,矩形高)
//這里我們的array一共有128組數據,所以我們當時canvas設置的寬度為5*128=640
ctxfa.fillRect(i*5,HEIGHT-value,3,HEIGHT);
}
//根據瀏覽器頻率繪圖或者操作一些非css效果
requestAnimationFrame(drawSpectrumfa);
}
第二塊:頻域圖模仿和圓形聲波圖
這兩個圖的音源都是利用瀏覽器調用電腦麥克風取得,所以一定要同意瀏覽器請求的麥克風權限。
繪圖區域
<h1>頻域圖模仿</h1> <canvas id="canvasOne" width="640"></canvas> <h1>圓形聲波圖</h1> <canvas id="canvasTwo" width="800" height="800"></canvas>
初始化init()函數、從麥克風獲取音源和繪圖函數
var canvas;
var ctx;
var audioContext;
var analyser;
var mic;
//初始化兩個畫布的函數,聲明為2d繪圖
function init() {
canvasOne = document.getElementById('canvasOne');
ctx = canvasOne.getContext("2d");
canvasTwo = document.getElementById('canvasTwo');
ctx2 = canvasTwo.getContext("2d");
}
//getMedia調用參數如下,返回一個多媒體流
//constraints可選{ video: true, audio: true },代表獲取多媒體的類型
//var stream = navigator.getUserMedia(constraints, successCallback, errorCallback);
navigator.getMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
navigator.getMedia ( { audio: true }, function (stream) {
audioContext = new (window.AudioContext || window.webkitAudioContext);
//返回一個多媒體流
mic = audioContext.createMediaStreamSource(stream);
//creates an AnalyserNode 創建一個分析節點
analyser= audioContext.createAnalyser();
//fftsize默認值2048,是快速傅立葉變換用於頻域分析的值,必須為2的冪,而我們得到的數據通常為其的一半,下面會說道
analyser.fftSize = 256;
mic.connect(analyser);
//調用繪圖函數
drawSpectrum();
},function(){});
//圓形聲波繪圖和矩形繪圖
function drawSpectrum() {
var WIDTH = canvasOne.width;
var HEIGHT= canvasOne.height;
//長度為128無符號數組用於保存getByteFrequencyData返回的頻域數據
var array = new Uint8Array(128);
analyser.getByteFrequencyData(array);
//以下是根據頻率數據畫圖,主要為canvas知識,不做詳細解答
ctx.clearRect(0, 0, WIDTH, HEIGHT);
ctx2.clearRect(0, 0, 800, 800);
for ( var i = 0; i < (array.length); i++ ){
var value = array[i];
ctx.fillRect(i*5,HEIGHT-value,3,HEIGHT);
}
//ctx2.clearRect(700, 700, WIDTH, HEIGHT);
for ( var i = 0; i < (array.length); i++ ){
var value = array[i];
ctx2.beginPath();
ctx2.arc(300,300,value,0,360,false);
ctx2.lineWidth=5;
ctx2.strokeStyle="rgba("+value+","+value+",0,0.2)";
ctx2.stroke();//畫空心圓
ctx2.closePath();
}
//
requestAnimationFrame(drawSpectrum);
};
4.最終效果


6.最新進展
文中的getMedia方法(getUserMedia)在chrome 47后已經不可以從非安全源訪問(Insecure Origins),即http協議,.firefox還可以但不知道為什么有bug,幾秒鍾后就share設備失敗。
現在chrome開發者可以使用以下方法繼續在非安全源使用這個函數:
You can run chrome with the --unsafely-treat-insecure-origin-as-secure="example.com" flag (replacing "example.com"with the origin you actually want to test), which will treat that origin as secure for this session. Note that you also need to include the --user-data-dir=/test/only/profile/dir to create a fresh testing profile for the flag to work
7.參考
1.MDN
2.api接口查詢
3.chrome
