figbar
figbar

Reputation: 794

Expo AV React Native Local Sound File issue

I am trying to create an audio player with react native and expo av. I can play online remote files (through uri) just fine, and my code works well. However, whenever I attempt to play a local file, it throws the error: [Unhandled promise rejection: Error: Cannot load an AV asset from a null playback source]. Most of my code for audio handling right now is dealt with through handleAudioPlayPause, which gets called on a button press. How can I get this program to play local files without an issue? Thanks

const audio = {
  filename: 'Audio file 1',
  uri:
  require('../assets/sounds/hello.mp3'), //this does not work
    // 'https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3', // this works
};

...

const [isPlaying, setIsPlaying] = useState(false);
const [playbackObject, setPlaybackObject] = useState<any | null>(null);
const [playbackStatus, setPlaybackStatus] = useState<any | null>(null);
useEffect(() => {
    if (playbackObject === null) {
      setPlaybackObject(new Audio.Sound());
    }
  }, []);
   const handleAudioPlayPause = async () => {
    if (playbackObject !== null && playbackStatus === null) {
      const status = await playbackObject.loadAsync(
        { uri: audio.uri },
        { shouldPlay: true }
      );
      setIsPlaying(true);
      return setPlaybackStatus(status);
    }

    // It will pause our audio
    if (playbackStatus.isPlaying) {
      const status = await playbackObject.pauseAsync();
      setIsPlaying(false);
      return setPlaybackStatus(status);
    }

    // It will resume our audio
    if (!playbackStatus.isPlaying) {
      const status = await playbackObject.playAsync();
      console.log(status);
      setIsPlaying(true);
      return setPlaybackStatus(status);
    }
  };
  

Upvotes: 2

Views: 3177

Answers (3)

NightMNKY
NightMNKY

Reputation: 11

Just to improve Kartikey answer, for a local file the following worked for me.

 const { sound } = await Audio.Sound.createAsync(
  { uri: recording.getURI() },
  {
    shouldPlay: true,
  }
);

Upvotes: 0

Yilmaz
Yilmaz

Reputation: 49681

I think instead of useState you should have used useRef, because useState causes rerender but useRef does not. We usually use useState if we want to reflect the change on UI.

  const soundRef = useRef(null);

useEffect(() => {
       #you could have multiple sound files to load
        const soundObject = new Audio.Sound();
  
        const loadSounds = async () => {
            await soundObject.loadAsync("Your config");
            soundRef.current = soundObject;

        };
        loadSounds();
        // ALways clean up running files
        return () => {
            soundObject && soundObject.unloadAsync();
            
        };
    }, []);

Upvotes: 0

Kartikey
Kartikey

Reputation: 5002

See there are different ways to load an audio file

  1. For remote Files use it as you were doing
const status = await playbackObject.loadAsync(
  { uri: audio.uri }, // For remote files we have to place uri like this
  { shouldPlay: true }
);
  1. But for local files, you have to use it like this,
const status = await playbackObject.loadAsync(
  audio.uri, // For local files we have to use it like this
  { shouldPlay: true }
);

Working Example here

Upvotes: 2

Related Questions