【第2019期】JS純前端實現audio音頻剪裁剪切複製播放與上傳

2021-02-15 前端早讀課

前言

還有這種技巧。今日早讀文章由@張鑫旭授權分享。

正文從這開始~~

背景是這樣的,用戶上傳音頻文件,可能只需要幾十秒就夠了,但是常規的音樂都要3~5分鐘,80%的流量都是不需要的,要是就這麼傳上去,其實是流量的浪費,如果可以在前端就進行剪裁,也就是只取前面一段時間的音頻,豈不是可以給公司省很多流量費用,前端的業務價值就體現了。

關鍵如何實現呢?

下面,就以「截取用戶上傳音頻前3秒內容」的需求示意下如何藉助Web Audio API實現音頻的部分複製與播放功能。

不嗶嗶,直接正題

實現步驟如下。

File對象轉ArrayBuffer

在Web網頁中,用戶選擇的文件是個file對象,我們可以將這個文件對象轉換成Blob、ArrayBuffer或者Base64。

在音頻處理這裡,都是使用ArrayBuffer這個數據類型。

代碼如下所示,假設file類型的文件選擇框的id是'file'。

file.onchange = function (event) {

var file = event.target.files[0];

// 開始識別

var reader = new FileReader();

reader.onload = function (event) {

var arrBuffer = event.target.result;

// arrBuffer就是包含音頻數據的ArrayBuffer對象

});

reader.readAsArrayBuffer(file);

};

使用的是readAsArrayBuffer()方法,無論是MP3格式、OGG格式還是WAV格式,都可以轉換成ArrayBuffer類型。

ArrayBuffer轉AudioBuffer

這裡的ArrayBuffer相對於把音頻文件數組化了,大家可以理解為把音頻文件分解成一段一段的,塞進了一個一個有地址的小屋子裡,在計算機領域稱為「緩衝區」,就是單詞Buffer的意思。

所謂音頻的剪裁,其實就是希望可以複製音頻前面一段時間的內容。

但是問題來了,ArrayBuffer裡面的數據並沒有分類,統一分解了,想要準確提取某一截音頻數據,提取不出來。

所以,才需要轉換成AudioBuffer,純粹的音頻數據,方便提取。

AudioBuffer是一個僅僅包含音頻數據的數據對象,是Web Audio API中的一個概念。

既然說到了Web Audio API,那我們就順便……順便……,想了想,還是不展開,因為太龐雜了,這Web Audio API至少比Web Animation API複雜了10倍,API之多,體量之大,世間罕見,想要完全吃透了,沒有三年五載,啃不下來。

如果大家不是想要立志成為音視頻處理專家,僅僅是臨時解決一點小毛小病的問題,則不必深入,否則腦坑疼,使用MDN文檔中的一些案例東拼西湊,基本的效果也能弄出來。

扯遠了,回到這裡。

AudioBuffer大家可以理解為音樂數據,那為什麼叫AudioBuffer,不叫AudioData呢?

因為Buffer是個專有名詞,直譯為緩衝區,大家可以理解為高速公路,AudioBuffer處理數據更快,而且還有很多延伸的API,就像是高速公路上的服務區,有吃有喝還有加油的地方。

AudioData一看名字就是鄉下土鱉,雖然接地氣,但是,處理好幾兆的數據的時候,就有些帶不動了,就好像騎小電驢,在公速公路和鄉道縣道沒多大區別,但是如果是開跑車,嘖嘖,鄉下路就帶不動了。

如何才能轉換成AudioBuffer呢?

使用AudioContext對象的decodeAudioData()方法,代碼如下:

var audioCtx = new AudioContext();

audioCtx.decodeAudioData(arrBuffer, function(audioBuffer) {

// audioBuffer就是AudioBuffer

});

複製AudioBuffer前3秒數據

AudioBuffer對象是一個音頻專用Buffer對象,包含很多音頻信息,包括:

音頻時長 duration

聲道數量 numberOfChannels

採樣率 sampleRate

等。

包括一些音頻聲道數據處理方法,例如:

獲取通道數據 getChannelData()

複製通道數據 copyFromChannel()

寫入通道數據 copyToChannel()

文檔見這裡:https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer

所以,實現的原理很簡單,創建一個空的AudioBuffer,複製現有的通道數據前3秒的數據,然後複製的內容寫入到這個空的AudioBuffer,於是我們就得到了一個剪裁後的音頻Buffer數據了。

代碼如下:

// 聲道數量和採樣率

var channels = audioBuffer.numberOfChannels;

var rate = audioBuffer.sampleRate;

// 截取前3秒

var startOffset = 0;

var endOffset = rate * 3;

// 3秒對應的幀數

var frameCount = endOffset - startOffset;

