純前端技術裁切合並音頻方式實現

隨着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的樣子。後面有時間再跟大家一塊聊,拜了個拜~~
(更正下 具體大小根據音頻的數據決定)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章