如何在 javascript 中快速播放声音文件的多个副本

Posted

技术标签:

【中文标题】如何在 javascript 中快速播放声音文件的多个副本【英文标题】:How to rapidly play multiple copies of a soundfile in javascript 【发布时间】:2020-04-27 07:32:49 【问题描述】:

我正在用 html+js 构建一个旋转得相当快的命运之轮。每次新的颜色飞过标记时,滚轮都会发出咔哒声。在最高速度下,这听起来几乎像机关枪,所以一个新文件在旧文件基本上完成之前就开始播放。文件本身总是一样的:click.wav

它在 Chrome 中运行良好,仅在 chrome 中。 Firefox 有一个奇怪的错误,它只播放声音,如果有任何其他音频源处于活动状态,例如在不同选项卡中播放的 youtube 视频。 Edge 和 Safari 可以确保点击到最后,然后同时播放它们。一团糟... 我使用method described here,它使用克隆<audio>标签

我想这就是问题所在:

var sound = new Audio("sounds/click.wav");
sound.preload = 'auto';
sound.load();

function playsound()
    var click=sound.cloneNode();
    click.volume=1;
    click.play();

这是我的旋转函数的简化版本,它每秒只调用 playsound() 函数几次:

function rotateWheel()
  angle = angle + acceleration
  while (angle >= 360) 
    angle = angle - 360
   
  var wheel = document.getElementById("wheel")
  wheel.style.transform = "rotate("+angle +"deg)"
  // play the click when a new segment rotates by
  if(Math.floor(angle/21) != previousSegment)
     playsound()
     previousSegment = Math.floor(angle/21)

【问题讨论】:

【参考方案1】:

您使用了answer from here 这种方法有时会导致浏览器进程崩溃,因为您要么创建内存问题,要么用浏览器必须处理的元素填充 DOM - 所以您应该重新考虑您的方法并且正如您发现的那样,它不适用于大多数浏览器(如 safari 或 FireFox)中的大量使用 深入研究<audio> 标签规范,很明显有很多事情无法用它来完成,这并不奇怪,因为它是为媒体播放而设计的。 限制之一包括 -> 没有精细的声音时序。

因此,您必须找到另一种方法来满足您的需求,我们使用专为在线视频游戏设计的 Web Audio API。Web Audio API AudioContext 用于管理和播放所有声音。要使用 Web Audio API 产生声音,请创建一个或多个声音源并将它们连接到 AudioContext 实例(通常是扬声器)提供的声音目标。 音频缓冲区 使用 Web Audio API,音频文件只有在加载到缓冲区后才能播放。加载声音需要时间,因此动画/游戏中使用的资产应在页面加载时、游戏或关卡开始时加载,或者在玩家玩游戏时逐步加载。基本步骤强>

我们使用 XMLHttpRequest 将数据从音频文件加载到缓冲区中。 接下来,我们进行异步回调并发送实际的加载请求。 一旦声音被缓冲和解码,就可以立即触发。 每次触发时,都会创建不同的缓冲声音实例。

游戏中音效的一个关键特性是可以同时存在许多音效。 因此,以“机枪”为例:想象一下,您正处于枪战中,正在使用射击机枪。 机枪每秒发射多次,造成数十种音效同时播放。这就是 Web Audio API 真正大放异彩的地方。 您的应用程序的一个简单示例:

/* global AudioContext:true,
*/

var clickingBuffer = null;
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();

function loadClickSound(url) 
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    // Decode asynchronously
    request.onload = function() 
        context.decodeAudioData(request.response, function(buffer) 
            if (!buffer) 
                console.log('Error decoding file data: ' + url);
                return;
            
        clickingBuffer = buffer;
        );
    request.onerror = function() 
        console.log('BufferLoader: XHR error');        
        ;
    request.send();
    ;


function playSound(buffer, time, volume)               
  var source = context.createBufferSource();   // creates a sound source
  source.buffer = buffer;                     // tell the source which sound to play
  source.connect(context.destination);          // connect the source to the context's destination (the speakers)
  var gainNode = context.createGain();          // Create a gain node
  source.connect(gainNode);                     // Connect the source to the gain node
  gainNode.connect(context.destination);        // Connect the gain node to the destination
  gainNode.gain.value = volume;                  // Set the volume
  source.start(time);                           // play the source at the deisred time 0=now    


// You call with in your document ready
   loadClickSound('sounds/click.wav');
//and this plays the sound
   playSound(clickingBuffer, 0, 1);

现在您可以尝试不同的时间和音量变化,例如通过引入随机因素 如果您需要具有不同点击声音(存储在缓冲区数组中)和音量/距离变化的更复杂的解决方案,这将是一段较长的代码。

【讨论】:

太棒了!非常感谢您提供如此详细的描述和答案。这正是我所需要的。 知道如何规避这个问题吗?我的意思是实际上有一个用户交互调用了所有这些,上面的一些函数......我试图在几个地方添加 context.resume() ,但仍然收到这条消息:The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page. [link](https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio) 很难调试我从未见过的代码 -> 默认情况下,Web Audio API 当前不受自动播放策略的影响。 (来自链接)上下文是全局定义的吗?如果问题仍然存在,请使用相关代码发布新问题并通过评论通知我这里 嗨@Codebreaker007,我按照你的建议创建了一个jsfiddle和单独的问题,以隔离问题:***.com/questions/61608637/…。如果你能去看看就好了。

以上是关于如何在 javascript 中快速播放声音文件的多个副本的主要内容,如果未能解决你的问题,请参考以下文章

在javascript中同时播放多个声音

使用 Javascript 在 iPhone 网络应用程序中播放声音?

如何在 JavaScript 中播放声音

如何播放每个 X 声音的音频?在javascript中

在javascript函数中使用嵌入[重复]

iphone快速加载声音文件