如何获得(非常)短的 .mp3 音频文件以在每次击键时播放?

How can I get a (very) short .mp3 audio file to play on every keystroke?

我正在构建一个包含表单的网络应用程序,并且(作为渐进式增强/UX 效果)我想要一个按键来触发击键音效。

我把.mp3音效剪的很短(0.18s)。

然而,当我在我的笔记本电脑上测试我在 Firefox 上的设置时,似乎有一个声音延迟(并不是每个按键都会触发声音效果)。

在我的 Android 上的 Firefox Mobile 上,很少有按键触发音效 - 可能八分之一。

我是在尝试实现使用当前网络技术无法完成的事情,还是我只是以错误的方式接近它?

这是我的设置:

var myInput = document.getElementsByTagName('input')[0];
var keypress = document.getElementsByClassName('keypress')[0];

function playSoundEffect() {
    keypress.play();
}

myInput.addEventListener('keydown', playSoundEffect, false);

/* I've also tried...
myInput.addEventListener('keyup', playSoundEffect, false);
myInput.addEventListener('keypress', playSoundEffect, false);
*/
input {
width: 90vw;
}
<input type="text" placeholder="Type to hear the sound effect..." />

<audio class="keypress">
<source src="http://handsoffhri.org/.assets/theme/elements/mp3/keypress.mp3" type="audio/mpeg">
</audio>


第二次尝试:

感谢@StackingForHeap 和@zero298 下面的有用评论,我重构了我的设置:

var myInput = document.getElementsByTagName('input')[0];

function playSoundEffect() {
    var jsKeypress = new Audio('http://handsoffhri.org/.assets/theme/elements/mp3/keypress.mp3');
    jsKeypress.play();
}

myInput.addEventListener('input', playSoundEffect, false);
input {
width: 90vw;
}
<input type="text" placeholder="Type to hear the sound effect..." />

这绝对是一项改进 - 每次创建一个新的 Audio 对象允许每个对象独立于任何先前创建的 Audio 对象开始播放。

我尝试对 howler.js 库执行相同的过程,它似乎工作得很好。 这是 example

 var myInput = document.getElementsByTagName('input')[0];
  var sound = new Howl({
    src:['http://handsoffhri.org/.assets/theme/elements/mp3/keypress.mp3']
  });

  sound.play();


  function playSoundEffect() {
    sound.play();
   }

  myInput.addEventListener('keydown', playSoundEffect, false);

我看过你的第二次尝试,它看起来不错(请原谅双关语)。然而,我只是试了一下,我可以用这个得到很好的结果:

这使用了我在 Freesound.org: UI Confirmation Alert, C4.wav 上找到的音频文件。

这会创建一个 AudioContext 而不仅仅是 Audio 并一遍又一遍地使用相同的、已经加载的缓冲区。我仍在尝试查看是否有性能提升。

看来你必须一遍又一遍地制作源节点:

An AudioBufferSourceNode can only be played once; after each call to start(), you have to create a new node if you want to play the same sound again. Fortunately, these nodes are very inexpensive to create, and the actual AudioBuffers can be reused for multiple plays of the sound. Indeed, you can use these nodes in a "fire and forget" manner: create the node, call start() to begin playing the sound, and don't even bother to hold a reference to it. It will automatically be garbage-collected at an appropriate time, which won't be until sometime after the sound has finished playing.

/*jslint browser:true, esversion:6, devel:true*/
/*global AudioContext*/

(function() {
  "use strict";

  function main() {
    let ctx = new AudioContext();

    fetch("/res/audio/twinkle.wav")
      .then(res => res.arrayBuffer())
      .then((buffer) => {
        return new Promise((resolve, reject) => {
          ctx.decodeAudioData(buffer, (audioBuffer) => {
            resolve(audioBuffer);
          });
        });
      })
      .then((audioBuffer) => {
        document.addEventListener("keydown", (e) => {
          console.log(e.keyCode);
          if (e.keyCode === 37) { // The "left arrow" key
            let source = ctx.createBufferSource();
            source.buffer = audioBuffer;
            source.connect(ctx.destination);
            source.start(0);
          }
        });
      });
  }
  document.addEventListener("DOMContentLoaded", main);
}());