1
\$\begingroup\$

I am using Vue.js to play mp3 sounds on keyboard clicks. Even though each mp3 is less than 40 KB, there is a 95ms delay on heroku, on each request.

Is there a way to download sounds when dom is ready and not when clicking the buttons?

 let a = 'sound1.mp3',
        b = 'sound2.mp3';
    new Vue({
        created() {
            this.onKeyDown = this.onKeyDown.bind(this);
            document.getElementById('btn-sound').addEventListener('keydown', this.onKeyDown);
        },
        destroyed() {
            document.getElementById('btn-sound').removeEventListener('keydown', this.onKeyDown);
        },
        methods: {
            playSound(sound) {
                if (sound) {
                    var audio = new Audio(sound);
                    audio.play();
                }
            },

            onKeyDown(e) {
                switch (e.keyCode) {
                    case 12:
                        this.playSound(a);
                        break;
                    case 13:
                        this.playSound(b);
                        break;

                }

            }

        }
    });
\$\endgroup\$
2
\$\begingroup\$

SFC

Consider using Single File Components instead of the declarative syntax you are using right now. It removes a lot of the headache of dealing with the this keyword, and removes the necessity of having to deal with handlers. See the documentation for more information.

Life cycle hooks

Vue has a couple of life cycle hooks. You are currently using two of them. However, your code currently only works, because your html is not part of the component you just wrote. The destroyed life cycle hook is called after everything has been torn down, so there would be no html element to remove the handler from. You should be using the beforeDestroy hook instead. See this lifecycle diagram for more information.

Internal data

Your sound filenames should be internal data of your component, not some random variables outside it. They should also have descriptive names.

Sound playback delay

You are experiencing playback delay, because you load the sounds when you want to play them, instead of pre-loading them. You have two ways of dealing with this problem. If your sound file names are hardcoded, you can simply use the data initialisation function to create HTMLAudioElement s you can then later start or stop. See mdn for more information.

data () {
  return {
    sounds: {
      enter: new Audio('sound1.wav'),
      whatevercode12is: new Audio('sound2.mp3')
    }
  }
}

If you pass these sounds as properties, you need to be a bit more clever about it. You can use the created lifecycle hook to fill the sounds object like we created above, but would instead be initialised as an empty object, or you could put a watcher on the property with the immediate flag on true, and fill the sounds object as needed.

In either case you must play and immediately pause them once so they are downloaded. Then you can look them up and play them whenever you like:

this.playSound('enter')

and

playSound (soundName) {
  const playableSound = this.sounds[soundName];

  if (!playableSound) {
    throw new Error(`Called 'playSound' with unknown identifier [${soundName}]`);
  }

  playableSound.play();
}

Implementing it all

When you implement all that, you will end up with something like this:

<template>
  <div id="app">
    <input v-on:keydown.prevent="onKeyDown" value="Just focus me and press keys" />
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      sounds: {
        enter: new Audio(
          "http://www.pacdv.com/sounds/miscellaneous_sounds/air_blower_1.wav"
        ),
        shift: new Audio(
          "http://www.pacdv.com/sounds/miscellaneous_sounds/bottle_pop_1.wav"
        )
      }
    };
  },

  created() {
    for (const sound of Object.values(this.sounds)) {
      // This makes sure the sounds are loaded, but not played
      sound.play().then(() => {
        sound.pause();
      });
    }
  },

  methods: {
    playSound(soundName) {
      const playableSound = this.sounds[soundName];

      if (!playableSound) {
        throw new Error(
          `Called 'playSound' with unknown identifier [${soundName}]`
        );
      }

      if (!playableSound.paused) {
        // The previous sound is playing. We could reset the current sound with
        // playableSound.currentTime = 0
        // but we probably don't want to cut off that sound, so lets make a copy of the
        // sound and dereference the previous one
        this.sounds[soundName] = new Audio(playableSound.src);
      }

      this.sounds[soundName].play();
    },

    onKeyDown(e) {
      switch (e.keyCode) {
        case 13:
          return this.playSound("enter");
        case 16:
          return this.playSound("shift");
        default:
          // Do nothing
          break;
      }
    }
  }
};
</script>

Example on code sandbox

\$\endgroup\$
  • \$\begingroup\$ Can you show a small completed example based on my code? I would appreciate it. \$\endgroup\$ – csandreas1 Jun 8 '18 at 6:45
  • \$\begingroup\$ I will try to look at it in the weekend \$\endgroup\$ – Sumurai8 Jun 8 '18 at 12:48
  • \$\begingroup\$ I have made an edit and I have created a Code Sandbox where you can try editing it. \$\endgroup\$ – Sumurai8 Jun 9 '18 at 15:44
  • 1
    \$\begingroup\$ It plays the sounds simultaneously for me. It does not cut off the previous sound. You can probably use currentTime to seek to the beginning. \$\endgroup\$ – Sumurai8 Jun 9 '18 at 19:54
  • 1
    \$\begingroup\$ Sorry, this site is for code reviews and to point out things that can be done better in current code. I don't think your current use of Vue is particularly well implemented, so I showed you how SFC's work. Read the documentation and probably also read up on how to use webpack with Vue. There is a boilerplate readily available. \$\endgroup\$ – Sumurai8 Jun 11 '18 at 17:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.