随着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的样子。后面有时间再跟大家一块聊,拜了个拜~~
(更正下 具体大小根据音频的数据决定)