I am trying to write a chatapp with a audio player which can play audio file in the chatscreen. I have written the following code to achieve the play and pause function, as well as to ensure the playback will go back to time = 0 after playing.
import React, { useState, useRef } from "react";
import {
HStack,
Icon,
Slider,
Spinner,
VStack,
Text,
Button,
NativeBaseProvider,
} from "native-base";
import { MaterialIcons } from "@expo/vector-icons";
import { Audio } from "expo-av";
function msToTime(millisec) {
var seconds = (millisec / 1000).toFixed(0);
var minutes = Math.floor(seconds / 60);
var hours = "";
if (minutes > 59) {
hours = Math.floor(minutes / 60);
hours = hours >= 10 ? hours : "0" + hours;
minutes = minutes - hours * 60;
minutes = minutes >= 10 ? minutes : "0" + minutes;
}
seconds = Math.floor(seconds % 60);
seconds = seconds >= 10 ? seconds : "0" + seconds;
if (hours != "") {
return hours + ":" + minutes + ":" + seconds;
}
return minutes + ":" + seconds;
}
export const AudioPlayerViewTest = (props) => {
const {
duration,
audioUrl,
} = props;
const [isPlaying, setIsPlaying] = useState(false);
const [active, setActive] = useState(false);
const [loaded, setLoaded] = useState(false);
const [loading, setLoading] = useState(false);
const sound = useRef(new Audio.Sound());
const [currentDuration, setCurrentDuration] = useState(0);
const [totalDuration, setTotalDuration] = useState(0);
React.useEffect(() => {
loadAudio();
}, []);
async function loadAudio() {
setLoaded(false);
setLoading(true);
const checkLoading = await sound.current.getStatusAsync();
if (checkLoading.isLoaded === false) {
try {
const result = await sound.current.loadAsync({ uri: audioUrl });
if (result.isLoaded === false) {
setLoading(false);
console.log("Error in Loading Audio");
} else {
setLoading(false);
setLoaded(true);
}
} catch (error) {
console.log(error);
setLoading(false);
}
} else {
setLoading(false);
}
}
async function playAudio() {
try {
loadAudio();
const result = await sound.current.getStatusAsync();
console.log(result);
if (result.isLoaded) {
console.log("Play Audio");
await sound.current.playAsync();
setIsPlaying(true);
setActive(true);
}
sound.current.setOnPlaybackStatusUpdate((playbackStatus) => {
if (playbackStatus.isPlaying) {
setCurrentDuration(playbackStatus.positionMillis);
setTotalDuration(playbackStatus.durationMillis);
}
if (playbackStatus.didJustFinish) {
setIsPlaying(false);
setActive(false);
setCurrentDuration(0);
sound.current.unloadAsync();
loadAudio();
}
});
} catch (error) {
console.log("Cannot Play Audio");
}
}
async function pauseAudio() {
try {
const result = await sound.current.getStatusAsync();
if (result.isLoaded) {
if (result.isPlaying === true) {
console.log("Pause Audio");
sound.current.pauseAsync();
setIsPlaying(false);
setActive(false);
}
}
} catch (error) {
console.log("Cannot Pause Audio");
}
}
return (
<NativeBaseProvider>
<HStack
w="100%"
p={1}
justifyContent={"space-between"}
alignItems={"center"}
space={0}
bg={active ? "blue.50" : "grey.50"}
borderRadius={5}
>
<Button
borderWidth={0}
variant="outline"
colorScheme="blue"
onPress={!isPlaying ? playAudio : pauseAudio}
>
<Icon
as={MaterialIcons}
name={!isPlaying ? "play-arrow" : "pause"}
color={!isPlaying ? "grey.300" : "blue.400"}
style={{ transform: [{ rotateY: "0deg" }] }}
/>
</Button>
<Slider
w="50%"
size="sm"
colorScheme={"blue"}
defaultValue={0}
value={currentDuration}
minValue={0}
maxValue={totalDuration == 0 ? 100 : totalDuration}
accessibilityLabel="Audio Player"
step={1}
isReadOnly={true}
>
<Slider.Track bg={"grey"} size={2}>
<Slider.FilledTrack bg={"blue.400"} size={2} />
</Slider.Track>
<Slider.Thumb bg={"blue.400"} ml={-2} />
</Slider>
<Text
fontFamily={"body"}
color={active ? "blue.600" : "grey.400"}
mr={2}
>
{msToTime(currentDuration)} / {msToTime(totalDuration)}
</Text>
</HStack>
</NativeBaseProvider>
);
};
After several trials, I managed to implement all the functions I want. However, it appears to be pure luck as the playAudio function becomes a bit weird now. I have added the loadAudio() inside the playAudio function twice which I do not feel quite right as the sound object can never be unloaded even after playing. Are there any other proper ways to achieve what I want (i.e. the play and pause function, as well as to ensure the playback will go back to time = 0 after playing)?