zmxv / react-native-sound

React Native module for playing sound clips
MIT License
2.78k stars 749 forks source link

Stopped working after CodePush #643

Open GNarek opened 4 years ago

GNarek commented 4 years ago

:beetle: Description

Issue happens on release build. It's working perfect while the app receives updates through CodePush

Event if update is very simple, like style changes (not related with the react-native-sound). Other things in app works as before, there are no other issues or crashes.

:beetle: What is the observed behavior?

No any sound after receiving updates from Appcenter.

:beetle: What is the expected behavior?

It should continue working.

:beetle: Please post your code:

const windSound = new Sound(require('../../assets/sounds/wind.mp3'));
windSound.play();

Is your issue with...

Are you using...

Which versions are you using?

"react-native-code-push": "^6.0.0",
"react-native-sound": "^0.11.0"

Does the problem occur on...

If your problem is happening on a device, which device?

murilobast commented 4 years ago

Have you tried to play the sound only in the callback instead of calling windSound.play() right after instantiating?

ex:

const windSound = new Sound(require('../../assets/sounds/wind.mp3'), err => {
     if (!err) {
         windSound.play();
    }
});

Also, it might be returning an error, you could try to log this error err somewhere

GNarek commented 4 years ago

@murilobast Thanks for response.

Just checked but not worked. and here is the error:

{ 
    "message": "resource not found",
    "code": -1
}
GNarek commented 4 years ago

I found workaround for android. I'm putting file into android/app/src/main/res/raw/wind.mp3

and call it like:

const windSound = new Sound('wind.mp3');
windSound.play();

But not sure about ios, where should I put files?

I tried to drag and drop files into xcode's project like in this tutorial but it didn't worked

murilobast commented 4 years ago

I found workaround for android. I'm putting file into android/app/src/main/res/raw/wind.mp3

and call it like:

const windSound = new Sound('wind.mp3');
windSound.play();

But not sure about ios, where should I put files?

I tried to drag and drop files into xcode's project like in this tutorial but it didn't worked

I ended up having the same issue. Codepush is messing with the audio url. Your workaround is what I did as well.

For iOS, you should open xcode and drag your audio files to your project, there's a guide in the readme.

The only problem is that I can't change the audio files without having to bundle and uploading to google play and apple store

murilobast commented 4 years ago

Now the sound stops playing after a while =/

Ex code:

import { useState, useEffect } from 'react'
import { AppState } from 'react-native'
import Sound from 'react-native-sound'

// Helpers
import randomBetween from '../helpers/randomBetween'

const BGM_KEYS = {
    main: 'bgm_1.mp3',
    battle: 'bgm_5.mp3'
}

const SFX_KEYS = {
    swing2: 'axe1.wav',
    swing3: 'axe4.wav',
    swing4: 'sword4.wav',
    hit1: 'monster2.wav',
    hit2: 'monster12.wav',
    hit3: 'bow1.wav',
    confirm: 'confirm.wav',
    cancel: 'cancel.wav',
    enter: 'enter.wav',
}

const enemySounds = ['swing2', 'swing2', 'swing3']
const playerSounds = ['hit1', 'hit2', 'hit3']

const arrayToObject = array => array.reduce((current, { key, value }) => {
    return {
        ...current,
        [key]: value
    }
}, {})

export default function useAudioPlayback(muteBGM, muteSFX, advertiseActive) {
    const [inBackground, setInBackground] = useState(false)
    const [BGM, setBGM] = useState({})
    const [SFX, setSFX] = useState({})
    const [current, setCurrent] = useState('main')

    const playBGM = name => {
        const audio = BGM[name]
        if (!audio) return
        if (BGM[current]) BGM[current].stop()

        setCurrent(name)

        if (!muteBGM) {
            audio.setNumberOfLoops(-1)
            audio.play()
        }
    }

    const playSFX = type => {
        if (muteSFX) return
        const sounds = type === 'enemy' ? enemySounds : playerSounds
        const name = sounds[randomBetween(0, sounds.length - 1)]

        const audio = SFX[name]

        if (!audio) {
            console.warn('SFX not found', name)
            return
        }

        audio.setVolume(0.5)
        audio.stop()
        audio.currentTime = 0
        audio.play()
    }

    const playUI = name => {
        if (muteSFX) return

        const audio = SFX[name]
        if (!audio) {
            console.warn('SFX not found', name)
            return
        }

        audio.setVolume(1)
        audio.stop()
        audio.currentTime = 0
        audio.play()
    }

    const preload = () => {
        const BGMArray = Object.keys(BGM_KEYS).map(key => {
            const name = BGM_KEYS[key]
            const audio = new Sound(name, Sound.MAIN_BUNDLE, err => {
                if (!err) {
                    audio.setNumberOfLoops(-1)
                    audio.setVolume(0.7)
                    if (key === 'main' && !muteBGM) audio.play()
                }
            })

            return {
                key,
                value: audio
            }
        })

        const SFXArray = Object.keys(SFX_KEYS).map(key => {
            const name = SFX_KEYS[key]
            const audio = new Sound(name, Sound.MAIN_BUNDLE, err => {
                console.log('SFX', name, err)
            })

            return {
                key,
                value: audio
            }
        })

        setBGM(arrayToObject(BGMArray))
        setSFX(arrayToObject(SFXArray))
    }

    const checkAudio = shouldMute => {
        if (shouldMute && BGM[current]) {
            BGM[current].pause()
        } else if (!shouldMute && BGM[current]) {
            BGM[current].setNumberOfLoops(-1)
            BGM[current].play()
        }
    }

    const handleAppStateChange = state => {
        setInBackground(state !== 'active')
    }

    useEffect(() => {
        preload()
        AppState.addEventListener('change', handleAppStateChange)
    }, [])

    useEffect(() => {
        checkAudio(muteBGM || inBackground || advertiseActive)
    }, [muteBGM, inBackground, advertiseActive])

    return {
        playBGM,
        playSFX,
        playUI
    }
}
sasweb commented 3 years ago

@murilobast have you been able to solve the issue? After doing some research on sound playback on Android I noticed that many people struggle with it. I think that Android isn't able to manage

a) the same sound played multiple times b) a large amount of preloaded sounds

due to memory issues. For example, I need to preload approx. 200 sounds (each just some kb in size). Depending on the Android device it is either fails during preloading at a random sound or after some playbacks. Loading (and unloading) sounds as they are used is not an option for me since I need immediate sound feedback after pressing a button.

I think you have/had a similar use case here. How did you handle this? Thanks!

sjonchhe commented 1 year ago

@murilobast have you been able to solve the issue? After doing some research on sound playback on Android I noticed that many people struggle with it. I think that Android isn't able to manage

a) the same sound played multiple times b) a large amount of preloaded sounds

due to memory issues. For example, I need to preload approx. 200 sounds (each just some kb in size). Depending on the Android device it is either fails during preloading at a random sound or after some playbacks. Loading (and unloading) sounds as they are used is not an option for me since I need immediate sound feedback after pressing a button.

I think you have/had a similar use case here. How did you handle this? Thanks!

I am facing the same issue. Did you by any chance found a solution for this issue?