Search code examples
javascriptvue.jsaudiovuejs3

Vuejs: Exclude last played sound from array of sounds


I'm trying to randomize the sounds that I play when clicking on a button, but I also wanted to skip the last played sound. For example, if Sfx4 will be played four times in a row it won't sound natural. I want to always play a new sound (within these 5 sounds of course). What I'm doing wrong? Give me some ideas on how to implement it if possible.

<template>
    <button class="book__nav-btn" @click="playTurnSfx">
        <slot> </slot>
    </button>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue';
import Sfx1 from '@/assets/sounds/page-turn-1.mp3?url';
import Sfx2 from '@/assets/sounds/page-turn-2.mp3?url';
import Sfx3 from '@/assets/sounds/page-turn-3.mp3?url';
import Sfx4 from '@/assets/sounds/page-turn-4.mp3?url';
import Sfx5 from '@/assets/sounds/page-turn-5.mp3?url';

const sounds = [Sfx1, Sfx2, Sfx3, Sfx4, Sfx5];
const lastPlayedIndex = ref(null);
let playableSounds = sounds.toSpliced(lastPlayedIndex.value, 1);
const randomIndex = computed(() => {
    return Math.floor(Math.random() * playableSounds.length);
});

const playTurnSfx = () => {
    const sound = new Audio(sounds[randomIndex.value]);
    sound.volume = 0.1;
    sound.play();
};
</script>

Solution

  • You can always keep track of what the latest value was, filter it out from the array (don't worry, the original array will not change, you will get a copy of the array via filter, but without the items that did not match your criteria) and randomize the result:

    let sounds = ['a', 'b', 'c', 'd', 'e'];
    let previous = -1;
    
    function randomize() {
        let randIndex = parseInt(Math.random() * (sounds.length - ((sounds.indexOf(previous) >= 0) ? 1 : 0)));
        console.log(previous = sounds.filter(item => (item !== previous))[randIndex]);
    }
    <input type="button" value="randomize" onclick="randomize()">

    EDIT

    Looked into the try inside the second edit to the question and this is how I made it to work (I ignore the sound-playing, we need to fix the logic that determines which sound is to be played, so here it's safe to asume that playing sounds works):

    const sounds = ['Sfx1', 'Sfx2', 'Sfx3', 'Sfx4', 'Sfx5'];
    let previous = null;
    
    const getRand = () => {
        const filteredSounds = sounds.filter(item => item !== previous);
        return filteredSounds[Math.floor(Math.random() * filteredSounds.length)];
    };
    
    const playSfx = () => {
        //const sound = new Audio(sounds[randIndex]);
        //sound.volume = 0.1;
        //sound.play();
        previous = getRand();
        console.log(previous);
    };
    
    for (let i = 0; i < 20; i++) playSfx();

    The logical mistake that you made while trying to apply this idea was to, after correctly filtering your array and getting a random index, you took the sound corresponding the index just randomized from the unfiltered array rather than the filtered array.

    So, if previous was Sfx2, for example, then your filter resulted in an array like [Sfx1, Sfx3, Sfx4, Sfx5], but your initial array remained [Sfx1, Sfx2, Sfx3, Sfx4, Sfx5], so if you get a random integer between 0..3, which happens to be 1, then filteredSounds[1] will be Sfx3, which differs from previous, which was Sfx2 in this scenario, whereas sounds[1] is still Sfx2, so you need to get the value at the randomized index of filteredSounds rather than the unfiltered sounds.