如何使用 Expo AV 重播音轨

问题描述 投票:0回答:2

我正在使用 React Native、aws 和 Expo 开发一款音乐应用程序。我正在使用 Expo AV 库来播放音频 文件。我很难让歌曲结束后自动重播。

以下是我的尝试。

失败的方法:

  1. 我看到一个 didjustFinish 布尔变量。我尝试在音频播放完毕后将其重置为 true,然后我可以

    await sound.playAsync();
    但似乎不起作用

  2. 我尝试将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;
javascript reactjs expo react-native
2个回答
7
投票

查看文档

有几点需要注意:

曲目播放完一次后,再次调用播放不会有任何效果。但是,您可以调用 sound.replayAsync() 来重新启动曲目。

您可以让声音循环播放,这样如果使用(引用文档)结束时它会自动重新启动:

playbackObject.setIsLoopingAsync(值) 这相当于playbackObject.setStatusAsync({ isLooping: value })

您需要重构播放/暂停方法以更好地处理不同的情况。例如,如果它已完成但仍要播放(可以尝试调用 replayAsync 而不是 playAsync)。

另一个想法是,如果曲目已完成但仍要播放,则重新启动曲目。因此,如果您不打算使用循环,则可以删除条件

     if (finish) {
         await sound.playAsync();
     }

并将其放入正在观看“完成”的 useEffect 中。我想使用循环标志更容易。


0
投票

我知道这已经很旧了,但对于任何可能感兴趣的人来说

  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);
    }
};
© www.soinside.com 2019 - 2024. All rights reserved.