使用canvas繪圖音樂(lè)頻譜示例及技術(shù)分析
正文
看到跳動(dòng)的音符你難道不想知道它是如何實(shí)現(xiàn)的么?開(kāi)擼!
問(wèn):實(shí)現(xiàn)這個(gè)有什么用呢?
答:裝杯用!??其實(shí)這個(gè)操作能實(shí)現(xiàn)音頻效果器。就是主播開(kāi)麥后大媽聲音變成少女,男人聲音可以變女聲!??因?yàn)橐纛l分析器可以拿到音頻的頻譜,既可以改變音調(diào)啊音色啊什么的就可以實(shí)現(xiàn)音頻效果器!??
不想看我啰嗦的直接看最后,有整體html+js代碼,粘貼到你的html頁(yè)面內(nèi),換以下音頻資源鏈接就能看到效果了!
效果圖
技術(shù)分析
音頻加載
在html
內(nèi)增加audio
標(biāo)簽。將音樂(lè)加載進(jìn)來(lái)。給audio
標(biāo)簽增加controls
屬性用來(lái)控制音樂(lè)播放。如下:
<html> <body> <audio src="/source/YOU.m4a" controls></audio> </body> </html>
頻譜獲取
要獲取音頻的頻譜需要使用javascript
的AudioContext
對(duì)象。
var audio = document.querySelector("audio"); let dataArray = new Uint8Array(512); let analyser = null audio.onplay = function() { let audctx = new AudioContext() // 創(chuàng)建音頻上下文 let source = audctx.createMediaElementSource(audio) // 創(chuàng)建音頻源 analyser = audctx.createAnalyser() // 創(chuàng)建音頻分析器 analyser.fftSize = 512 // 設(shè)置分析器大小, 必須為2^n次方 // 創(chuàng)建一個(gè)數(shù)組,用于存放分析器節(jié)點(diǎn)數(shù)據(jù) // analyser.frequencyBinCount可以拿到傅里葉變換的值,因?yàn)楦道锶~變換是對(duì)稱的因此只要一半即可(傅里葉變換請(qǐng)自行搜索) dataArray = new Uint8Array(analyser.frequencyBinCount) source.connect(analyser) // 音頻源連接到音頻分析器 analyser.connect(audctx.destination) // 音頻分析器連接到音頻輸出 }
至此我們已經(jīng)獲取到了音頻的頻譜數(shù)據(jù)。
頻譜可視化
對(duì)于高效繪制需要用到畫(huà)布,既canvas
標(biāo)簽,在body
內(nèi)增加canvas
標(biāo)簽,并起id
為cvs
。
<body> <canvas id="cvs"></canvas> <audio src="/source/YOU.m4a" controls></audio> </body>
var cvs = document.getElementById("cvs"); var ctx = cvs.getContext("2d"); function initCanvas() { // 設(shè)置canvas寬度為瀏覽器可視區(qū)域 cvs.width = window.innerWidth * devicePixelRatio; // 設(shè)置canvas高度為瀏覽器可視區(qū)域的一半 cvs.height = window.innerHeight / 2 * devicePixelRatio; } // 窗口變化時(shí)候重新設(shè)置canvas寬高 window.onresize = function() { initCanvas(); } // 定義draw方法用于繪制頻譜 function draw() { // 使用瀏覽器自帶幀動(dòng)畫(huà)函數(shù)實(shí)現(xiàn)每一幀都繪制。 requestAnimationFrame(draw); // 清空畫(huà)布 ctx.clearRect(0, 0, cvs.width, cvs.height); const len = dataArray.length / 1.2; // 截取頻譜繪制長(zhǎng)度 const barWidth = cvs.width / len; // 每一根柱子的寬度 ctx.fillStyle = '#c875fc'; // 給柱子填充顏色 // 遍歷每一根柱子,設(shè)置其位置以及邊框。 for (let i = 0; i < len; i++) { const data = dataArray[i] const barHeight = data / 255 * cvs.height const x = i * barWidth const y = cvs.height - barHeight drawBorder(x,y,barWidth, barHeight) ctx.fillStyle = '#8ca86d' ctx.fillRect(x, y, barWidth, barHeight) } }
至此基本完成了繪制方面的任務(wù)
全部合并后就是以下代碼,將以下代碼在你本機(jī)創(chuàng)建一個(gè)html復(fù)制進(jìn)去就可以看到效果(注意把音頻資源換成自己的):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>聲音波浪</title> </head> <style> #cvs { border: 1px solid #000; } audio { display: block; } </style> <body> <canvas id="cvs"></canvas> <audio src="/source/YOU.m4a" controls></audio> </body> <script> var cvs = document.getElementById("cvs"); var ctx = cvs.getContext("2d"); var audio = document.querySelector("audio"); let isInitAudio = false; let analyser = null let dataArray = new Uint8Array(512); /* 初始化canvas */ function initCanvas() { cvs.width = window.innerWidth * devicePixelRatio; cvs.height = window.innerHeight / 2 * devicePixelRatio; } window.onresize = function() { initCanvas(); } /* 初始化音頻 */ function initAudio() { audio.onplay = function() { let audctx = new AudioContext() // 創(chuàng)建音頻上下文 let source = audctx.createMediaElementSource(audio) // 創(chuàng)建音頻源 analyser = audctx.createAnalyser() // 創(chuàng)建音頻分析器 analyser.fftSize = 512 // 設(shè)置分析器大小, 必須為2^n次方 // 創(chuàng)建一個(gè)數(shù)組,用于存放分析器節(jié)點(diǎn)數(shù)據(jù) // analyser.frequencyBinCount可以拿到傅里葉變換的值,因?yàn)楦道锶~變換是對(duì)稱的因此只要一半即可 dataArray = new Uint8Array(analyser.frequencyBinCount) draw() source.connect(analyser) // 音頻源連接到音頻分析器 analyser.connect(audctx.destination) // 音頻分析器連接到音頻輸出 let bufferLength = analyser.frequencyBinCount } } function draw() { requestAnimationFrame(draw); // 清空畫(huà)布 ctx.clearRect(0, 0, cvs.width, cvs.height); analyser.getByteFrequencyData(dataArray) const len = dataArray.length / 1.2 const barWidth = cvs.width / len ctx.fillStyle = '#c875fc' for (let i = 0; i < len; i++) { const data = dataArray[i] const barHeight = data / 255 * cvs.height const x = i * barWidth const y = cvs.height - barHeight drawBorder(x,y,barWidth, barHeight) ctx.fillStyle = '#8ca86d' ctx.fillRect(x, y, barWidth, barHeight) } } function drawBorder(xPos, yPos, width, height, thickness = 1) { ctx.fillStyle='#000'; ctx.fillRect(xPos - (thickness), yPos - (thickness), width + (thickness * 2), height + (thickness * 2)); } function initAll() { initCanvas(); initAudio() } initAll() </script> </html>
以上就是使用canvas實(shí)現(xiàn)音樂(lè)頻譜示例及技術(shù)分析的詳細(xì)內(nèi)容,更多關(guān)于canvas音樂(lè)頻譜的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS網(wǎng)頁(yè)播放聲音實(shí)現(xiàn)代碼兼容各種瀏覽器
JS網(wǎng)頁(yè)播放聲音有多種方法可以實(shí)現(xiàn),不過(guò)兼容各種瀏覽器的就沒(méi)有幾個(gè)了,不過(guò)本文的這個(gè)示例或許對(duì)大家有所幫助2013-09-09支付寶小程序自定義彈窗dialog插件的實(shí)現(xiàn)代碼
支付寶小程序官方提供的alert提示框、dialog對(duì)話框、model彈窗功能比較有限,有些都不能隨意自定義修改的。這篇文章主要介紹了支付寶小程序自定義彈窗dialog插件的實(shí)現(xiàn)代碼,需要的朋友可以參考下2018-11-11微信小程序開(kāi)發(fā)WXML模板語(yǔ)法基礎(chǔ)教程
這篇文章主要介紹了微信小程序模板語(yǔ)法,WXML(WeiXin?Markup?Language)是框架設(shè)計(jì)的一套標(biāo)簽語(yǔ)言,結(jié)合基礎(chǔ)組件、事件系統(tǒng),可以構(gòu)建出頁(yè)面的結(jié)構(gòu),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08用JS實(shí)現(xiàn)HTML標(biāo)簽替換效果
用JS實(shí)現(xiàn)HTML標(biāo)簽替換效果...2007-06-06js數(shù)組循環(huán)遍歷數(shù)組內(nèi)所有元素的方法
在js中數(shù)組遍歷最簡(jiǎn)單的辦法就是使用for然后再利用arr.length長(zhǎng)度作為for最大限度值即可解決了,下面我們來(lái)看看一些有用的實(shí)例2014-01-01???????Rxjs?map,?mergeMap?和?switchMap?的區(qū)別與聯(lián)系
這篇文章主要介紹了???????Rxjs?map,mergeMap和switchMap的區(qū)別與聯(lián)系,map、mergeMap和switchMap是RxJS中的三個(gè)主要運(yùn)算符,在SAP?Spartacus開(kāi)發(fā)中有著廣泛的使用場(chǎng)景2022-07-0710個(gè)功能強(qiáng)大的JavaScript動(dòng)畫(huà)庫(kù)分享
動(dòng)畫(huà),從人群中脫穎而出、吸引訪客注意力的絕佳方式,本文將給大家分享10 個(gè)功能強(qiáng)大的 JavaScript 動(dòng)畫(huà)庫(kù),有了這 10 個(gè)功能強(qiáng)大的 JavaScript 庫(kù),創(chuàng)建動(dòng)畫(huà)再簡(jiǎn)單不過(guò)了,感興趣的同學(xué)可以參考閱讀2023-09-09JS關(guān)鍵字球狀旋轉(zhuǎn)效果的實(shí)例代碼
這篇文章主要介紹了JS關(guān)鍵字球狀旋轉(zhuǎn)效果的實(shí)例代碼。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11