<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還要時間長,怕寫出來的東西有錯誤會誤導你們(有錯誤請你們評論指出~),因此會去查不少相關資料,這個過程也是學習的過程,之後會常常寫寫博文滴!最後,但願你們經過這篇文章也能學會本身作這種可視化的效果,配合一些可視化庫還能作出很酷炫的效果呢,一塊兒互相學習進步吧,加油!(。・д・。)