web-audio 实时更改两个音频源
例子
本示例说明了如何使用两个音频源,以及如何基于另一个来更改它们。在这种情况下,我们将创建一个音频Ducker,如果次要轨道产生声音,它将降低主要轨道的音量。
ScriptProcessorNode将常规事件发送到其audioprocess处理程序。在链接到辅助音频源的此处理程序中,我们计算音频的“响度”,并使用它来更改主要音频源上的动态压缩器。然后将两者都发送到用户的扬声器/耳机。当在辅助音频轨道中检测到声音时,结果是主要音频轨道中的音量非常突然变化。我们可以通过使用平均值,并使用延迟线在检测到次要音频之前更改音量来使此过程更加平滑,但是在此示例中,该过程应该很清楚。
//当次要来源产生声音时,DuckingNode将严重压缩主要来源
class DuckingNode {
constructor(context) {
let blocksize = 2048;
let normalThreshold = -50;
let duckThreshold = 0.15;
//创建节点
this.compressor= context.createDynamicsCompressor();
this.processor= context.createScriptProcessor(blocksize, 2, 2);
//配置节点
this.compressor.threshold.value = normalThreshold;
this.compressor.knee.value = 0;
this.compressor.ratio.value = 12;
this.compressor.reduction.value = -20;
this.compressor.attack.value = 0;
this.compressor.release.value = 1.5;
let self = this;
this.processor.onaudioprocess = function(audioProcessingEvent) {
let inputBuffer = audioProcessingEvent.inputBuffer;
let outputBuffer = audioProcessingEvent.outputBuffer;
let rms;
let total = 0.0;
let len = blocksize * outputBuffer.numberOfChannels;
for (let channel = 0; channel < outputBuffer.numberOfChannels; channel++) {
let inputData = inputBuffer.getChannelData(channel);
let outputData = outputBuffer.getChannelData(channel);
for (let sample = 0; sample < inputBuffer.length; sample++) {
//使输出等于输入
outputData[sample] = inputData[sample];
total += Math.abs(inputData[sample]);
}
}
//均方根
rms = Math.sqrt( total / len );
//我们将阈值至少设置为“normalThreshold”
self.compressor.threshold.value = normalThreshold + Math.min(rms - duckThreshold, 0) * 5 * normalThreshold;
}
}
get primaryInput () {
return this.compressor;
}
get secondaryInput () {
return this.processor;
}
connectPrimary(node) {
this.compressor.connect(node);
}
connectSecondary(node) {
this.processor.connect(node);
}
};let audioContext = new (window.AudioContext || window.webkitAudioContext)();
//Select two <audio> elements on the page. Ideally they have the autoplay attribute
let musicElement = document.getElementById("music");
let voiceoverElement = document.getElementById("voiceover");
//我们从它们创建源节点
let musicSourceNode = audioContext.createMediaElementSource(musicElement);
let voiceoverSourceNode = audioContext.createMediaElementSource(voiceoverElement);
let duckingNode = new DuckingNode(audioContext);
//连接一切
musicSourceNode.connect(duckingNode.primaryInput);
voiceoverSourceNode.connect(duckingNode.secondaryInput);
duckingNode.connectPrimary(audioContext.destination);
duckingNode.connectSecondary(audioContext.destination);该示例的一部分来自KevinEnnis的回答以及mdn上的代码。