【问题标题】:How to play an audio file multiple times如何多次播放音频文件
【发布时间】:2014-11-16 14:51:45
【问题描述】:

我想在游戏中发生碰撞时发出碰撞声音。为此,我如何多次运行音频文件?

下面是一个例子,音乐一直在播放:Example。但是if条件下如何多次播放小mp3文件呢?

【问题讨论】:

    标签: javascript audio


    【解决方案1】:

    这实际上是一个很大的问题。我建议使用图书馆。

    Web Audio API 可以说是在浏览器中播放音频的最佳方式。 Here's a good article on how to use it。但不幸的是,它并非在每个浏览器上都可用,这意味着您需要某种回退。它在 Chrome、Safari 和 Firefox 中受支持。 IE 支持即将到来,但还不存在。

    这是一个图书馆

    (function(global) {
      var webAudioAPI = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
    
      // To play a sound, simply call audio.playSound(id), where id is
      // one of the keys of the g_sound_files array, e.g. "damage".
    
      // options:
      //   startedOnTouchCallback: on iOS no sounds can be played unless at least one is first initiated during
      //       a use gesture. If a function is attached here it will be called when that user gesture has happened.
      //       This is useful for situtations where sounds 'should' start right from the beginning
      //       even if the player as not touched the screen. In that case we put up a message, "touch the screen"
      //       and remove that message when we get this callback
      //
      //   callback: called when all the sounds have loaded.
      var AudioManager = function(sounds, options) {
        options = options || {};
        var g_context;
        var g_audioMgr;
        var g_soundBank = {};
        var g_canPlay = false;
        var g_canPlayOgg;
        var g_canPlayMp3;
        var g_canPlayWav;
        var g_canPlayAif;
        var g_createFromFileFn;
    
        var changeExt = function(filename, ext) {
          return filename.substring(0, filename.length - 3) + ext;
        };
    
        this.needUserGesture = (function() {
          var iOS = ( navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false );
          var needUserGesture = iOS;
          return function() {
            return needUserGesture;
          };
        }());
    
        var WebAudioBuffer = function() {
        };
    
        WebAudioBuffer.prototype.play = function(opt_when, opt_loop) {
          if (!this.buffer) {
            console.log(this.name, " not loaded");
            return;
          }
          var src = g_context.createBufferSource();
          src.buffer = this.buffer;
          src.loop = opt_loop || false;
          src.connect(g_context.destination);
          if (src.start) {
            src.start(opt_when);
          } else {
            src.noteOn(opt_when);
          }
          return src;
        };
    
        function WebAudioSound(name, filename, samples, opt_callback) {
          this.name = name;
          var that = this;
          var req = new XMLHttpRequest();
          req.open("GET", filename, true);
          req.responseType = "arraybuffer";
          req.onload = function() {
            g_context.decodeAudioData(req.response, function onSuccess(decodedBuffer) {
              // Decoding was successful, do something useful with the audio buffer
              that.buffer = decodedBuffer;
              if (opt_callback) {
                opt_callback(false);
              }
            }, function onFailure() {
               console.error("failed to decoding audio buffer: " + filename);
               if (opt_callback) {
                 opt_callback(true);
               }
            });
          }
          req.addEventListener("error", function(e) {
            console.error("failed to load:", filename, " : ", e.target.status);
          }, false);
          req.send();
        }
    
        WebAudioSound.prototype = new WebAudioBuffer();
    
        function AudioTagSound(name, filename, samples, opt_callback) {
          this.waiting_on_load = samples;
          this.samples = samples || 1;
          this.name = name;
          this.play_idx = 0;
          this.audio = {};
          for (var i = 0; i < samples; i++) {
            var audio = new Audio();
            var that = this;
            var checkCallback = function(err) {
              that.waiting_on_load--;
              if (opt_callback) {
                opt_callback(err);
              }
            };
            audio.addEventListener("canplaythrough", function() {
              checkCallback(false);
            }, false);
            audio.src = filename;
            audio.onerror = function() {
              checkCallback(true);
            };
            audio.load();
            this.audio[i] = audio;
          }
        };
    
        AudioTagSound.prototype.play = function(opt_when, opt_loop) {
          if (this.waiting_on_load > 0) {
            console.log(this.name, " not loaded");
            return;
          }
          this.play_idx = (this.play_idx + 1) % this.samples;
          var a = this.audio[this.play_idx];
          // console.log(this.name, ":", this.play_idx, ":", a.src);
          var b = new Audio();
          b.src = a.src;
          // TODO: use when
          b.addEventListener("canplaythrough", function() {
            b.play();
            }, false);
          b.load();
        };
    
        var handleError = function(filename, audio) {
          return function(e) {
            console.error("can't load ", filename);
          }
        };
    
        this.playSound = function(name, opt_when, opt_loop) {
          if (!g_canPlay)
            return;
          var sound = g_soundBank[name];
          if (!sound) {
            console.error("audio: '" + name + "' not known.");
            return;
          }
          return sound.play(opt_when, opt_loop);
        }.bind(this);
    
        this.getTime = function() {
          return g_context ? g_context.currentTime : Date.now() * 0.001;
        }.bind(this);
    
        // on iOS and possibly other devices you can't play any
        // sounds in the browser unless you first play a sound
        // in response to a user gesture. So, make something
        // to respond to a user gesture.
        var setupGesture = function() {
          if (this.needUserGesture()) {
            var count = 0;
            var elem = window;
            var that = this;
            var eventNames = ['touchstart', 'mousedown'];
            var playSoundToStartAudio = function() {
              ++count;
             if (count < 3) {
                // just playing any sound does not seem to work.
                var source = g_context.createOscillator();
                var gain = g_context.createGain();
                source.frequency.value = 440;
                source.connect(gain);
                gain.gain.value = 0;
                gain.connect(g_context.destination);
                if (source.start) {
                  source.start(0);
                } else {
                  source.noteOn(0);
                }
                setTimeout(function() {
                  source.disconnect();
                }, 100);
              }
              if (count == 3) {
                for (var ii = 0; ii < eventNames.length; ++ii) {
                  elem.removeEventListener(eventNames[ii], playSoundToStartAudio, false);
                }
                if (options.startedOnTouchCallback) {
                  options.startedOnTouchCallback();
                }
              }
            }
            for (var ii = 0; ii < eventNames.length; ++ii) {
              elem.addEventListener(eventNames[ii], playSoundToStartAudio, false);
            }
          }
        }.bind(this);
    
        this.loadSound = function(soundName, filename, samples, opt_callback) {
          var ext = filename.substring(filename.length - 3);
          if (ext == 'ogg' && !g_canPlayOgg) {
            filename = changeExt(filename, "mp3");
          } else if (ext == 'mp3' && !g_canPlayMp3) {
            filename = changeExt(filename, "ogg");
          }
          var s = new g_createFromFileFn(soundName, filename, samples, opt_callback);
          g_soundBank[soundName] = s;
          return s;
        }.bind(this);
    
        this.init = function(sounds) {
          var a = new Audio()
          g_canPlayOgg = a.canPlayType("audio/ogg");
          g_canPlayMp3 = a.canPlayType("audio/mp3");
          g_canPlayWav = a.canPlayType("audio/wav");
          g_canPlayAif = a.canPlayType("audio/aif") || a.canPlayType("audio/aiff");
          g_canPlay = g_canPlayOgg || g_canPlayMp3;
          if (!g_canPlay)
            return;
    
          if (webAudioAPI) {
            console.log("Using Web Audio API");
            g_context = new webAudioAPI();
    
            if (!g_context.createGain) { g_context.createGain = g_context.createGainNode.bind(g_context); }
    
            g_createFromFileFn = WebAudioSound;
          } else {
            console.log("Using Audio Tag");
            g_createFromFileFn = AudioTagSound;
          }
    
          var soundsPending = 1;
          var soundsLoaded = function() {
            --soundsPending;
            if (soundsPending == 0 && options.callback) {
              options.callback();
            }
          };
    
          if (sounds) {
            Object.keys(sounds).forEach(function(sound) {
              var data = sounds[sound];
              ++soundsPending;
              this.loadSound(sound, data.filename, data.samples, soundsLoaded);
            }.bind(this));
          }
    
          // so that we generate a callback even if there are no sounds.
          // That way users don't have to restructure their code if they have no sounds or if they
          // disable sounds by passing none in.
          setTimeout(soundsLoaded, 0);
    
          if (webAudioAPI) {
            setupGesture();
          }
        }.bind(this);
        this.init(sounds);
    
        this.getSoundIds = function() {
          return Object.keys(g_soundBank);
        };
      };
    
      AudioManager.hasWebAudio = function() {
        return webAudioAPI !== undefined;
      };
    
      global.AudioManager = AudioManager;
    }(this));
    

    You can DL it here 这里有一个现场样本 (http://greggman.github.io/doodles/audio.html);

    要使用它,请将其与&lt;script src="audio.js"&gt;&lt;/script&gt; 一起使用。

    然后给它一个这样的声音列表

    var audioMgr = new AudioManager({                               
      fire:      { filename: "assets/fire.ogg",      samples: 8, }, 
      explosion: { filename: "assets/explosion.ogg", samples: 6, }, 
      hitshield: { filename: "assets/hitshield.ogg", samples: 6, }, 
      launch:    { filename: "assets/launch.ogg",    samples: 2, }, 
      gameover:  { filename: "assets/gameover.ogg",  samples: 1, }, 
      play:      { filename: "assets/play.ogg",      samples: 1, }, 
    });                                                             
    

    之后你可以用

    播放声音
     audioMgr.playSound('explosion');                               
     audioMgr.playSound('fire');                                    
    

    等等……

    samples 是您希望能够同时播放的声音的多少。任何支持 Web Audio API 的浏览器都不需要这样做。换句话说,它只需要 IE。

    另请注意,据我所知,Firefox 不支持 MP3,因此您需要为其提供 .ogg 文件。相反,Safari 不支持.ogg。无论您在初始化库时指定了什么,该库都会处理加载 .mp3.ogg 文件。换句话说,如果您输入filename: "foo.mp3",库将尝试加载foo.mp3foo.ogg,具体取决于您所在的浏览器是否支持其中之一。

    【讨论】:

      【解决方案2】:

      我建议您更改碰撞逻辑以发出新的事件类型,然后您可以捕获该事件以触发播放音频呼叫。下面显示了一个模拟碰撞,它调度新的发出事件,这些事件被监听以播放音频。

      <html>
      <head>
          <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
      </head>
      <body>
          <script>
      
              var sound_on_click = (function() {
      
                  // --- setup audio logic
      
                  var array_audio_files = []; // array to hold list available media filenames
      
                  array_audio_files.push("awesome_blip.mp3");
                  array_audio_files.push("smash_crunch.wav");
                  array_audio_files.push("wild_screech.wav");
      
                  var play_sound = function (given_index) {
      
                      var cool_tune = new Audio(array_audio_files[given_index]);
      
                      cool_tune.play();
                  };
      
                  // --- event emit
      
                  var event_collision = new Event("see_a_collision"); // define new event type
      
                  document.addEventListener("see_a_collision", function(e) { 
      
                      // randomly pick a media file from all available
                      var index_media_file = Math.floor(Math.random() * array_audio_files.length);
      
                      console.log("about to play sound ", index_media_file);
      
                      play_sound(index_media_file);
                  });
      
      
                  // --- below is a mock up of some collision condition
      
                  (function mock_collision(){
      
                      document.dispatchEvent(event_collision); // collision happened so emit event
      
                      setTimeout(mock_collision, 2000); // launch every x milliseconds
      
                  }());
      
              }());
      
          </script>
      </body>
      </html>
      

      【讨论】:

      • 我只是将new Audio('audiofile.mp3').play(); 放在我用来检测碰撞的if 条件中。实际上,我只需要播放音频文件的语法。如果我想播放多个文件,我会使用你的代码。所以我接受了你的回答。谢谢:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-02
      相关资源
      最近更新 更多