给出了30s的简单网络视频:
<video src="my-video.mp4"></video>
我如何生成其音量水平图?
volume|
level| ******
| * * **
| * * * **
|** * *** *
| ** * * * *
+---------------*-*-----************------+--- time
0 30s
video is and quiet
loud here here
注意:
答案 0 :(得分:4)
根据用途,有几种方法可以做到这一点。
为了准确,您可以使用常规音量和单位进行测量,例如RMS,LUFS / LKFS(K加权,响度),dBFS(满量程dB)等等
简单朴素的方法是绘制波形的峰值。您只会对正值感兴趣。要获得峰值,您将检测两点之间的方向,并在方向从向上变为向下时记录第一个点(p0> p1)。
对于所有方法,您最终可以应用某种形式的平滑,例如加权移动平均(example)或通用平滑算法,以消除小峰值和变化,在RMS,dB等情况下,您将使用窗口大小,可与bin平滑(每段平均)结合使用。
要绘制图形,您将获得当前样本的值,假设它被标准化并将其绘制为线条或指向由绘图区域高度缩放的画布。
解决评论中的一些问题;这些只是在我的头脑中提供一些指示 -
由于Web Audio API无法自行进行流式处理,因此您必须将整个文件加载到内存中并将音轨解码为缓冲区。
* :总是可以选择将下载的视频存储为IndexedDB中的blob(含义),并使用带有该blob的Object-URL在视频元素中流式传输(可能要求MSE正常工作,我自己也没有尝试过。
流式传输时的绘图:
侧面加载低质量单声道纯音频文件:
服务器端绘图:
我可能会遗漏或错过一些观点,但它应该给出一般的想法......
此示例测量每个样本的给定窗口大小的常规dB。窗口大小越大,结果越平滑,但也需要更多时间来计算。
请注意,为简单起见,此示例中像素位置确定dB窗口范围。这可能会产生不均匀的间隙/重叠,这取决于影响当前样本值的缓冲区大小,但是应该用于此处演示的目的。同样为了简单起见,我将dB读数除以40,这里有点任意数字(ABS仅用于绘图和我的大脑工作方式(?)在深夜/清晨我做了这个:))
我在顶部添加了红色的bin / segment-smoothing,以更好地显示与自动调平等相关的长期音频变化。
我在这里使用音频源但你可以插入视频源,只要它包含可以解码的音轨格式(aac,mp3,ogg等)。
除此之外,这个例子只是一个例子。它不是生产代码,所以要把它当作它的价值。根据需要进行调整。
(出于某种原因,音频不会在Firefox v58beta中播放,但它会描绘。音频在Chrome中播放,FF58dev)。
var ctx = c.getContext("2d"), ref, audio;
var actx = new (AudioContext || webkitAudioContext)();
var url = "//dl.dropboxusercontent.com/s/a6s1qq4lnwj46uj/testaudiobyk3n_lo.mp3";
ctx.font = "20px sans-serif";
ctx.fillText("Loading and processing...", 10, 50);
ctx.fillStyle = "#001730";
// Load audio
fetch(url, {mode: "cors"})
.then(function(resp) {return resp.arrayBuffer()})
.then(actx.decodeAudioData.bind(actx))
.then(function(buffer) {
// Get data from channel 0 (you will want to measure all/avg.)
var channel = buffer.getChannelData(0);
// dB per window + Plot
var points = [0];
ctx.clearRect(0, 0, c.width, c.height);
ctx.moveTo(x, c.height);
for(var x = 1, i, v; x < c.width; x++) {
i = ((x / c.width) * channel.length)|0; // get index in buffer based on x
v = Math.abs(dB(channel, i, 8820)) / 40; // 200ms window, normalize
ctx.lineTo(x, c.height * v);
points.push(v);
}
ctx.fill();
// smooth using bins
var bins = 40; // segments
var range = (c.width / bins)|0;
var sum;
ctx.beginPath();
ctx.moveTo(0,c.height);
for(x = 0, v; x < points.length; x++) {
for(v = 0, i = 0; i < range; i++) {
v += points[x++];
}
sum = v / range;
ctx.lineTo(x - (range>>1), sum * c.height); //-r/2 to compensate visually
}
ctx.lineWidth = 2;
ctx.strokeStyle = "#c00";
ctx.stroke();
// for audio / progressbar only
c.style.backgroundImage = "url(" + c.toDataURL() + ")";
c.width = c.width;
ctx.fillStyle = "#c00";
audio = document.querySelector("audio");
audio.onplay = start;
audio.onended = stop;
audio.style.display = "block";
});
// calculates RMS per window and returns dB
function dB(buffer, pos, winSize) {
for(var rms, sum = 0, v, i = pos - winSize; i <= pos; i++) {
v = i < 0 ? 0 : buffer[i];
sum += v * v;
}
rms = Math.sqrt(sum / winSize); // corrected!
return 20 * Math.log10(rms);
}
// for progress bar (audio)
function start() {if (!ref) ref = requestAnimationFrame(progress)}
function stop() {cancelAnimationFrame(ref);ref=null}
function progress() {
var x = audio.currentTime / audio.duration * c.width;
ctx.clearRect(0,0,c.width,c.height);
ctx.fillRect(x-1,0,2,c.height);
ref = requestAnimationFrame(progress)
}
&#13;
body {background:#536375}
#c {border:1px solid;background:#7b8ca0}
&#13;
<canvas id=c width=640 height=300></canvas><br>
<audio style="display:none" src="//dl.dropboxusercontent.com/s/a6s1qq4lnwj46uj/testaudiobyk3n_lo.mp3" controls></audio>
&#13;