Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web Audio API: How Can I Re-start the Playback Of a Sound?

I've written a basic script in Chrome that uses the new Web Audio Api to load 3 sound files (over XMLHTTPRequest) and play each of them back individually. I've provided a separate button for each sound that allows the user to start and stop each sound.

The script immediately loads all three sound files and when done, un-dims the play buttons so the user can only hit play once the sounds are ready. Further, the sounds are looping so the label on each button changes between "Play" and "Stop" when the button is clicked.

This all works great... When you click a Play button you hear the looping sound and when you hit Stop the sound stops. However, when you try to re-play the same sound for a second time, the sound will not start playing again. Each time you click the Play/Stop button the appropriate playSound() or stopSound() function gets called and the appropriate parameters get passed in but for some reason I just can't get the sounds to play a second time. Am I doing something wrong?

Here is my code:

<body>

<label for="playBtn1">Moog:</label>
<input id="playBtn1" type="button" value="Play" disabled />
<label for="playBtn1">Drums:</label>
<input id="playBtn2" type="button" value="Play" disabled />
<label for="playBtn1">Choir:</label>
<input id="playBtn3" type="button" value="Play" disabled />

<script>
  var playBtn1 = document.getElementById("playBtn1");
  var playBtn2 = document.getElementById("playBtn2");
  var playBtn3 = document.getElementById("playBtn3");

  var context = new webkitAudioContext();

  var soundBuffer1 = null;
  var soundBuffer2 = null;
  var soundBuffer3 = null;

  var soundBufferSourceNode1 = context.createBufferSource();
  soundBufferSourceNode1.looping = true;
  var soundBufferSourceNode2 = context.createBufferSource();
  soundBufferSourceNode2.looping = true;
  var soundBufferSourceNode3 = context.createBufferSource();
  soundBufferSourceNode3.looping = true;

  loadSound('micromoog.wav', 1);
  loadSound('breakbeat-drum-loop.wav', 2);
  loadSound('choir.wav', 3);

  playBtn1.addEventListener("click", function(e) {
    if(this.value == "Play") {
      this.value = "Stop";
      playSound(soundBuffer1, soundBufferSourceNode1);
    } else if(this.value == "Stop") {
      this.value = "Play";
      stopSound(soundBufferSourceNode1);
    }
  }, false);
  playBtn2.addEventListener("click", function(e) {
    if(this.value == "Play") {
      this.value = "Stop";
      playSound(soundBuffer2, soundBufferSourceNode2);
    } else if(this.value == "Stop") {
      this.value = "Play";
      stopSound(soundBufferSourceNode2);
    }
  }, false);
  playBtn3.addEventListener("click", function(e) {
    if(this.value == "Play") {
      this.value = "Stop";
      playSound(soundBuffer3, soundBufferSourceNode3);
    } else if(this.value == "Stop") {
      this.value = "Play";
      stopSound(soundBufferSourceNode3);
    }
  }, false);

  function loadSound(url, bufferNum) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    // Decode asynchronously
    request.onload = function() {
      var successCallback = function(buffer) {
        switch(bufferNum) {
          case 1:
            soundBuffer1 = buffer;
            playBtn1.disabled = false;
          break;
          case 2:
                soundBuffer2 = buffer;
            playBtn2.disabled = false;
          break;
          case 3:
            soundBuffer3 = buffer;
            playBtn3.disabled = false;
          break;
        }
      }
      var errorCallback = function(e) {
      console.log(e);
      }
      context.decodeAudioData(request.response, successCallback, errorCallback);
    }

    request.send();
  }

  function playSound(buffer, bufferSourceNode) {
    bufferSourceNode.buffer = buffer;
    bufferSourceNode.connect(context.destination);
    bufferSourceNode.noteOn(0);
  }

  function stopSound(bufferSourceNode) {
    bufferSourceNode.noteOff(0);
  }
</script>

</body>

Also, is anybody aware of any kind of event that might fire once a non-looping sound is done playing? It would be cool if I could set these sounds to non-looping and as soon as a sound is done playing, use such an event to toggle it's label. I don't see anything in the spec but maybe there's a better way?

Thanks, Brad.

like image 480
Brad Marshall Avatar asked Oct 22 '11 20:10

Brad Marshall


1 Answers

I know this is a year late, but I had a similar issue, did a few quick searches and here is what I found:

In this page: http://updates.html5rocks.com/2012/01/Web-Audio-FAQ

Then refer to the questions:

  • "How can I check when an AudioSourceNode has finished playing?"
  • "I’ve got an AudioBufferSourceNode, that I just played back with noteOn(), and I want to play it again, but noteOn() doesn’t do anything! Help!"

From what I understand: A source created from createBufferSource() can only be played once. Once you stop it, you have to create a new one. If you wanted to know when a particular sound has stopped playing, there is no event that dispatches it; so you have to use a timeout.

Hope this helps!

like image 71
LWY Avatar answered Sep 21 '22 01:09

LWY