使用Web音频API播放简单音频

9

我一直在尝试跟随一些教程中的步骤,使用按钮和Web Audio API播放简单编码的本地WAV或MP3文件。我的代码如下(testAudioAPI.js):

window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
var myBuffer;

clickme = document.getElementById('clickme');
clickme.addEventListener('click',clickHandler);

var request = new XMLHttpRequest();

request.open('GET', 'WoodeBlock_SMan_B.wav', true);

request.responseType = 'arraybuffer';

// Decode asynchronously
request.onload = function() {
  context.decodeAudioData(request.response, function(theBuffer) {
    myBuffer = theBuffer;
  }, onError);
}
request.send();

function playSound(buffer) {
  var source = context.createBufferSource(), g = context.createGain();
  source.buffer = buffer;
  source.start(0);
  g.gain.value = 0.5;
  source.connect(g);
  g.connect(context.destination);
}

function clickHandler(e) {
    playSound(myBuffer);
}

HTML文件将如下所示:

    <!doctype html>
<html>
    <body>
        <button id="clickme">Play</button>
        <script src='testAudioAPI.js'></script>
    </body>
</html>

然而,完全没有实现任何声音。我尝试了几个片段,但仍然无法弄清楚。当我尝试通过创建振荡器节点合成声音时,确实可以得到声音,但使用来自本地文件的缓冲区时却不行。这里可能出了什么问题?谢谢大家。


请参考以下链接 https://dev59.com/-oPba4cB1Zd3GeqPzO-A ,了解可能的解释。 - Tom
你在控制台里面看到任何错误信息了吗? - Tom
请参见https://dev59.com/s4nda4cB1Zd3GeqPEdOd。 - Tom
4个回答

11

现代ES6的极简主义方法。

  1. 新建AudioContext();
  2. context.createBufferSource();
  3. source.buffer = audioBuffer; audioBuffer通过fetch请求ArrayBuffer数据,然后decodeAudioData将其解码为AudioBuffer。
  4. source.start()
<button id="start">playSound</button>

const audioPlay = async url => {
  const context = new AudioContext();
  const source = context.createBufferSource();
  const audioBuffer = await fetch(url)
    .then(res => res.arrayBuffer())
    .then(ArrayBuffer => context.decodeAudioData(ArrayBuffer));

  source.buffer = audioBuffer;
  source.connect(context.destination);
  source.start();
};

document.querySelector('#start').onclick = () => audioPlay('music/music.mp3');

停止播放:source.stop();

Web Audio API 不能自动播放,需要通过事件触发。

创建多个AudioContext对象会导致错误,应先注销再创建。

无法构造 'AudioContext':已达到最大硬件上下文数

const audioPlay = (() => {
  let context = null;
  return async url => {
    if (context) context.close();
    context = new AudioContext();
    const source = context.createBufferSource();
    source.buffer = await fetch(url)
      .then(res => res.arrayBuffer())
      .then(arrayBuffer => context.decodeAudioData(arrayBuffer));
    source.connect(context.destination);
    source.start();
  };
})();

document.querySelector('#start').onclick = () => audioPlay('music/music.mp3');

我最初使用了Howler库,但在iOS上特别容易出现错误。后来改用Web Audio API直接进行开发,在iOS、Android或Windows上都没有出现问题。 - rolznz

6
本地文件?你是直接从文件系统中获取它们(例如“file://”),还是有一个本地 Web 服务器在运行?
据我所知,直接从文件系统中提供服务会违反大多数(所有?)浏览器的CORS策略,这将阻止您的AJAX请求成功。
也许尝试使用 python -m SimpleHTTPServer 或类似的方式来提供页面,然后在 http://localhost:8000 上访问它。
就我所看到的,您的所有代码看起来都很好。

嗨,是的,我正在从文件系统中获取它们。但我会尝试服务页面...那将是唯一的方法来进行XMLHttpRequest,谢谢! - joobla

1
你有一个未定义的onError调用。
除此之外,它应该是可以工作的,但是对于Chrome而言是不行的,因为Chrome会阻止所有针对本地文件的XMLHttpRequest。但例如在Firefox中,只要定义(或者去掉)你的onError调用,它就能够正常工作。

有一个很好的Chrome插件可以允许所有跨域调用:https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en - Simple Simon
嗨,感谢您提供有关Chrome本地文件的提示。您知道使用JavaScript代码加载本地文件的其他方法吗?我刚刚删除了对“onError”的调用,但我担心它在Firefox上仍无法正常工作... - joobla
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Julien Grégoire
是的,@julien,我知道这一点。实际上,我的目标是用Web音频做比仅仅播放文件更复杂的事情,这就是为什么我从Web Audio API的简单事情开始的原因。再次感谢! - joobla
这并没有提供问题的答案。如果您想对作者进行批评或请求澄清,请在他们的帖子下留言。 - Sam Hanley
@sphanley 好的,我已经编辑了,我猜应该不该在回答中问问题,但原始代码中的一个问题是调用未定义的 onError 函数,所以它是回答的一部分。 - Julien Grégoire

0

您可以使用两行代码创建一个URL引用来播放本地文件:

const sound = new URL('../assets/sound.mp3', import.meta.url)
new Audio(sound.href).play()

href 只是为了满足 TypeScript 的需要,否则你可以省略该后缀。


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