// 創建同樣採用率、同樣聲道數量,長度是前3秒的空的AudioBuffer

var newAudioBuffer = new AudioContext().createBuffer(channels, endOffset - startOffset, rate);

// 創建臨時的Array存放複製的buffer數據

var anotherArray = new Float32Array(frameCount);

// 聲道的數據的複製和寫入

var offset = 0;

for (var channel = 0; channel < channels; channel++) {

audioBuffer.copyFromChannel(anotherArray, channel, startOffset);

newAudioBuffer.copyToChannel(anotherArray, channel, offset);

}

// newAudioBuffer就是全新的複製的3秒長度的AudioBuffer對象

上面JavaScript代碼中的變量newAudioBuffer就是全新的複製的3秒長度的AudioBuffer對象。

使用newAudioBuffer做點什麼?

其實應該是有了AudioBuffer對象後我們可以做點什麼。

能做很多事情。

如果希望直接播放

我們可以直接把AudioBuffer的數據作為音頻數據進行播放

// 創建AudioBufferSourceNode對象

var source = audioCtx.createBufferSource();

// 設置AudioBufferSourceNode對象的buffer為複製的3秒AudioBuffer對象

source.buffer = newAudioBuffer;

// 這一句是必須的,表示結束,沒有這一句沒法播放,沒有聲音

// 這裡直接結束,實際上可以對結束做一些特效處理

source.connect(audioCtx.destination);

// 資源開始播放

source.start();

如果希望在 <audio>元素中播放

這個還挺麻煩的。

從 <audio>的src屬性獲取音頻資源,再進行處理是簡單的,網上的案例也很多。

但是,想要處理後的AudioBuffer再變成src讓 <audio>元素播放,嘿嘿,就沒那麼容易了。

我找了一圈,沒有看到Web Audio API中有專門的「逆轉錄」方法。

唯一可行的路數就是根據AudioBuffer數據,重新構建原始的音頻數據。研究了一番,轉成WAV格式相對容易,想要轉換成MP3格式比較麻煩,這裡有個項目:https://github.com/higuma/mp3-lame-encoder-js 不過自己沒驗證過,不過看代碼量,還挺驚人的。

因此,我們的目標還是轉到WAV音頻文件生成上吧,下面這段方法是從網上找的AudioBuffer轉WAV文件的方法,以Blob數據格式返回。

// Convert AudioBuffer to a Blob using WAVE representation

function bufferToWave(abuffer, len) {

var numOfChan = abuffer.numberOfChannels,

length = len * numOfChan * 2 + 44,

buffer = new ArrayBuffer(length),

view = new DataView(buffer),

channels = [], i, sample,

offset = 0,

pos = 0;

// write WAVE header

// "RIFF"

setUint32(0x46464952);

// file length - 8

setUint32(length - 8);

// "WAVE"

setUint32(0x45564157);

// "fmt " chunk

setUint32(0x20746d66);

// length = 16

setUint32(16);

// PCM (uncompressed)

setUint16(1);

setUint16(numOfChan);

setUint32(abuffer.sampleRate);

// avg. bytes/sec

setUint32(abuffer.sampleRate * 2 * numOfChan);

// block-align

setUint16(numOfChan * 2);

// 16-bit (hardcoded in this demo)

setUint16(16);

// "data" - chunk

setUint32(0x61746164);

// chunk length

setUint32(length - pos - 4);

// write interleaved data

for(i = 0; i < abuffer.numberOfChannels; i++)

channels.push(abuffer.getChannelData(i));

while(pos < length) {

// interleave channels

for(i = 0; i < numOfChan; i++) {

// clamp

sample = Math.max(-1, Math.min(1, channels[i][offset]));

// scale to 16-bit signed int

sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767)|0;

// write 16-bit sample

view.setInt16(pos, sample, true);

pos += 2;

}

// next source sample

offset++

}

// create Blob

return new Blob([buffer], {type: "audio/wav"});

function setUint16(data) {

view.setUint16(pos, data, true);

pos += 2;

}

function setUint32(data) {

view.setUint32(pos, data, true);

pos += 4;

}

}

WAV格式的兼容性還是很6的,如下圖所示:

凡事支持Web Audio API的瀏覽器都支持WAV格式,所以,技術上完全可行。

下面這段JS可以得到剪裁後的WAV音頻的Blob數據格式:

var blob = bufferToWave(newAudioBuffer, frameCount);

有了Blob數據,接下來事情就簡單了。

我們可以直接把Blob數據轉換成URL,可以使用URL.createObjectURL()生成一個Blob連結。

假設頁面上有如下HTML代碼:

<audio id="audio" controls=""></audio>

則如下設置,就可以點擊上面的

audio.src = URL.createObjectURL(blob);

如果要轉換成Base64地址,可以這麼處理:

var reader2 = new FileReader();

