不使用任何插件,前端原生js從音頻數據源上改動嘗試音頻倒放(未完成)

不使用任何插件,前端原生js從音頻數據源上改動嘗試音頻倒放

爲啥是嘗試——由於如今我也沒搞明白前端使用blob二進制儲存音頻究竟是個什麼解析規則,網上實在找不到參考資料,徹底是本身一點一點試出來的,下面說我是怎麼試的以及試出來的結果。javascript

界面

三個<audio>標籤,分別用來錄音,正放和倒放,兩個<button>分別用來開始錄音和結束錄音。html

lsf的小玩意兒

嘗試

首先我將二進制的blob轉爲了無符號Uint8Array數組,而後:前端

第一次嘗試

我天真的覺得把這個數組倒過來,而後再轉回Blob,而後就能夠播放了!java

結果固然是——門都沒有!!!web

第二次嘗試

我發現我把他截取了數組前面的一部分,依然能夠播放,可是單獨放後面的,不可。數組

獲得結論,這個數組前面有一段固定得長度用來表示這個是個音頻文件(還有單聲道呀,或者什麼什麼得,我也不太懂),而後我就對比了兩個錄音片斷,你們公共的長度是多少呢——91(90以前兩個音頻數組都是是同樣的),可是公共長度只是音頻頭(不循環的部分)的一部分,音頻頭是截止到160的(長度爲161)ide

問題一:至於91到97這一段爲啥不同?

答:我也不知道ui

問題二:那164以後也相同,爲何你認定這個就不是音頻頭了呢?

答:從161位開始的【163,65,X,129】,我發現這個東西在後面重復出現了,並且出現下一個這個的間隔等於X+259,那麼我就大膽推測——這個就是表明聲音的一個最小單位的開始。spa

lsf的小玩應
lsf的小玩應

注:

咱們倒置的最小單位是以【163,65,X,129.......】開頭的數組段(其中X是這段數組的長度),10個數組段播放時間是0.48s,可是16個數組段播放時間是從0.78s到0.9s不等,因此說並非一個代碼塊記錄了固定多少毫秒的時間(這不是我定義的,用官方方法把blob音頻轉爲數組而後他就這樣,我也沒什麼辦法)插件

第三次嘗試

而後我就開始,在保留代碼頭的狀況下,把從一個【163,65,X,129】到另外一個【163,65,X,129】之間的代碼做爲最小單位S,而後在保持最小單位S內的代碼順序不變,將全部最小單位的排序順序倒置了(從S_1,S_2,S_3,.....,S_n變成S_n,S_n-1,.....,S_1)

而後結果固然是——仍是不行。

而後我將每一個都倒放變成了把5個S看成一個總體,再倒放,成功了!我本身讀的123456變成了654321!可是那個音頻條不能拖動,一直是在最後一秒。以下圖這樣播完4s。

第四次嘗試

這我能忍麼!確定不能,音頻條不能拖動是個什麼玩應兒!而後我把數組的前一半倒置了,後一半不倒置,結果點擊播放那一刻,6秒的音頻是從3s開始的,持續了3s(一直顯示0:03),音頻正常播放到了6s(由於後三秒沒倒置),那麼我以爲,應該是有個東西記錄了時間。那麼咱們往下看,發現了一些規律(輸出的分別是,X,【163,65,X,129】中65的位置,後面接着的四位),直覺告訴我,這個遞增的兩位,就是記錄了時間!

lsf的小玩意

lsf的小玩應

而後我就循環將每一段的這兩位更改成遞增,播放時即是從零秒開始的,逐漸增長直到最後一秒,如今的代碼,有的能夠倒放,有的不行(8s的原音頻倒放後變成了4s或者5s反正就是變短了),緣由還未知,因此寫着未完成。。。

代碼

<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>Document</title>
 <script>
   let recorder
   function start() {
     let videoTarget = document.getElementById('audio')
     navigator.mediaDevices.getUserMedia({audio: true, video: false})
       .then((stream) => {
         recorder = new MediaRecorder(stream)
         videoTarget.srcObject = stream
         //s用來標記這是否找到了代碼頭
         let s = 0
         recorder.ondataavailable = (event) => {
           let blob = event.data
           let videoTarget2 = document.getElementById('audio2')
           let videoTarget3 = document.getElementById('audio3')
           let a = blob.stream().getReader()
           a.read().then(({ done, value }) => {
             let newValue = [] //用來存放倒置後的新數組
             let r = 0 //r表明指針記錄新數組插入元素的位置
             let b = 0 //b用來記錄這是第幾個S(S是咱們上面提到的最小音頻單位)
             for(i = 0; i < value.length; i++) {
               if(value[i] === 163){
               //s=1表示進入戒備狀態
                 s = 1
               } else if(s === 1 && value[i] === 65) {
               //在戒備狀態的條件下value[i] === 65說明的確是開始了新的一段最小單位的音頻
                 b === 5 ? (r = 162,b = 0) : b++
                 //這裏b===5是能夠改的,我定的是5個S做爲一個總體倒置,由於每一個S持續時間過短了,一個S一倒置放出來的音頻效果不好
                 //r=162是將指針移到162也就是出去音頻頭最先開始循環的位置,這樣咱們就完成了倒置,打完5個S就把指針移回最開始繼續打
               } else {
               //value[i] !== 65說明只是湊巧,並非找到了代碼頭,咱們再將s標示置爲0
                 s = 0
               }
               //在指針位置插入元素
               newValue.splice(r, 0, value[i])
               //指針後移
               r++
             }
             b = 0
             //咱們如今獲得的數組就是第三次嘗試時的到的數組,播放時一直顯示最後一秒
             newValue.splice(r, 0, 163)
             //刪掉最後一個元素是由於上面指針後移操做會使數組最後多一個163(戒備狀態那個判斷用到的163)
             newValue.pop()
             //而後咱們就要將音頻最小單位S的代碼頭後面兩位變成0 0, 0 60,0 120....
             for(i = 0; i < newValue.length; i++) {
               if(newValue[i] == 163){
                 s = 1
               } else if(s == 1 && newValue[i] == 65) {
               //獲得的規律是每次後面加60,可是若是超過了256就要進位,前面加1
                 newValue[i+3] = parseInt(b * 60 / 256)
                 newValue[i+4] = b * 60 % 256
                 b++
                 console.log(i, newValue[i+3], newValue[i+4])
               } else {
                 s = 0
               }
             }
             console.log(newValue)
             console.log(value)
             //而後再將新數組轉成blob賦給audio就可播放了
             newValue = new Uint8Array(newValue)
             let blob2 = new Blob([newValue], {type: 'audio/webm;codecs=opus'})
             videoTarget2.src = window.URL.createObjectURL(blob2)
             let blob3 = new Blob([value], {type: 'audio/webm;codecs=opus'})
             videoTarget3.src = window.URL.createObjectURL(blob3)
           })
         }
         recorder.start()
       });
   }

   function stop() {
     document.getElementById('audio').pause()
     recorder.stop()
   }
 </script>
</head>

<body>
 <audio id="audio" controls autoplay></audio>錄音(不用點它去點開始和結束)
 <audio id="audio2" controls autoplay></audio>倒放
 <audio id="audio3" controls></audio>正放
 <input onclick="start()" type="button" value="開始" />
 <input onclick="stop()" type="button" value="結束" />
</body>

</html>
複製代碼
相關文章
相關標籤/搜索