最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to replay an audio track using Expo AV - Stack Overflow

programmeradmin3浏览0评论

I am working on a musical app with React native, aws and Expo. I am using the Expo AV library to play audio files. I am trouble getting the song to automatically replay after it finishes.

Below are my attempts at this.

Failed approaches:

  1. I see a didjustFinish boolean variable. I try to reset it to true after the audio finishes playing, then I can await sound.playAsync(); but it appears that is not working

  2. I try to match the durationMillis with the playableDurationMillis - if they are equal then call await sound.playAsync();. This also doe not work.

    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;

I am working on a musical app with React native, aws and Expo. I am using the Expo AV library to play audio files. I am trouble getting the song to automatically replay after it finishes.

Below are my attempts at this.

Failed approaches:

  1. I see a didjustFinish boolean variable. I try to reset it to true after the audio finishes playing, then I can await sound.playAsync(); but it appears that is not working

  2. I try to match the durationMillis with the playableDurationMillis - if they are equal then call await sound.playAsync();. This also doe not work.

    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;
Share Improve this question edited Jun 21, 2021 at 2:41 Vlad L 1,6943 gold badges9 silver badges22 bronze badges asked May 16, 2021 at 2:30 George S Mulbah IIGeorge S Mulbah II 3352 silver badges12 bronze badges 2
  • You can use useRef instead of UseState for holding the Sound object. – Kartikey Commented May 16, 2021 at 6:04
  • Also, instead of import { Sound } from "expo-av/build/Audio/Sound"; write import { Sound } from "expo-av"; – Kartikey Commented May 16, 2021 at 6:05
Add a ment  | 

2 Answers 2

Reset to default 7

Have a look at the docs

There are a few points to keep in mind:

After you play the track through once, calling play on it again will not have any effect. However, you can call sound.replayAsync() to re-start the track.

You could get the sound to loop, so that it automatically restarts if it gets to the end by using (quoting the docs):

playbackObject.setIsLoopingAsync(value) This is equivalent to playbackObject.setStatusAsync({ isLooping: value })

You need refactor your play/pause method to handle the different cases better. For example, if it's finished but is meant to still be playing (may be try calling replayAsync instead of playAsync).

Another idea is to restart the track if it's finished but still meant to be playing. So if you're not going to be using looping, you can remove the condition

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

and put it in a useEffect which is watching 'finish'. I guess using the looping flag is easier.

I know this is old but for anyone who might be interested

  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);
    }
};
发布评论

评论列表(0)

  1. 暂无评论