环境: Windows 10和MacOS High Seirra Chrome和Safari
用例: 我们想一次播放一个N个音频文件。无缝地循环给定音频文件,直到用户通过UI交互切换另一个音轨。目前我们在轨道之间使用线性淡入淡出。因为用户可以在轨道将循环的哪个点进行改变,所以我们需要能够通过环路市场检测轨道位置并在轨道上实时转换。我们安排淡出当前增益节点,并在我们到达循环结束后立即淡入同一轨道的新增益节点。
预期成果: 使用110hz余弦信号音频文件,我们在源的播放之间循环无限衰落。给定相同频率的两个余弦波形之间的完美对齐的过渡,我们预期在轨道之间的过渡过程中没有幅度变化。在这种情况下,相同持续时间和起始时间的两个线性斜坡应完全相互抵消。
观察结果: 即使我们将第二个源的开始时间设置为与第一个源的淡出开始时相同,我们最终会在播放时出现随机幅度下降。偶尔播放将对齐,我们将看到对齐波形的预期结果。
可能导致此行为的原因是什么? context.currentTime是否不可靠,以便根据?
计划事件此代码是一个卑鄙的版本,但复制了该问题:
function Crossfade() {
console.log('crossfade function');
var request = new XMLHttpRequest();
request.open('GET', 'http://localhost:8080/cosine_110hz.wav', true);
request.responseType = 'arraybuffer';
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
console.log("track received.");
let trackStart = context.currentTime + .1;
let fadeDuration = .25;
let fadeOutStart = trackStart;
var gainNode2 = context.createGain();
var source2 = context.createBufferSource();
source2.buffer = buffer;
source2.connect(gainNode2);
gainNode2.connect(context.destination);
gainNode.gain.setValueAtTime(1, trackStart);
gainNode.gain.linearRampToValueAtTime(0, trackStart + fadeDuration);
source.stop(trackStart + fadeDuration);
gainNode2.gain.setValueAtTime(0, trackStart);
gainNode2.gain.linearRampToValueAtTime(1, trackStart + fadeDuration);
source2.start(trackStart, 0);
gainNode = gainNode2;
source = source2;
setTimeout(Crossfade, 1000);
})
}
request.send();
}
答案 0 :(得分:0)
您可以使用.setValueCurveAtTime()
方法而不是.linearRampToValueAtTime()
来实现等功率交叉淡入淡出。您可以在下面使用这些方法来创建所需的曲线并根据需要对其进行调整:
const curve = new Float32Array(64);
function makeEqualPowerCurveIn(startVolume, endVolume) {
var range = endVolume - startVolume;
curve[0] = startVolume;
for (var i = 1; i < curve.length; i++)
curve[i] = startVolume + range * Math.cos((1 - i / (curve.length-1)) * 0.5*Math.PI);
return curve;
}
function makeEqualPowerCurveOut(startVolume, endVolume) {
var range = startVolume - endVolume;
for (var i = 0; i < curve.length; i++)
curve[i] = endVolume + range * Math.cos(i / (curve.length-1) * 0.5*Math.PI);
curve[curve.length - 1] = endVolume;
return curve;
}