reader2.onload = function(event){

audio.src = event.target.result;

};

reader2.readAsDataURL(blob);

如果希望上傳剪裁的音頻

有了Blob數據,上傳還不是灑灑水的事情。

可以使用FormData進行傳輸,例如:

var formData = new FormData();

formData.append('audio', blob);

// 請求走起

var xhr = new XMLHttpRequest();

xhr.open('POST', this.cgiGetImg, true);

// 請求成功

xhr.onload = function () {

};

// 發送數據

xhr.send(formData);

有demo可以進行效果體驗的,您可以狠狠地點擊這裡:用戶上傳的MP3音頻剪裁併播放demohttps://www.zhangxinxu.com/study/202007/upload-audio-clip-play-demo.php

使用截圖示意如下:

關於本文作者:@張鑫旭原文:https://www.zhangxinxu.com/wordpress/2020/07/js-audio-clip-copy-upload/

為你推薦

【第1829期】複製黏貼上傳圖片和跨瀏覽器自動化測試

【第2009期】實現 Bilibili 視頻播放Chrome 媒體控制效果

歡迎自薦投稿,前端早讀課等你來

相關焦點

  • Web Audio API介紹和web音頻應用案例分析
    本篇文章首先介紹了Web Audio API相關概念、常用的幾個接口節點,以便不熟悉Web Audio的開發人員有個了解。後面主要分析了3個Web Audio API的應用案例,web音頻錄音與實時回放、web音頻剪切、web實現在線k歌,通過應用案例加深對Web Audio API的了解。讀者也可以根據案例開拓思維,做出更好玩的web音頻應用。
  • 音視頻開發之旅(35) -FFmpeg + AudioTrack 實現音頻解碼和播放
    這篇我們來實現下音頻的解碼器。解碼流程和視頻的基本一致。FFmpeg解碼的音頻裸數據是PCM格式,android上播放PCM音頻數據可以通過AudioTrack和OpenSL ES來實現。下面我們下來看下解碼的流程一、音頻解碼流程和上一篇的視頻解碼流程基本一致。
  • 84.AudioManager 音頻播放功能
    我們呢就先馬上實現一個音頻播放的邏輯,先準備一個音效,放在我們的 Resources 目錄下,如下圖所示。006tNc79gy1fzgf4x6dr8j306c01ya9y.jpg然後直接實現我們的播放音頻邏輯,代碼如下:QFramework/Example/13.AudioManager/AudioExample.csusing UnityEngine;namespace QFramework{    public class AudioExample
  • ffmpeg解碼音頻,使用AudioQueue 播放
    播放📗 第七節 - 音視頻同步📗 第八節 - 完善播放控制📗 第九節 - 倍速播放📗 第十節 - 增加視頻過濾效果📗 第十一節 - 音頻變聲該節 Demo 地址: https://github.com/czqasngit/ffmpeg-player/releases
  • 功能強大的純前端 Excel 在線表格: Luckysheet
    簡介Luckysheet是一款類似excel的純前端在線表格,只需要引入js和css文件即可使用。Luckysheet保持了和excel一致的操作體驗,樣式、快捷鍵、函數等,都與excel的操作方式一致,用戶沒有額外的學習成本。Luckysheet目前還在持續的迭代演進中,已經實現的功能包括:1.格式設置。
  • Soundop Audio Editor 音頻編輯器 軟體
    Soundop Audio Editor 音頻編輯器 Soundop 是一款適用於 Windows 的綜合音頻編輯器,可在一個應用程式中無縫地進行音頻編輯和多軌混音
  • Android音頻播放AudioTrack詳解
    Android 中常用的播放音頻的接口有MediaPlayer、AudioTrack和SoundPool,音頻的渲染最常用的是AudioTrack和OpenSL ES ,下面將介紹下AudioTrack相關知識,主要內容如下:AudioTrack介紹AudioTrack用來點播放原始
  • 音頻編輯軟體
    最多可以混合128個聲道,可編輯單個音頻文件。總而言之,對專業人員來說,這款應用是比較好用的。MP3剪切合併大師 12.4剪切合併大師是一款界面大方簡潔,操作簡便的MP3剪切軟體。擁有這款軟體,您可以方便的將MP3類型的音頻和視頻剪接成各種片段。對於非專業人員,或者對視頻功能的要求不是那麼全面的朋友可以嘗試使用這款軟體。
  • 音頻應用編輯處理軟體
    不少人把它形容為音頻「繪畫」程序。你可以用聲音來「繪」制:音調、歌曲的一部分、聲音、弦樂、顫音、噪音或是調整靜音。而且它還提供有多種特效為你的作品增色:放大、降低噪音、壓縮、擴展、回聲、失真、延遲等。你可以同時處理多個文件,輕鬆地在幾個文件中進行剪切、粘貼、合併、重疊聲音操作。使用它可以生成的聲音有:噪音、低音、靜音、電話信號等。該軟體還包含有CD播放器。
  • 音視頻開發之旅(三)AudioTrack播放PCM音頻
    可以將PCM音頻數據傳輸到音頻接收器,以供播放,只能播放源碼流即PCM,wav封裝格式的音頻也可以用AudioTrack播放,但是wav頭部分在播放解析時會發出噪音。而MediaPlayer可以播放多種格式的音頻文件,比如 mp3 aac等,因為MediaPlayer會在framework層創建對應的音頻解碼器。
  • iOS 錄音、音頻的拼接剪切以及邊錄邊壓縮轉碼
    總體內容1、錄音實現2、錄音的編輯 (拼接音頻:可以設置多段,音頻的剪切:按照時間段剪切
  • 如何使用 MediaStream API 錄製音頻!
    然後,您可以在錄製後直接播放這些曲目,也可以將錄製的媒體上傳到您的伺服器。在本教程中,我們將創建一個網站,該網站將使用 Media Streams API 來允許用戶錄製某些內容,然後將錄製的音頻上傳到伺服器進行保存。用戶還可以查看和播放所有上傳的錄音。
  • ​Web前端剪切板文本分享到文件發送
    實現一個「複製」按鈕,方便內容分享傳播在網頁上一段文本後添加一個「複製」按鈕並不是啥新鮮的事情,比如「複製分享網址」、「幫我砍一刀」...,早先FLASH還紅火的時候,也有很多是基於FLASH實現的。第1步,監聽document或目標元素的copy事件就可以。第2步,要獲取用戶選中的內容,需要用到另外一個能力 Selection API[5] ;下面示例代碼中考慮兼容性實現了一個工具函數 getSelectionTxt。
  • Mp3directcut( MP3音頻剪切工具)中文版分享
    Mp3directcut是一款功能強大的MP3音頻剪切工具,可以將你喜愛的歌曲剪切成鈴聲,用來做成超炫個性的鈴聲。
  • 【第2009期】實現 Bilibili 視頻播放Chrome 媒體控制效果
    尤其最新的樣式實現非常酷炫,加入對封面的支持,這樣整體效果看著非常棒。很早之前我們寫過一篇關於 使用 mediaSession 實現媒體播放的通知欄控制 這裡介紹了我們如何在網頁播放音頻的時候鎖屏控制項如何顯示當前播放音樂的封面和簡介,或者控制上一曲下去。
  • Android Audio音頻系統
    的成員變量mAudioHwDevs中十一、audio_policy.conf同時定義了多個audio接口十二、通過AudioFlinger的loadHwModule加載各audio接口對應的庫文件實現調用PlaybackThread播放線程或RecordThread錄音線程十三、AudioFlinger的openInput()方法調用流程分析十四
  • Android 音視頻開發-- 使用AudioRecord 錄製PCM(錄音),AudioTrack播放音頻
    使用 AudioRecord 錄音上面了解音頻的基礎知識後,我們接著使用 AudioRecord 來錄製原始數據,即 PCM 數據;當手機的硬體錄音之後,AudioRecord 可以從該硬體提取音頻資源;讀取的方法可以使用 read()方法來實現。
  • 【第2016期】WXInlinePlayer:高性能WASM播放器實現
    整體邏輯實際上現在基於WASM的播放器在架構實現上基本上都趨同,主要分為幾層:I/O層:通過XHR或者WebSocket進行音視頻數據的獲取解碼層:解碼獲取YUV圖像數據及PCM音頻數據音畫同步及緩衝區層:通過音視頻時間戳進行對齊及緩衝部分播放數據繪製層:通過Canvas或WebGL進行繪製音頻播放層:通過AudioContext
  • iOS Audio 手把手: 錄音、播放、音頻播放控制(音量採樣檢測等),Swift5,基於 AVFoundation
    iOS 設備中,每一個應用 app,都有一個音頻會話 Audio Session.app 調用音頻相關,自然會用到 iOS 的硬體功能。音頻會話 Audio Session ,就是來管理音頻操作的。iOS 使用音頻,管理粒度很細你覺得:後臺播放的音樂,要不要與你 app 的音頻,混雜在一起?
  • 音頻編輯軟體Goldwave如何使用?附GoldWave綠色漢化版
    ,或者按下快捷鍵F11,可以打開控制器屬性設置  在這裡,我們可以調整播放屬性、錄音屬性、音量、視覺的屬性,以及音效卡設備  比如第1項,播放設置,可以定義這個按鈕播放全部波形、選定部分的波形、未選部分的波形、在窗口中顯示出來的部分波形等等  同時,你還可以調整快進和倒帶的速度。