I have multiple audio files that my audioplayer plays one by one. At times, especially after playing them 2 or 3 times, the audio pauses in the middle. When I try to press the play/pause button, I receive an error alert displaying either 'E_AUDIO_NOPLAYER' or 'E_LOAD_ERROR.' I then have to reload the app, after which it works fine. However, the same issue reappears after some time. What's wrong here? Following is my code:
const AudioPlayer = memo(
({
data,
audioPlayerState,
currentIdx,
setCurrentIdx,
setAudioPlayerState,
audioBaseUri,
audioFieldName,
onNextOrPreviousPageClick,
flatlistRef,
onAudioFinishCb
}) => {
const styles = useStyles()
const sound = useRef(new Audio.Sound())
useEffect(() => {
initializeAudioMode()
return () => {
sound?.current?.pauseAsync()
}
}, [])
initializeAudioMode = async () => {
try {
await Audio.setAudioModeAsync({
allowsRecordingIOS: false,
interruptionModeIOS: 'DoNotMix',
playsInSilentModeIOS: true,
staysActiveInBackground: true
// interruptionModeAndroid: 'DuckOthers',
// shouldDuckAndroid: true,
// playThroughEarpieceAndroid: true
})
loadAudio()
} catch (e) {
console.log(e)
}
}
//unload previous sound on every loadAudio call
useEffect(() => {
return sound?.current
? async () => {
await sound?.current?.unloadAsync()
console.log('Unloading Sound')
}
: undefined
}, [sound?.current])
useEffect(() => {
onDataChange()
}, [data])
onDataChange = async () => {
await loadAudio()
}
loadAudio = async () => {
try {
const { isPlaying, volume } = audioPlayerState
sound.current = new Audio.Sound() // to trigger sound.current useEffect which will unload prev audio
const fieldName = getValueByKeyString(data[currentIdx], audioFieldName) // because audioFieldName can be nested e.g. 'b.c.e'
const source = { uri: `${audioBaseUri}/${fieldName}` }
console.log(source)
const status = {
shouldPlay: isPlaying,
volume: volume,
progressUpdateIntervalMillis:
audioPlayerState.progressUpdateIntervalMillis ?? 1000
}
sound?.current?.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate)
await sound?.current?.loadAsync(source, status)
} catch (e) {
debugger
console.log(e)
alert(JSON.stringify(e))
}
}
onPlaybackStatusUpdate = async status => {
if (status.didJustFinish && !status.isLooping) {
let newIndex = currentIdx < data.length - 1 ? currentIdx + 1 : 0
// for Learn Quran Tab
if (onAudioFinishCb) {
await onAudioFinishCb(newIndex, currentIdx)
loadAudio()
}
// for Read Quran Tab
else if (onNextOrPreviousPageClick) {
// if last verse of the page then increment the page
if (currentIdx === data.length - 1) {
onNextOrPreviousPageClick(+1)
}
// else just increment the verse
else {
setCurrentIdx(newIndex)
flatlistRef?.current?.scrollToIndex({
animated: true,
index: newIndex
})
loadAudio()
}
}
} else {
setAudioPlayerState({
...audioPlayerState,
isPlaying: status.isPlaying,
isBuffering: status.isBuffering,
currentTime: status.positionMillis,
totalTime: status.durationMillis,
isLoaded: status.isLoaded
})
}
}
handlePlayPause = async () => {
try {
const { isPlaying } = audioPlayerState
if (sound) {
isPlaying
? await sound?.current?.pauseAsync()
: await sound?.current?.playAsync()
// set isPlaying to previous status because after unloading, the isPlaying status will change to false
setAudioPlayerState({
...audioPlayerState,
isPlaying: !isPlaying
})
}
} catch (e) {
console.log(JSON.stringify(sound.current))
alert(JSON.stringify(e))
}
}
unloadSound = async () => {
const { isPlaying } = audioPlayerState
if (sound?.current) {
await sound?.current?.unloadAsync()
// set isPlaying to previous status because after unloading, the isPlaying status will change to false
setAudioPlayerState({
...audioPlayerState,
isPlaying: isPlaying
})
}
}
MiniAudioPlayer = memo(() => {
return (
<View style={styles.container}>
<View style={styles.controls}>
{audioPlayerState.isLoaded ? (
<View style={[styles.controls, styles.control]}>
<CustomIcon
iconName={
audioPlayerState.isPlaying ? 'ios-pause' : 'ios-play-circle'
}
iconFamily='Ionicons'
iconSize={24}
iconColor='white'
// iconStyle={styles.control}
iconOnPress={handlePlayPause}
/>
{audioPlayerState.isBuffering && (
<Loader
isLoading
// color='white'
// style={styles.control}
size={34}
iconOnly
style={styles.buffering}
/>
)}
</View>
) : (
<Loader
isLoading
color='white'
style={styles.control}
size={24}
iconOnly
/>
)}
</View>
</View>
)
})
return data ? <MiniAudioPlayer /> : null
}
)
export default AudioPlayer