一文詳解Web Audi 繪制音頻圖譜
背景
前端處理音頻,目前一些開源的插件和js庫(kù)已經(jīng)提供了非常好的支持。其中小編了解的比較多的是sound.js和wavasuffer.js這倆個(gè)庫(kù)。其中sound.js是一個(gè)大而全的音頻處理庫(kù),功能豐富,兼容性也處理的很好。wavesuffer則偏重于音頻波形圖繪制處理,相對(duì)比較輕量。小編此篇不在于比較二者的差異,而是和大家一起學(xué)習(xí)下如何自己實(shí)現(xiàn)一個(gè)簡(jiǎn)易的音頻圖譜繪制。
實(shí)現(xiàn)思路
先介紹下小編的整體思路吧。所謂的音頻圖譜,其實(shí)只是將聲音的響度具象化為一個(gè)波形圖,響度高對(duì)應(yīng)的波形高,響度低波形也就低。所以第一步,我們可以通過(guò)xhr拿到一個(gè)音頻文件的數(shù)據(jù)。那么,第二步便是如何處理這組數(shù)據(jù),讓數(shù)據(jù)能夠比較真實(shí)的反應(yīng)音頻的響度。這時(shí)候就需要前端的Web Audio Api來(lái)發(fā)揮作用了,具體如何處理,我們后面詳細(xì)說(shuō)明。完成數(shù)據(jù)處理之后,最后一步就是需要根據(jù)數(shù)據(jù)繪制出波形圖,這里我們使用canvas來(lái)做波形圖的繪制。
獲取音頻文件
首先,我們利用fetch,來(lái)獲取一個(gè)線上音頻。這里,我們借用一下wavesuffer官網(wǎng)demo中用的線上音頻來(lái)做示范。
// 音頻url
let audioUrl = 'https://wavesurfer-js.org/example/media/demo.wav';
// 創(chuàng)建音頻上下文
let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// 創(chuàng)建音頻源
let source = audioCtx.createBufferSource();
/*
* 通過(guò)fetch下載音頻,responseType設(shè)置為'arrayBuffer',我們以arrayBuffer格式接收返回的數(shù)據(jù)
*/
fetch(audioUrl, {
method: 'GET',
responseType: 'arraybuffer',
}).then(res => {
return res.arrayBuffer();
}).then(data => {
// 處理音頻數(shù)據(jù)
initAudio(data);
});
利用Web Audio Api 處理音頻數(shù)據(jù)
拿到音頻數(shù)據(jù)之后,我們需要利用Web Audio Api,來(lái)處理音頻數(shù)據(jù),實(shí)現(xiàn)音頻的播放,暫停等操作以及我們后續(xù)的波形圖繪制。這里簡(jiǎn)單介紹下,Web Audio Api是一組非常強(qiáng)大的Api,它提供了在Web中控制音頻、處理音頻的一整套有效通用的系統(tǒng)。它能夠允許開發(fā)著,控制音頻,自選音頻源、對(duì)音頻添加特效,使音頻可視化,添加空間效果,添加混響等等。而我們今天要實(shí)現(xiàn)的功能,僅僅只用到了其中幾個(gè)Api,整體流程如下:

