Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio plays as soon as an instance is created #819

Open
n-ii-ma opened this issue Feb 22, 2023 · 9 comments
Open

Audio plays as soon as an instance is created #819

n-ii-ma opened this issue Feb 22, 2023 · 9 comments

Comments

@n-ii-ma
Copy link

n-ii-ma commented Feb 22, 2023

This packages has an annoying bug which makes it totally useless and that is playing without even calling the .play() method.

This only happens on Android. The issue is the fact that the method is being called inside of the callback of Sound:

var whoosh = new Sound('whoosh.mp3', Sound.MAIN_BUNDLE, (error) => {
  if (error) {
    console.log('failed to load the sound', error);
    return;
  }
  // loaded successfully
  console.log('duration in seconds: ' + whoosh.getDuration() + 'number of channels: ' + whoosh.getNumberOfChannels());

  // Play the sound with an onEnd callback
  whoosh.play((success) => {
    if (success) {
      console.log('successfully finished playing');
    } else {
      console.log('playback failed due to audio decoding errors');
    }
  });
});

And if the method of .play() isn't called inside of it, it won't work at all.

@sultson
Copy link

sultson commented Feb 24, 2023

Hey, @n-ii-ma, does this example fix the issue?

App.tsx

import React from 'react';
import { View, Button, StyleSheet } from 'react-native';
import Sound from 'react-native-sound'

Sound.setCategory('Playback');
var whoosh = new Sound('whoosh.mp3', Sound.MAIN_BUNDLE, (error) => {
  if (error) {
    console.log('failed to load the sound', error);
    return;
  }
});


function App(): JSX.Element {

  function playSound() {
    whoosh.play((success) => {
      if (!success) { console.log('Playback failed due to decoding errors') }
    });
  }

  return (
    <View style={styles.container}>
     <Button title='Play Sound' onPress={playSound} />
      
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent:'center',
    alignItems:'center'
  }
})

export default App;

Environment

package version
react-native 0.71.3
react-native-sound 0.11.2

@n-ii-ma
Copy link
Author

n-ii-ma commented Feb 25, 2023

Hey, @n-ii-ma, does this example fix the issue?

App.tsx

import React from 'react';
import { View, Button, StyleSheet } from 'react-native';
import Sound from 'react-native-sound'

Sound.setCategory('Playback');
var whoosh = new Sound('whoosh.mp3', Sound.MAIN_BUNDLE, (error) => {
  if (error) {
    console.log('failed to load the sound', error);
    return;
  }
});


function App(): JSX.Element {

  function playSound() {
    whoosh.play((success) => {
      if (!success) { console.log('Playback failed due to decoding errors') }
    });
  }

  return (
    <View style={styles.container}>
     <Button title='Play Sound' onPress={playSound} />
      
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent:'center',
    alignItems:'center'
  }
})

export default App;

Environment

package version
react-native 0.71.3
react-native-sound 0.11.2

On iOS it does work in this way but on Android I have to call the .play() method inside the callback of creating the Sound instance for it to work and that results in it playing as soon as it's created.

@sultson
Copy link

sultson commented Feb 25, 2023

Hmm that is weird, it works on my Android device. What can you see in the Android Studio logcat when pressing the button to play the sound? (without having the .play() inside the callback)

I get those 2 lines everytime the audio is played:
image

@n-ii-ma
Copy link
Author

n-ii-ma commented Feb 26, 2023

At first it logs the same lines as you but then logs these:
Screenshot 2023-02-26 at 1 12 10 PM

Might be because I'm calling the .play method inside a useEffect since by doing a button press it does actually play.

@n-ii-ma
Copy link
Author

n-ii-ma commented Feb 26, 2023

I just tried calling the .play() method with a button and still nothing. It seems that the asset won't get played at first but then it will.

Getting a lot of headaches with this library, TBH.

@sultson
Copy link

sultson commented Feb 26, 2023

You can solve it using the useState() hook, this way, you can display a loading screen until the asset has been loaded properly & then dismiss it. Also, wrap the function with useMemo() so that it will only be executed on the first render, like this:

const [loading, setLoading] = useState(true)
const whoosh =  useMemo(() => {
  return ( 
    new Sound('whoosh.mp3', Sound.MAIN_BUNDLE, (error) => {
      if (error) {
        console.log('failed to load the sound', error);
        return;
      }
      setLoading(false)
    })
  )
},[]);

@n-ii-ma
Copy link
Author

n-ii-ma commented Feb 27, 2023

I did as you mentioned and still nothing but I just realized that I'm releasing the audio in the cleanup function of useEffect and that seems to have been the issue here since the cleanup function runs when the conditions set in useEffect are not met and this causes the audio not to play. This is how my useEffect looks at the moment:

const [loading, setLoading] = useState(true); // Sound loading state

const orderMusicAudio = useMemo(() => {
    return new Sound('order_music.mp3', Sound.MAIN_BUNDLE, error => {
      // Return if there's an error
      if (error) {
        return;
      }
      // Set indefinite loop
      orderMusicAudio.setNumberOfLoops(-1);

      // Set loading to false
      setLoading(false);
    });
  }, []);

// Play sound if an order has been selected
  useEffect(() => {
    // Play sound if sound has loaded, the courier has incoming orders and hasn't yet accepted any
    if (!loading && hasOrders && isObjEmpty(acceptedOrder)) {
      // Start vibration
      Vibration.vibrate([1000, 1000], true);

      // Play the audio file
      // After it's completed, stop the vibration and release the audio player resource
      orderMusicAudio.play(() => {
        Vibration.cancel();
        orderMusicAudio.release();
      });
    }
    // Stop playing the audio file if otherwise
    else {
      // After the audio is stopped, stop the vibration and release the audio player resource
      orderMusicAudio.stop(() => {
        Vibration.cancel();
        orderMusicAudio.release();
      });
    }

    // Release the audio player resource on unmount
    return () => {
      console.log('RELEASE');
      orderMusicAudio.release();
    };
  }, [loading, hasOrders, acceptedOrder]);

This is for an app like Uber for the drivers which will play a sound after an order arrives via a Socket Connection.
(The reason I'm releasing the audio after each successful play and stop is for performance purposes although to be honest, I'm beginning to doubt its usefulness)

I myself am a bit confused here for even if the cleanup function gets called, after the conditions are met, the .play() method is invoked and the sound should play again but it doesn't.

Also, as I mentioned before, this problem only occurs on Android.

@sultson
Copy link

sultson commented Feb 27, 2023

You might be over-complicating things with .release(), try this instead:

useEffect(() => {
  if(loading) return 

  if (hasOrders && isObjEmpty(acceptedOrder)) {
    Vibration.vibrate([1000, 1000], true);
    orderMusicAudio.play((success) => {
      if (!success) { console.log('Playback failed due to decoding errors') }
    });
  } else {
    orderMusicAudio.stop()
  }
     
},[loading, hasOrders, acceptedOrder])

Let me know if it works now!

@n-ii-ma
Copy link
Author

n-ii-ma commented Feb 28, 2023

Yeah this does work indeed but since no cleanup is done in the useEffect, closing the app while the audio is still playing doesn't release the resource and stop the audio.

One workaround I found was to just stop playing the sound inside the cleanup function yet I'm wondering if not releasing the audio might lead to memory leak issues.

@BraveEvidence
Copy link

This will help https://www.youtube.com/watch?v=vVI7ZAZq5e0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants
@sultson @BraveEvidence @n-ii-ma and others