<audio>
標籤是HTML5的新標籤,經過添加src
屬性實現音樂播放。javascript
AudioContext
是音頻播放環境,原理與canvas的繪製環境相似,都是須要建立環境上下文,經過上下文的調用相關的建立音頻節點,控制音頻流播放暫停操做等操做,這一些操做都須要發生在這個環境之中。html
try{ var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); }catch(e){ alert('Web Audio API is not supported in this browser'); } 複製代碼
AudioNode
接口是一個處理音頻的通用模塊,它能夠是音頻音源模塊,音頻播放設備模塊,也能夠是中間音頻處理模塊。不一樣的音頻節點的鏈接(經過AudioContext.connect()
),以及終點鏈接AudioContext.destination
(能夠看做是鏈接到耳機或揚聲器設備)完成後,才能輸出音樂。java
常見的音頻節點:
AudioBufferSourceNode: 播放和處理音頻數據
AnalyserNode: 顯示音頻時間和頻率數據 (經過分析頻率數據能夠繪製出波形圖之類的視圖,可視化的主要途徑)
GainNode: 音量節點,控制音頻的總音量
MediaElementAudioSourceNode: 關聯HTMLMediaElement,播放和處理來自<video>和<audio>元素的音頻
OscillatorNode: 一個週期性波形,只建立一個音調
...
複製代碼
try{ var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); }catch(e){ alert('Web Audio API is not supported in this browser'); } 複製代碼
因爲音頻文件的數據是二進制(非文本),因此要設置請求頭的responseType
爲arraybuffer
,將.mp3音頻文件轉換成數組緩衝區ArrayBuffergit
當AudioContext.decodeAudioData
解碼成功以後獲取buffer
,執行回調函數,將數據放入AudioBufferSourceNode
中github
方法一採用流式加載音樂文件,簡單易懂,缺點是經過createMediaElementSource
加載的src文件必須是同源,不容許跨域web
下面步驟主要根據方法2。canvas
<audio src="1.mp3"></audio> <script> let audio = document.querySelector('audio'); let audioCtx = new (window.AudioContext || window.webkitAudioContext)(); audio.addEventListener('canplay', function () { let source = audioCtx.createMediaElementSource(audio); source.connect(audioCtx.destination); audio.play() }) </script> 複製代碼
let xhr = new XMLHttpRequest(); xhr.open('GET', '1.mp3', true); xhr.responseType = 'arraybuffer'; xhr.onload = function () { audioCtx.decodeAudioData(xhr.response, function (buffer) { getBufferSuccess(buffer) }) } 複製代碼
let input = document.querySelector('input'); input.addEventListener('change', function () { if (this.files.length !== 0) { let file = this.files[0]; let fr = new FileReader(); fr.onload = function () { let fileRet = e.target.result; audioCtx.decodeAudioData(fileRet, function (buffer) { getBufferSuccess(buffer); }, function (err) { console.log(err) }) } fr.readAsArrayBuffer(file); } }) 複製代碼
function getBufferSuccess(buffer) { // 建立頻率分析節點 let analyser = audioCtx.createAnalyser(); // 肯定頻域的快速傅里葉變換大小 analyser.fftSize = 2048; // 這個屬性可讓最後一個分析幀的數據隨時間使值之間的過渡更平滑。 analyser.smoothingTimeConstant = 0.6; // 建立播放對象節點 let source = audioCtx.createBufferSource(); // 填充音頻buffer數據 source.buffer = buffer; // 建立音量節點(若是你須要用調整音量大小的話) let gainNode = audioCtx.createGain(); // 鏈接節點對象 source.connect(gainNode); gainNode.connect(analyser); analyser.connect(audioCtx.destination); } 複製代碼
// 此方法須要補充節點的鏈接 let javascriptNode = audioCtx.createScriptProcessor(2048, 1, 1); javascriptNode.connect(audioCtx.destination); analyser.connect(javascriptNode); this.javascriptNode.onaudioprocess = function () { currData = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(currData); } 複製代碼
獲取AnalyserNode
節點裏的頻率長度frequencyBinCount
,實例化長度爲8位的整型數組,經過AnalyserNode.getByteFrequencyData
將節點中的頻率數據拷貝到數組中去,值的大小在0 - 256
之間,數值越高代表頻率越高;AnalyserNode.getByteTimeDomainData
原理同樣,不過獲取的是頻率大小,兩種方法根據需求選一種便可。api
function getData () { // analyser.frequencyBinCount 可視化值的數量,是前面fftSize的一半 let currData = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(currData); analyser.getByteTimeDomainData(currData); } 複製代碼
AudioBufferSourceNode.start(n)
n表示開始的時間,默認爲0,開始播放音頻 AudioBufferSourceNode.stop(n)
音頻在第n秒時間中止,若沒有傳值表示當即中止跨域
AudioContext.resume()
控制音頻的播放
AudioContext.suspend()
控制音頻的暫停
AudioContext.currentTime
獲取當前音頻播放時間
AudioBufferSourceNode.buffer.duration
獲取音頻的播放總時長
GainNode.gain.value
控制音量大小 [0, 1]
GainNode.gain.linearRampToValueAtTime
實現音量的漸入漸出數組
瞭解上面的api,就能夠來着手繪製啦~,你想繪啥就繪啥,頻繁的調用canvas的api很耗性能問題,這裏講下我在測試中提升性能的小技巧。
在切換歌曲中,遇到了這個報錯Failed to set the 'buffer' property on 'AudioBufferSourceNode': Cannot set buffer to non-null after it has been already been set to a non-null buffer at AudioContext
,大體是講AudioBufferSourceNode
的buffer屬性在以前我已經設置過了,不能被從新設置新的buffer值,因爲播放歌曲主要是經過其數組緩衝區ArrayBuffer來進行,可看看issue,解決辦法就是當須要切換歌曲狀況下,將當前的AudioBufferSourceNode
銷燬,從新建立上下文環境,音頻節點,鏈接等操做。
源碼在這,交互部分寫得有點亂,由於當時原來只是想練練可視化,以後想到啥功能就加,因此致使代碼看起來冗餘繁瑣,你們能夠參考看看audio
實現,主要在MusicPlay
對象。
小白第一次發表博文,發現寫博文比寫一個demo還要時間長,怕寫出來的東西有錯誤會誤導你們(有錯誤請你們評論指出~),因此會去查不少相關資料,這個過程也是學習的過程,之後會常常寫寫博文滴!最後,但願你們經過這篇文章也能學會本身作這種可視化的效果,配合一些可視化庫還能作出很酷炫的效果呢,一塊兒互相學習進步吧,加油!(。・д・。)