隨着5G時代的來臨,音視頻的領域的必將嫌棄新的浪潮,只有不斷的學習才能跟的上步伐,吧啦吧啦....
好吧前面真的編不下去了~~~,快速進入正題,這篇文章主要介紹的功能
1、選擇本地音頻文件 (多段)
2、選擇音頻區間播放 (多段)
3、合併audiobuffer
4、下載編輯後的音頻
整體例子功能比較單一,不過後面可以思考視頻是否可以沿用方式,拼接多段,可以肯定的應該能提取視頻的聲音,視頻畫面部分沒有嘗試,目前只測試了音頻功能正常; 注意這個是純前端的技術並不是把音頻文件上傳到服務端 用 ffmpeg方式進行裁切
demo早就做了,今天終於有時間整理下,同時也自己複習下,如果有錯誤的地方希望大神能指出來 。
主要的知識點:
- AudioContext 前面的文章已經講過了,
- decodeAudioData 把filedata 轉換爲 audiobuffer
- createBuffer 創建空的audiobuffer
- AudioBuffers 主要是編輯該數據
- getChannelData 獲取buffer的實際數據
- ChannerlData.set 設置buffer數據
先貼下體驗地址: http://works.ibeeger.com/learn/audioctx/merge.html
選擇多段,選擇區間試聽,完成後可以點擊下載,下載的音頻文件就是你所選擇的區間範圍。
當然還有很大的提升空間,這裏只作爲一個demo演示
還是看代碼把。
decode轉換
const decodecFile = function(fileContent, id) {
audioContext.decodeAudioData(fileContent, function(buffer) {
let index = id.replace(/choose/g,'');
buffers[index] = Object.assign({buffer: buffer},showBuffer(buffer, index));
});
}
可視化
function showBuffer(buffer, index) {
var cs = '';
var ctx = '';
var item = document.querySelectorAll('section>div')[index];
if(item.querySelector('canvas')){
cs = buffers[index]['cs']
ctx = buffers[index]['ctx'];
} else {
cs = document.createElement('canvas');
cs.width = window.innerWidth;
ctx = cs.getContext('2d');
}
ctx.clearRect(0,0, cs.width, cs.height);
const lth = buffer.getChannelData(1).length;
const arr = buffer.getChannelData(1);
let w = Math.floor(lth/cs.width/2);
ctx.fillStyle ='#efefef'
const list = []
for(let i =0; i<cs.width; i++) {
list.push(arr[i*w]*cs.height);
ctx.fillRect(i,(cs.height-arr[i*w]*cs.height)/2,1,arr[i*w]*cs.height);
};
ctx.save();
cs.dataset['index'] = index;
cs.addEventListener('mousedown', mousedown, false);
cs.addEventListener('touchstart', mousedown, false);
cs.addEventListener('mousemove', mousemove, false);
cs.addEventListener('touchmove', mousemove, false);
cs.addEventListener('mouseup', mouseup, false);
cs.addEventListener('touchend', mouseup, false);
document.querySelectorAll("section>div")[index].appendChild(cs);
return {
cs: cs,
ctx: ctx,
step: w,
list: list
}
}
播放指定的應的audiobuffer
function start(buffer) {
if (audioBufferSourceNode) {
audioBufferSourceNode.stop();
}
audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
audioBufferSourceNode = audioContext.createBufferSource();
audioBufferSourceNode.connect(analyser);
analyser.connect(audioContext.destination);
audioBufferSourceNode.buffer = buffer;
audioBufferSourceNode.start(0);
audioBufferSourceNode.onended = function() {
isplay = false;
cancelAnimationFrame(req);
};
}
最後一步下載,說實話 這個方法還沒有下來分析,只是從 google 找到了對應的方式
裏面應該也有很多參數值得去研究
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
setUint32(0x46464952); // "RIFF"
setUint32(length - 8); // file length - 8
setUint32(0x45564157); // "WAVE"
setUint32(0x20746d66); // "fmt " chunk
setUint32(16); // length = 16
setUint16(1); // PCM (uncompressed)
setUint16(numOfChan);
setUint32(abuffer.sampleRate);
setUint32(abuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
setUint16(numOfChan * 2); // block-align
setUint16(16); // 16-bit (hardcoded in this demo)
setUint32(0x61746164); // "data" - chunk
setUint32(length - pos - 4); // chunk length
// write interleaved data
for(i = 0; i < abuffer.numberOfChannels; i++)
channels.push(abuffer.getChannelData(i));
while(pos < length) {
for(i = 0; i < numOfChan; i++) { // interleave channels
sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767)|0; // scale to 16-bit signed int
view.setInt16(pos, sample, true); // write 16-bit sample
pos += 2;
}
offset++ // next source sample
}
// 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;
}
}
到此我覺得比較重要的幾點代碼已經貼出來了,目前下載下的音頻格式文件較大,應該跟我所說的參數設置有關係
大概一分鐘10MB的樣子。後面有時間再跟大家一塊聊,拜了個拜~~
(更正下 具體大小根據音頻的數據決定)