1

Im working on a tutorial to improve my JavaScript. I need to make a 'drum kit' where pressing keys makes a sound.

I have a code pen here although it doesn't work fully as I cant reference the audio files: https://codepen.io/anon/pen/vmzjPy

Ive got it working if you tap the keys slowly. However if you tap the same key quickly the sound doesn't play multiples times until the first instance has finished playing.

 <div class="keys">
    <div data-key="65" class="key">
      <kbd>A</kbd>
      <span class="sound">clap</span>
    </div>
    <div data-key="83" class="key">
      <kbd>S</kbd>
      <span class="sound">hihat</span>
    </div>
    <div data-key="68" class="key">
      <kbd>D</kbd>
      <span class="sound">kick</span>
    </div>
    <div data-key="70" class="key">
      <kbd>F</kbd>
      <span class="sound">openhat</span>
    </div>
    <div data-key="71" class="key">
      <kbd>G</kbd>
      <span class="sound">boom</span>
    </div>
    <div data-key="72" class="key">
      <kbd>H</kbd>
      <span class="sound">ride</span>
    </div>
    <div data-key="74" class="key">
      <kbd>J</kbd>
      <span class="sound">snare</span>
    </div>
    <div data-key="75" class="key">
      <kbd>K</kbd>
      <span class="sound">tom</span>
    </div>
    <div data-key="76" class="key">
      <kbd>L</kbd>
      <span class="sound">tink</span>
    </div>
  </div>

  <audio data-key="65" src="sounds/clap.wav"></audio>
  <audio data-key="83" src="sounds/hihat.wav"></audio>
  <audio data-key="68" src="sounds/kick.wav"></audio>
  <audio data-key="70" src="sounds/openhat.wav"></audio>
  <audio data-key="71" src="sounds/boom.wav"></audio>
  <audio data-key="72" src="sounds/ride.wav"></audio>
  <audio data-key="74" src="sounds/snare.wav"></audio>
  <audio data-key="75" src="sounds/tom.wav"></audio>
  <audio data-key="76" src="sounds/tink.wav"></audio>

<script>

  'use strict';

  const keyElems = document.getElementsByClassName('key');
  const soundElems = document.getElementsByTagName('audio');

  const highlight = function(elem) {
    elem.classList.add('playing');
    setTimeout(function(){
        elem.classList.remove('playing');
    }, 300);
  }

  const makeSound = function(val){
    for (const soundsElem of soundElems) {
      const soundsElemVal = soundsElem.getAttribute('data-key');
      if (val === soundsElemVal) {
        soundsElem.play();
      }
    }
  }

  const keyPressedSoundVal = function(obj) {
    //console.log('keyPressedSoundVal');
    const keyPressed = obj.key;
    for (const keyElem of keyElems) {
      const val = keyElem.getElementsByTagName('kbd')[0].textContent.toLowerCase();
      if (keyPressed === val) {
        highlight(keyElem);
        const val = keyElem.getAttribute('data-key');
        makeSound(val);
      }
    }
  }

  document.addEventListener("keypress", keyPressedSoundVal);

</script>
Evanss
  • 23,390
  • 94
  • 282
  • 505
  • Probably because `makeSound` just calls the respective audio elements `play` method. Calling it when the element is still playing has no effect - see https://www.w3.org/TR/html5/embedded-content-0.html#playing-the-media-resource - If you want to efficiently play sounds in parallel, have a look at the Web Audio API. – le_m May 18 '17 at 02:56
  • @le_m is right, if you are doing an drum-kit, use the WebAudio API, media elements are not meant for such purposes. You can find an implementation in [this Q/A](http://stackoverflow.com/questions/30433667/cloning-audio-source-without-having-to-download-it-again/30440830#30440830). – Kaiido May 18 '17 at 04:04

1 Answers1

1

Audio elements wont play again until they've finished playing, so the solution is to rewind the audio element before you play it:

soundsElem.currentTime = 0;
soundsElem.play();
Evanss
  • 23,390
  • 94
  • 282
  • 505