// audio 初始化
function initAudio (data) {
// 音頻數(shù)據(jù)解碼
// decodeAudioData方法接收一個(gè)arrayBuffer數(shù)據(jù)作為參數(shù),這也是為什么前面fetch音頻時(shí)設(shè)置以arrayBuffer格式接收數(shù)據(jù)
audioCtx.decodeAudioData(data).then(buffer => {
// decodeAudioData解碼完成后,返回一個(gè)AudioBuffer對(duì)象
// 繪制音頻波形圖
drawWave(buffer);
// 連接音頻源
source.buffer = buffer;
source.connect(audioCtx.destination);
// 音頻數(shù)據(jù)處理完畢
alert('音頻數(shù)據(jù)處理完畢!');
});
}
// web audio 規(guī)范不允許音頻自動(dòng)播放,需要用戶觸發(fā)頁(yè)面事件來(lái)觸發(fā)播放,這里我們?cè)黾右粋€(gè)播放按鈕,數(shù)據(jù)處理完畢后點(diǎn)擊播放
document.querySelector('#btn').onclick = () => {
// 播放音頻
source.start(0);
}
通過(guò)解碼后的音頻數(shù)據(jù),繪制波形圖
音頻數(shù)據(jù)通過(guò)AudioContext解碼后,返回一個(gè)AudioBuffer對(duì)象,這個(gè)對(duì)象,保存有音頻的采樣率、聲道、pcm數(shù)據(jù)等信息。通過(guò)getChannelData方法可以獲取到音頻某個(gè)聲道的pcm數(shù)據(jù)。返回的是一個(gè)Float32Array對(duì)象,數(shù)值范圍在-1到1之間。音頻數(shù)據(jù)比較龐大,每一秒鐘可能包含成千上萬(wàn)的數(shù)據(jù),因此我們?cè)谧鰣D形繪制時(shí),需要對(duì)數(shù)據(jù)進(jìn)一步采樣。比如,這里我們采用每1000條數(shù)據(jù)中,取一個(gè)最大值(正數(shù))一個(gè)最小值(負(fù)數(shù))來(lái)繪制圖形;
// 繪制波形圖
function drawWave (buffer) {
// buffer.numberOfChannels返回音頻的通道數(shù)量,1即為單聲道,2代表雙聲道。這里我們只取一條通道的數(shù)據(jù)
let data = [];
let originData = buffer.getChannelData(0);
// 存儲(chǔ)所有的正數(shù)據(jù)
let positives = [];
// 存儲(chǔ)所有的負(fù)數(shù)據(jù)
let negatives = [];
// 先每隔100條數(shù)據(jù)取1條
for (let i = 0; i < originData.length; i += 100) {
data.push(originData[i]);
}
// 再?gòu)膁ata中每10條取一個(gè)最大值一個(gè)最小值
for (let j = 0, len = parseInt(data.length / 10); j < len; j++) {
let temp = data.slice(j * 10, (j + 1) * 10);
positives.push(Math.max.apply(null, temp));
negatives.push(Math.min.apply(null, temp));
}
// 創(chuàng)建canvas上下文
let canvas = document.querySelector('#canvas');
if (canvas.getContext) {
let ctx = canvas.getContext('2d');
canvas.width = positives.length;
let x = 0;
let y = 100;
let offset = 0;
ctx.fillStyle = '#fa541c';
ctx.beginPath();
ctx.moveTo(x, y);
// canvas高度200,橫坐標(biāo)在canvas中點(diǎn)100px的位置,橫坐標(biāo)上方繪制正數(shù)據(jù),下方繪制負(fù)數(shù)據(jù)
// 先從左往右繪制正數(shù)據(jù)
// x + 0.5是為了解決canvas 1像素線條模糊的問(wèn)題
for (let k = 0; k < positives.length; k++) {
ctx.lineTo(x + k + 0.5, y - (100 * positives[k]));
}
// 再?gòu)挠彝罄L制負(fù)數(shù)據(jù)
for (let l = negatives.length - 1; l >= 0; l--) {
ctx.lineTo(x + l + 0.5, y + (100 * Math.abs(negatives[l])));
}
// 填充圖形
ctx.fill();
}
};
這樣,簡(jiǎn)單的音頻波形圖繪制就完成了。小編這里僅做拋磚引玉,簡(jiǎn)單介紹下Web Audio的一個(gè)應(yīng)用場(chǎng)景。更多更復(fù)雜的應(yīng)用,大家可以深入了解學(xué)習(xí)Web Audio相關(guān)api。最后,貼一下效果圖:

以上就是一文詳解Web Audi 繪制音頻圖譜的詳細(xì)內(nèi)容,更多關(guān)于Web Audio繪制音頻圖譜的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于JS實(shí)現(xiàn)蜘蛛俠動(dòng)作游戲的示例代碼
這篇文章主要介紹了如何利用JavaScript實(shí)現(xiàn)簡(jiǎn)單的蜘蛛俠動(dòng)作游戲,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)JavaScript游戲開發(fā)有一定的幫助,需要的可以參考一下2022-06-06
javaScript canvas實(shí)現(xiàn)(畫筆大小 顏色 橡皮的實(shí)例)
下面小編就為大家分享一篇javaScript canvas實(shí)現(xiàn)(畫筆大小 顏色 橡皮的實(shí)例),具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-11-11
livereload工具實(shí)現(xiàn)前端可視化開發(fā)【推薦】
本文將介紹一個(gè)工具--livereload。這是一款能根據(jù)你本地文件(html、css、js)的變化,自動(dòng)跟蹤刷新瀏覽器的實(shí)時(shí)刷新工具,有了這個(gè)工具,會(huì)大大減輕你刷新頁(yè)面的工作量。下面跟著小編一起來(lái)看下吧2016-12-12
第一次接觸神奇的Bootstrap網(wǎng)格系統(tǒng)
第一次接觸神奇的Bootstrap網(wǎng)格系統(tǒng),Bootstrap讓W(xué)eb開發(fā)更迅速、更簡(jiǎn)單,感興趣的小伙伴們可以參考一下2016-07-07
一個(gè)非常好用的文字滾動(dòng)的案例,鼠標(biāo)懸浮可暫停[兩種方案任選]
本文主要介紹了非常好用的文字滾動(dòng)的案例,鼠標(biāo)懸浮可暫停的兩種實(shí)現(xiàn)方案,代碼簡(jiǎn)潔,可收藏備用。需要的朋友來(lái)看下吧2016-12-12
js return返回多個(gè)值,通過(guò)對(duì)象的屬性訪問(wèn)方法
下面小編就為大家?guī)?lái)一篇js return返回多個(gè)值,通過(guò)對(duì)象的屬性訪問(wèn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02
js數(shù)組常用19種方法(你會(huì)的到底有多少呢)
這篇文章主要給大家介紹了關(guān)于js數(shù)組常用19種方法,大家可以看看你會(huì)的到底有多少呢,在日常開發(fā)中我們會(huì)接觸到j(luò)s中數(shù)組的一些方法,需要的朋友可以參考下2023-09-09
html+css+js實(shí)現(xiàn)別踩白板小游戲
大家好,本篇文章主要的講的是html+css+js實(shí)現(xiàn)別踩白板小游戲,感興趣的同學(xué)趕快來(lái)看一看吧,覺得不錯(cuò)的話可以收藏一下哦,方便下次瀏覽2021-11-11

