javascript - 如何使用 Expo AV 重放音频轨道
我正在使用 React Native、AWS 和 Expo 开发一个音乐应用。我正在使用 Expo AV 库来播放音频文件。我在让歌曲播放结束后自动重播时遇到了麻烦。
以下是我对此的尝试。
失败的方法:
我看到一个 didjustFinish 布尔变量。我尝试在音频播放完毕后将其重置为 true,然后我可以
await sound.playAsync();,但似乎不起作用我试着将 durationMillis 与 playableDurationMillis 匹配——如果它们相等,则调用
await sound.playAsync();。这也不起作用。
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;
答案#1
看看文档
有几点需要注意:
当你播放完这段音轨一次后,再次调用 play 将不会有任何效果。不过,你可以调用 sound.replayAsync() 来重新开始播放这段音轨。
你可以让声音循环,这样当它播放到末尾时会自动重新开始,可以使用(引用文档):
playbackObject.setIsLoopingAsync(value) 这等同于 playbackObject.setStatusAsync({ isLooping: value })
你需要重构你的播放/暂停方法,以更好地处理不同的情况。例如,如果它已经完成但仍然应该播放(可能尝试调用 replayAsync 而不是 playAsync)。
另一个想法是,如果音轨已经结束但仍然应该播放,则重新启动音轨。因此,如果你不打算使用循环,可以去掉这个条件
if (finish) {
await sound.playAsync();
}
并把它放在一个监视 'finish' 的 useEffect 中。我猜使用循环标志更容易。
答案#2
我知道这很旧,但对于可能感兴趣的人
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);
}
};
