javascript - Hvordan spille av et lydspor på nytt ved å bruke Expo AV
Jeg jobber med en musikkapp med React Native, AWS og Expo. Jeg bruker Expo AV-biblioteket for å spille av lydfiler. Jeg har problemer med å få sangen til å spille av automatisk igjen etter at den er ferdig.
Nedenfor er mine forsøk på dette.
Mislykkede tilnærminger:
Jeg ser en boolean-variabel didjustFinish. Jeg prøver å tilbakestille den til true etter at lyden har spilt ferdig, så kan jeg
await sound.playAsync();men det ser ut til at det ikke fungererJeg prøver å matche durationMillis med playableDurationMillis - hvis de er like, kaller jeg
await sound.playAsync();. Dette fungerer heller ikke.
import React, { useContext, useEffect, useState } from 'react';
import { Text, Image, View, TouchableOpacity } from 'react-native';
import { AntDesign, FontAwesome } from "@expo/vector-icons";
import { API, graphqlOperation } from 'aws-amplify';
import styles from './styles';
import { Song } from "../../types";
import { Sound } from "expo-av/build/Audio/Sound";
import { AppContext } from '../../AppContext';
import { getSong } from "../../src/graphql/queries";
const PlayerWidget = () => {
const [song, setSong] = useState(null);
const [sound, setSound] = useState<Sound | null>(null);
const [isPlaying, setIsPlaying] = useState<boolean>(true);
const [duration, setDuration] = useState<number | null>(null);
const [position, setPosition] = useState<number | null>(null);
const [finish, setFinish] = useState<boolean>(true);
const { songId } = useContext(AppContext);
useEffect(() => {
const fetchSong = async () => {
try {
const data = await API.graphql(graphqlOperation(getSong, { id: songId }))
setSong(data.data.getSong);
} catch (e) {
console.log(e);
}
}
fetchSong();
}, [songId])
const onPlaybackStatusUpdate = (status) => {
setIsPlaying(status.isPlaying);
setDuration(status.durationMillis);
setPosition(status.positionMillis);
setFinish(status.didJustFinish);
// console.log(finish);
console.log(status);
}
const playCurrentSong = async () => {
if (song.artist.length > 10) {
song.artist = song.artist.substring(0, 6) + "...";
}
if (song.title.length > 8) {
song.title = song.title.substring(0, 5) + "...";
}
if (sound) {
await sound.unloadAsync();
}
const { sound: newSound } = await Sound.createAsync(
{ uri: song.uri },
{ shouldPlay: isPlaying },
onPlaybackStatusUpdate
)
setSound(newSound)
}
useEffect(() => {
if (song) {
playCurrentSong();
}
}, [song])
const onPlayPausePress = async () => {
if (!sound) {
return;
}
if (isPlaying) {
await sound.pauseAsync();
}
else {
await sound.playAsync();
}
if (finish) {
await sound.playAsync();
}
}
const getProgress = () => {
if (sound === null || duration === null || position === null) {
return 0;
}
return (position / duration) * 100;
}
if (!song) {
return null;
}
return (
<View style={styles.container}>
<View style={[styles.progress, { width: `${getProgress()}%` }]} />
<View style={styles.row}>
<Image source={{ uri: song.imageUri }} style={styles.image} />
<View style={styles.rightContainer}>
<View style={styles.nameContainer}>
<Text style={styles.title}>{song.title}</Text>
<Text style={styles.artist}>{song.artist}</Text>
</View>
<View style={styles.iconsContainer}>
<AntDesign name="hearto" size={20} color={'white'} />
<TouchableOpacity onPress={onPlayPausePress}>
<AntDesign name={isPlaying ? 'pausecircleo' : 'playcircleo'} size={25} color={'white'} />
</TouchableOpacity>
</View>
</View>
</View>
</View>
)
}
export default PlayerWidget;
Svar #1
Ta en titt på dokumentene
Det er noen punkter å huske på:
Etter at du har spilt av sporet en gang, vil det å kalle play på det igjen ikke ha noen effekt. Du kan imidlertid kalle sound.replayAsync() for å starte sporet på nytt.
Du kunne få lyden til å loope, slik at den automatisk starter på nytt hvis den når slutten ved å bruke (siterer dokumentasjonen):
playbackObject.setIsLoopingAsync(value) Dette er tilsvarende playbackObject.setStatusAsync({ isLooping: value })
Du trenger å refaktorere play/pause-metoden din for å håndtere de forskjellige tilfellene bedre. For eksempel, hvis den er ferdig, men skal fortsatt spille (kan være lurt å prøve å kalle replayAsync i stedet for playAsync).
En annen idé er å starte sporet på nytt hvis det er ferdig, men fortsatt skal spilles. Så hvis du ikke skal bruke looping, kan du fjerne betingelsen
if (finish) {
await sound.playAsync();
}
og legg det i en useEffect som ser på 'finish'. Jeg antar at det er lettere å bruke loop-flagget.
Svar #2
Jeg vet at dette er gammelt, men for alle som måtte være interessert
const [isPlaying, setIsPlaying] = useState(false);
const [sound, setSound] = useState(null);
const [numberOfLoops, setNumberOfLoops] = useState(0);
const N = 2; // Number of times to loop
const dir = FileSystem.cacheDirectory + 'xxx/';
const baseuri = `xxxx`;
const fileUri = dir;
const onPlaybackStatusUpdate = playbackStatus => {
if (playbackStatus.didJustFinish) {
if (numberOfLoops < N - 1) {
sound.setPositionAsync(0);
sound.playAsync();
setNumberOfLoops(numberOfLoops + 1);
} else {
sound.setIsLoopingAsync(false);
setIsPlaying(false);
}
}
};
const startSound = async (id) => {
const audioFile = fileUri + id + ". 1.mp3";
console.log('Playing File:', audioFile);
try {
const { sound: newSound } = await Audio.Sound.createAsync(
{ uri: audioFile },
{ shouldPlay: true }
);
setSound(newSound);
setNumberOfLoops(0);
newSound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate);
newSound.setIsLoopingAsync(true);
setIsPlaying(true);
} catch (e) {
console.log(`Cannot play the sound file`, e);
}
};
