我有一首曲子要跟我一起演唱,并且能够静音并播放其他歌曲。所以我需要能够同时启动它们。现在,它们都开始有点不同步了:
// Start playing
for ( i = 0; i < 5; i++ ) {
tracks[i].audio.play();
}
即使这显然也不足以同时启动它们。
javascript中是否有任何方法可以保证HTML5音频标签会同时开始播放?
答案 0 :(得分:2)
不确定你是否已经这样做了,但这里有一些用于预加载音频的示例代码。
var audios = [];
var loading = 0;
AddNote("2C");
AddNote("2E");
AddNote("2G");
AddNote("3C");
function AddNote(name) {
loading++;
var audio = document.createElement("audio");
audio.loop = true;
audio.addEventListener("canplaythrough", function () {
loading--;
if (loading == 0) // All files are preloaded
StartPlayingAll();
}, false);
audio.src = "piano/" + name + ".mp3";
audios.push(audio);
}
function StartPlayingAll() {
for (var i = 0; i < audios.length; i++)
audios[i].play();
}
}
您可以尝试的另一件事是在每个曲目上设置audio.currentTime
以手动同步音频。
答案 1 :(得分:0)
您可以使用setTimeout在开头短暂延迟后同步它们(您可能希望等待加载所有音频对象)。
JSFiddle:http://jsfiddle.net/bmAYb/35/
var au1 = document.getElementById('au1');
var au2 = document.getElementById('au2');
au1.volume = 1;
au2.volume = 0; //mute
au1.play();
au2.play();
var notfirstRun = false;
//sync for the first time
setTimeout(function() {
au2.currentTime = au1.currentTime;
au2.volume = 1;
}, 250);
我最初的想法是使用setInterval每隔x毫秒进行一次同步,但如果volume
设置为1
(听得见),则会弹出音频。
我的小提琴不完全同步,但它非常接近。您可以100%同步,但您需要将其他音轨上的音频静音或处理弹出。
代码(以及小提琴中的音乐)来自Hungry Media。
答案 2 :(得分:0)
我遇到了同样的问题,并找到了使用Audio API的解决方案。
问题在于音频输出具有几毫秒的延迟,因此不可能同时启动多个音频。但是,您可以通过使用ChannelMergerNode将音频源合并为一个来解决此问题。通过在两者之间放置GainNode,您可以分别控制每个音频源的音量。
我为此编写了一个简单的javascript类。这是您可以使用的方式:
var audioMerger = new AudioMerger(["file1.ogg", "file2.mp3", "file3.mp3",
"file4.ogg", "file5.mp3"]);
audioMerger.onBuffered(() => audioMerger.play());
// Make sure it's always in sync (delay should be less than 50 ms)
setInterval(() => {
if (audioMerger.getDelay() >= 0.05) {
audioMerger.setTime(audioMerger.getTime());
}
}, 200);
// Set volume of 3rd audio to 50%
audioMerger.setVolume(0.5, 2);
// When you want to turn it off:
audioMerger.pause();
此代码在PC上的Firefox中将音频之间的延迟减少到不到10毫秒。延迟是如此之小,您不会注意到它。不幸的是,它不适用于Internet Explorer等较旧的浏览器。
这是该类的代码:
class AudioMerger {
constructor(files) {
this.files = files;
this.audios = files.map(file => new Audio(file));
var AudioContext = window.AudioContext || window.webkitAudioContext;
var ctx = new AudioContext();
this.merger = ctx.createChannelMerger(this.audios.length);
this.merger.connect(ctx.destination);
this.gains = this.audios.map(audio => {
var gain = ctx.createGain();
var source = ctx.createMediaElementSource(audio);
source.connect(gain);
gain.connect(this.merger);
return gain;
});
this.buffered = false;
var load = files.length;
this.audios.forEach(audio => {
audio.addEventListener("canplaythrough", () => {
load--;
if (load === 0) {
this.buffered = true;
if (this.bufferCallback != null) this.bufferCallback();
}
});
});
}
onBuffered(callback) {
if (this.buffered) callback();
else this.bufferCallback = callback;
}
play() {
this.audios.forEach(audio => audio.play());
}
pause() {
this.audios.forEach(audio => audio.pause());
}
getTime() {
return this.audios[0].currentTime;
}
setTime(time) {
this.audios.forEach(audio => audio.currentTime = time);
}
getDelay() {
var times = [];
for (var i = 0; i < this.audios.length; i++) {
times.push(this.audios[i].currentTime);
}
var minTime = Math.min.apply(Math, times);
var maxTime = Math.max.apply(Math, times);
return maxTime - minTime;
}
setVolume(volume, audioID) {
this.gains[audioID].gain.value = volume;
}
}