本身動手封裝AudioContext,可作播放器還能夠解析音頻

前言

使用AudioContext有下面幾個好處vue

  • 無需額外引入audio標籤git

  • 跟隨系統的狀態,即手機調成震動/靜音模式了,這個聲音也就不要出了github

  • 能夠音頻解析,作特效,好比下面,一個是用canvas手寫的,一個是echart作的ajax


Media實現對AudioContext的封裝,使用以下canvas

使用方法

1. 引入media

<script src="media.js"></script>複製代碼

2. 參數

引入後會在window會掛載一個Media,接受兩個參數(source, options)

souce:能夠傳出一個url或者一個ArrayBufferbash

options異步

  • loop:boolean類型,是否循環播放oop

  • volume:0~1,控制音量ui

  • analyser:是否開啓音頻分析,使用默認設置能夠設爲true;也能夠是一個對象,size用於配置fftSize,默認1024this

let media = new Media(source, {
  loop: true,
  volume: 0.6,
  analyser: {
    size: 512
  }
})複製代碼

3. 事件

onload:音頻加載完成,邏輯下載此事件中

onended:音頻播放完觸發

4. 方法

  • getData 獲取分析的音頻數據,類型Uint8Array,須要開啓analyser選項

  • play 播放音頻

  • suspend 暫停播放

  • start(offset) 設置音頻開始播放的時刻,offset的範圍爲0~duration

  • setLoop(bool) 設置音頻是否循環播放

  • setVolume(val) 設置音頻音量,0 ~ 1.0

  • getCurrentTime 獲取當前播放的時長

  • setOptions(options) 能夠統一設置,如:{ loop: true, volume: 0.5 }

5. 屬性

    • duration 獲取音頻總時長

    • state 獲取當前音頻的狀態,running | suspend

    • volume 獲取當前音量

    • loop 獲取音頻是否循環

踩的坑

下面說一下封裝過程遇到的問題和解決辦法

1. 獲取當前時間

AudioContext實例上是有個currentTime屬性,可是這個屬性真的很差用,音頻加載完成它就開始更新,不受音頻開關的控制,更像是一個age屬性,

解決辦法,聲明一個delta用來保存這之間的差值,每次start需更新這個值,獲取小心音頻時刻時就相減,固然了,最大值不超過duration。

start(offset) {
  this.delta = this.ctx.currentTime - offset
  // ...}
getCurrentTime() {
   return Math.min(this.ctx.currentTime - this.delta, this.duration)
}複製代碼

2. 實現滑動條控制音頻播放

AudioContext裏面有個start(offset)方法可讓音頻從哪一個時刻後開始播放,可是這個方法只能使用一次,再次調用會報錯,所以若是要實現用滑動條控制音頻播放,必須處理start(offset)的問題。解決辦法:只能從新生成source,在這以前要先調用stop,否則就會多首音頻同時播放。

this.source.stop()
initBufferSource(decodedData) {  this.source = this.ctx.createBufferSource()  this.decodedData = decodedData  this.source.buffer = this.decodedData}複製代碼

3. onended事件

本來想註冊一個onended事件方便作別的操做,可是每次滑動滾動條,都會觸發這個事件,而後音頻並無播放完啊。找了很久發現是每次執行stop就會調用,所以在從新生成source前要解綁這個事件,而後從新綁定

this.source.onended = null   
this.onended && (this.source.onended = this.onended)複製代碼

4. 處理異步是個問題

裏面有這樣幾個異步,若是開始傳來的是url,會先調用request,也就是ajax請求音頻資源。

還有一個是decodeAudioData,AudioContext把ArrayBuffer解碼也是一個異步,好在新的API返回的是一個Promise對象。

問題是隻有解碼完成才能夠操做音頻,想了很久不知道怎麼解決暫時註冊一個onload事件,操做音頻請在這個事件裏處理,後續可能會改爲返回一個Promise對象

大致是這樣子的,特別小的細節就不說了,github有個vue的案例,你們能夠看一下

代碼

github地址

若是您有什麼建議、意見能夠留言

相關文章
相關標籤/搜索