Proper way to unload sound with unloadAsync if it can be replayed.

  1. SDK Version: 42.0.5 , expo-av 9.2.3
  2. Platforms: iOS and Android
  3. expo-av , Audio

I have a component where different short sounds can be played multiple times.
What is the proper way to use unloadAsync to clear sound from the memory when I will not need them anymore (something like “before component will unmount”) ?

  • I tried to call createAsync every time I click on a button and unloadAsync after sound did finished. But there is a huge delay (~300-600ms) before play especially on Android even for short sounds. With such a long delay click sound doesn’t feel like a callback to touch.

  • I tried to call unloadAsync in a component destroy callback but sound objects are already undefined at this point.

There is my simplified component:


let soundSounds = {
    "de": require("./../../sounds/sound_m_de.mp3"),
    "en_us": require("./../../sounds/sound_m_en_us.mp3"),
    "fr": require("./../../sounds/sound_m_fr.mp3"),
}

let letterSounds = {
    "de": require("./../../sounds/letter_m_de.mp3"),
    "en_us": require("./../../sounds/letter_m_en_us.mp3"),
    "fr": require("./../../sounds/letter_m_fr.mp3"),
}

function PronounceSettings({route, start, utils, navigation}){

        const [clickSound, setClickSound] = useState();
        const [letterSound, setLetterSound] = useState();
        const [soundSound, setSoundSound] = useState();

        useEffect(() => {
            _initSounds();
            return () => {
                // that doesn't work. clickSound and others are already undefined
                clickSound.unloadAsync();
                letterSound.unloadAsync();
                soundSound.unloadAsync();
            }
        },[]);



       const _initSounds = async() => {
                await Audio.setAudioModeAsync({
                    playsInSilentModeIOS: true,
                });
                const {sound} = await Audio.Sound.createAsync(require('./../../sounds/click.mp3'));
                setClickSound(sound);

                const {sound: letterSound} = await Audio.Sound.createAsync(letterSounds[utils.locale]);
                setLetterSound(letterSound);

                const {sound: soundSound} = await Audio.Sound.createAsync(soundSounds[utils.locale]);
                setSoundSound(soundSound);

                return true
    };

        const _playSound = async(sound) => {
             if (sound){
                  await sound.setPositionAsync(0);
                  sound.playAsync();
             }
        };

        return <View>
               <TouchableOpacity onPress={()=>{_playSound(clickSound)}}>
                   <Text>go back button</Text>
               </TouchableOpacity>

               <Text>Choose pronounciation</Text>
               <TouchableOpacity onPress={()=>{_playSound(letterSound)}}>
                   <Text>Letter</Text>
               </TouchableOpacity>
               <TouchableOpacity onPress={()=>{_playSound(soundSound)}}>
                   <Text>Sound</Text>
               </TouchableOpacity>

               <TouchableOpacity onPress={()=>{_playSound(clickSound)}}>
                   <Text>save button</Text>
               </TouchableOpacity>
        </View>


}

Looks like formulating a problem helps to solve it.
Answer to my own question. Just use useEffect callback for every sound:

useEffect(()=>{
        return clickSound ? () => {
          clickSound.unloadAsync(); }
          : undefined;
    },[clickSound])

    useEffect(()=>{
        return letterSound ? () => {
          letterSound.unloadAsync(); }
      : undefined;
    },[letterSound])

    useEffect(()=>{
        return soundSound ? () => {
          soundSound.unloadAsync(); }
      : undefined;
    },[soundSound])


1 Like

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.