Search code examples
audioaframe

In a-frame how can I stop all sounds when a new sound starts playing?


When I play click on the a-entity it plays the sound, but how can I get it to stop all other sounds when played, so it is not a loud muddled mess of a thing?

I've googled the problem and tried adding the codes they use, but they haven't been working, I've tried about 5 different ones.

I am running my sound like this:

<audio id="mercury-sound" src="mercury.mp3" preload="auto"></audio>

<script id="mercury" type="text/html">
          <a-entity class="mercury"
            geometry="primitive: sphere; radius: 0.67"
            material="shader: flat; src: ${thumb}"
            event-set__mouseenter="_target: #image-mercury; material.src: ${src}; opacity: 1"
            event-set__mouseleave="_target: #image-mercury; material.src: ${src}; opacity: 0">
            </a-entity>
          </script>


<a-entity template="src: #mercury" sound="src: #mercury-sound; on"></a-entity>

I want it to play the sound and stop all other sounds when played.

EDIT: if anyone else is having this issue, this is what fixed it

The template component is creating child nodes, You need to grab let el = e.target.parentNode. Check it out here also i would manage all sound-related logic in js, but thats another topic :) btw where is poor pluto ! – Piotr Adam Milewski


Solution

  • Here's an approach using js. The idea is to:

    1) keep an array of the elements with sound
    2) when one is clicked - iterate through the array and stop sounds
    3) play the 'clicked' one

    Having a setup like this:

    <a-box sound='#first'></a-box>
    <a-box sound='#second'></a-box>
    <a-box sound='#third'></a-box>
    

    You can create a script, which will:

    // grab all elements with the sound component
    var soundEls = document.querySelectorAll('[sound]')
    
    // make sure all of those react on a 'click' event
    for (var i = 0; i < soundEls.length; i++) {
       var item = soundEls[i];
       item.addEventListener('click', e => {
            let el = e.target
    
            // don't replay an already playing sound
            let isPlaying = el.components.sound.isPlaying
    
            // stop all sounds
            soundEls.forEach(soundEl => {
                soundEl.components.sound.stopSound()
            })
    
            // play the clicked sound if isn't already playing
            if (!isPlaying)
                el.components.sound.playSound()
      })
    }
    

    Usually, i'd recommend using an aframe custom component. But it may be easier to get the idea this way. Check it out in this glitch.