如何在Javascript中去除音频轨道之间的暂停

8
我正在制作一款JavaScript游戏,希望基于声音片段创建背景音乐。我想将短的mp3文件播放为一个连续的曲目。我尝试在音频文件上绑定“ended”事件处理程序,但这会导致音频片段之间出现延迟。
为了解决这个问题,我做了一个笨拙的解决方案,但仍然无法正常工作,即在音频即将结束前1秒钟更改它。
Ebuc.manageAudio = function(){
    var listener = function (event) {
         if (this.currentTime > (this.duration - 1) && Ebuc.bgnext) {
             Ebuc.manageAudio();
             console.log("aduio");
             Ebuc.bgnext = false;
         }
         if(this.currentTime < 2){
            Ebuc.bgnext = true;
             console.log("reset");
         }
        console.log(event);
        console.log("listener active")

    };
    var color = Level.current.color;
    if(Ebuc.bgsong == null) {
        Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
        Ebuc.bgsong.addEventListener('timeupdate', listener, true);

    }
    else{
        Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
    }

    Ebuc.bgsong.play();
    Resources.audioSetList[color].next();

};

这个示例在一开始可以正常运行,但当要从第二个片段切换到第三个片段时,循环停止了。在记录事件监听器时,会有4次日志记录,然后停止。

Q1:为什么这个事件监听器突然消失了? Q2:是否有一种非hack的解决方案来链接这些音频片段。

提前感谢您的帮助。

3个回答

7

如果你需要在两个短音频片段之间快速切换,仅仅暂停无法解决问题,你还需要快速在两个音轨之间进行交叉淡入淡出来防止出现弹跳、杂音等问题。

以下是一个使用Howler实现的交叉淡入淡出的例子,参考自howler的Github问题页面。你可以使用这个例子,并保持已加载实例的队列以进行过渡。希望这能帮到你。

//you'll probably want this crossfade duration to be shorter.
var crossfadeDuration = 5000,
    volume            = 0.7;

var instance1, instance2, soundDuration;

// Singleton helper to build similar instances
var createHowlerInstance = function (urls, onload) {
  return new Howl({
    urls: urls,
    loop: false,
    volume: 0,
    onload: onload
  });
};

// Create "slave" instance. This instance is meant
// to be played after the first one is done.
instance2 = createHowlerInstance(['file2.mp3']);

// Create "master" instance. The onload function passed to
// the singleton creator will coordinate the crossfaded loop
instance1 = createHowlerInstance(['file1.mp3'], function(){

  // Get the sound duration in ms from the Howler engine
  soundDuration = Math.floor(instance1._duration * 1000);

  (function crossfadedLoop(enteringInstance, leavingInstance){

    // Fade in entering instance
    enteringInstance.pos(0).play().fade(0, volume, crossfadeDuration);

    // Wait for the audio end to fade out entering instance
    // white fading in leaving instance
    setTimeout(function(){

      enteringInstance.fade(volume, 0, crossfadeDuration);
      crossfadedLoop(leavingInstance, enteringInstance);

    }, soundDuration - crossfadeDuration);

  })(instance1, instance2);

});

谢谢您的回答,尽管它并没有回答我的问题。问题在于事件监听器中的延迟,简单地淡入淡出文件会打破这个模块化声音循环的目的。 - Martijn de Langh
1
这不是简单的“淡入淡出”,而是称为交叉淡入淡出,它是如何平滑连接两个不连续波形的音频片段的。在你的情况下,它会非常非常短,并且会在音频文件之间平滑过渡,避免你目前遇到的延迟问题。在这几毫秒内,两个文件将同时播放。这可能不能回答你的第一个问题,但绝对是一种非黑客方式来实现你所要求的效果。setTimeout(function(){...}, soundDuration - crossfadeDuration)就是解决你延迟问题的方法。 - pantalohnes
因此,当文件1淡出时,文件2正在淡入。 - pantalohnes

3

通过使用在pantalohnes的回答中设置timeOut的想法,我创建了以下代码来解决这个问题:

Ebuc.manageAudio = function(){
        var color = Level.current.color;
        Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
        Ebuc.bgsong.addEventListener("loadedmetadata",function(){
            setTimeout(Ebuc.manageAudio, (Ebuc.bgsong.duration * 1000) - 50);
            Ebuc.bgsong.play();
            console.log(Ebuc.bgsong.duration);
            Resources.audioSetList[color].next();
        });
    };

50毫秒的超时恰好弥合了有序文件之间的差距。

1

回答你的问题(虽然我看到你找到了另一个解决方案),我认为我找到了你的错误:

第二次进入Ebuc.manageAudio()时,Ebuc.bgsong已经设置,并且您只是创建了一个新的音频Ebuc.bgsong = new Audio(...),而没有将侦听器附加到它上面,因此在播放第二个音频文件时不会收到任何“timeupdate”事件的通知。

您还应该从先前的播放音频中删除侦听器。

所以,如果其他一切都好,我认为这应该可以解决问题:

Ebuc.manageAudio = function(){
    var listener = function (event) {
        if (this.currentTime > (this.duration - 1) && Ebuc.bgnext) {
            Ebuc.manageAudio();
            console.log("aduio");
            Ebuc.bgnext = false;
        }
        if(this.currentTime < 2){
            Ebuc.bgnext = true;
            console.log("reset");
        }
        console.log(event);
        console.log("listener active")

    };
    var color = Level.current.color;
    if(Ebuc.bgsong != null) {
        Ebuc.bgsong.removeEventListener('timeupdate', listener, true);
    }
    Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
    Ebuc.bgsong.addEventListener('timeupdate', listener, true);

    Ebuc.bgsong.play();
    Resources.audioSetList[color].next();

};

更重要的是,如果您正确地从先前播放的音频中移除监听器,则根本不需要使用bgnext hack。
var listener = function (event) {
    if (this.currentTime > (this.duration - 1)) {
        Ebuc.manageAudio();
        console.log("aduio");
    }
    console.log(event);
    console.log("listener active")
};

Ebuc.manageAudio = function () {
    var color = Level.current.color;
    if (Ebuc.bgsong != null) {
        Ebuc.bgsong.removeEventListener('timeupdate', listener, true);
    }
    Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
    Ebuc.bgsong.addEventListener('timeupdate', listener, true);

    Ebuc.bgsong.play();
    Resources.audioSetList[color].next();

};

如果那样可行,请告诉我 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接