在Javascript中使用本地文件的Web Audio API

29

我正在尝试使用Web Audio API在iPhone游戏中实现声音效果。问题在于这个应用程序完全是客户端的,我想将我的mp3存储在本地文件夹中(而且不能由用户输入驱动),所以我无法使用XMLHttpRequest来读取数据。我在考虑使用FileSystem,但Safari不支持它。

是否有任何替代方案?

编辑:感谢下面的回复。不幸的是,Audio API对于游戏来说速度非常慢。我已经让它工作了,但延迟使用户体验无法接受。为了澄清,我需要像这样的东西 -

var request = new XMLHttpRequest();
request.open('GET', 'file:///./../sounds/beep-1.mp3', true);
request.responseType = 'arraybuffer';
request.onload = function() {
    context.decodeAudioData(request.response, function(buffer) {
    dogBarkingBuffer = buffer;
}, onError);
}
request.send();

但是这给了我以下的错误 -

XMLHttpRequest无法加载file:///sounds/beep-1.mp3。跨域请求仅支持HTTP。 Uncaught Error: NETWORK_ERR: XMLHttpRequest Exception 101

我理解读取本地文件存在安全风险,但在自己的域内应该没问题吧?


你可能会发现这个问题的答案很有用:https://dev59.com/82PVa4cB1Zd3GeqP8b3s - vittore
你考虑过本地存储吗? - Mark Meyer
不确定您上面所说的意思。我将音频文件存储在与我的JavaScript相同的域中。只是不知道如何访问它或是否允许? - user1904515
4个回答

43

我也遇到了同样的问题,但我找到了一个非常简单的解决方法。

audio_file.onchange = function(){
  var files = this.files;
  var file = URL.createObjectURL(files[0]); 
              audio_player.src = file; 
  audio_player.play();
};
<input id="audio_file" type="file" accept="audio/*" />
<audio id="audio_player" />

您可以在此处进行测试:http://jsfiddle.net/Tv8Cm/


3
太棒了。如此简单! - fralbo
注意:虽然这个方法可以实现,但是在较慢的设备上会有明显的延迟,因此可能不适合用于游戏声音,正如OP所提到的。 - Marcel

17

好的,我尝试了两天不同的方案来解决问题,最终找到了一种无需将资源存储在服务器上的方法。有几篇博客详细介绍了这个方法,但我没有找到完整的解决方案,所以我将其添加到这里。经验丰富的程序员可能认为这有些hacky,但这是我能想到的唯一方法,如果有更优雅的解决方案,我很乐意听取。

解决方法是将我��声音文件作为Base64编码字符串存储。声音文件相对较小(小于30kb),因此我希望性能不会太差。请注意,我在某些超链接前面放了“xxx”,因为我的n00b状态意味着我不能发布超过两个链接。

步骤1:创建Base 64声音字体

首先,我需要将我的mp3转换为Base64编码字符串,并将其存储为JSON。我在这里找到了一个网站,可以为我完成这个转换 - xxxhttp://www.mobilefish.com/services/base64/base64.php 您可能需要使用文本编辑器删除返回字符,但对于需要示例的任何人,我在这里找到了一些钢琴音调 - xxxhttps://raw.github.com/mudcube/MIDI.js/master/soundfont/acoustic_grand_piano-mp3.js 请注意,为了与我的示例一起使用,您需要删除标题部分data:audio / mpeg; base64,

步骤2:将声音字体解码为ArrayBuffer

您可以自己实现这个方法,但我找到了一个完美的API,可以做到这一点(为什么要重新发明轮子呢?) - https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js 资源来源-此处

步骤3:添加其余代码

相当简单

var cNote  = acoustic_grand_piano.C2;
var byteArray = Base64Binary.decodeArrayBuffer(cNote); 
var context = new webkitAudioContext();

context.decodeAudioData(byteArray, function(buffer) {
    var source = context.createBufferSource(); // creates a sound source
    source.buffer = buffer;    
    source.connect(context.destination); // connect the source to the context's destination (the speakers)
    source.noteOn(0); 
}, function(err) { console.log("err(decodeAudioData): "+err); });

就是这样!我已经在我的桌面版Chrome和移动版Safari上实现了它(当然,仅适用于iOS 6,因为Web Audio不支持旧版本)。在移动版Safari上加载需要几秒钟的时间(与桌面版Chrome的不到1秒相比),但这可能是因为它花费时间下载声音字体。也可能是因为iOS防止任何声音播放,直到出现用户交互事件。我需要更多的工作来研究它的表现。

希望这可以为其他人节省我所经历的痛苦。


非常有趣!感谢提供详细信息。这种方法能在另一个并发进程录制的音频文件上运行吗?而不是在JS浏览器/引擎中运行。 - alfonsodev

1
由于iOS应用程序被沙箱化,WebView(基本上是在PhoneGap中包装的Safari)允许您在本地存储MP3文件。即,没有“跨域”安全问题。
截至iOS6,以前的iOS版本不支持Web Audio API。

-2

使用HTML5音频标签在浏览器中播放音频文件。

Ajax请求使用http协议,因此当您尝试使用file://获取音频文件时,浏览器将此请求标记为跨域请求。请在请求头中设置以下代码 -

header('Access-Control-Allow-Origin: *');

刚刚在帖子中添加了一些细节(以及为什么我不能使用音频)。 - user1904515
1
谢谢回复。添加以上内容会导致错误:XMLHttpRequest无法加载file://beep-1.mp3/。Origin null不被Access-Control-Allow-Origin允许。 - user1904515
对此有什么其他想法吗?在WAMP上运行时这很好,但我想要完全离线(可能会使用PhoneGap,但我现在无法使用它,因为我在Windows上)。我几乎考虑通过将文件转换为类似Base64编码的字符串并存储它们来破解此步骤,但我不确定这是否有效?抱歉,我对这个音频东西完全是新手。 - user1904515
Access-Control-Allow-Origin是一个响应头。将其添加到请求中不会产生任何影响。请参阅https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS以了解其工作原理。 - sengi

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