2

I've been trying to fix this for over a week now and I just can't solve it.

I want to be able to map through an array of data that has a link for an audio file, and play each audio after another. The way I have it in my code now just plays all audios at once, so it doesn't wait until one is finished.

I know there are a couple ways you can wait until it finished playing, I just can't figure out how to use it. :/

Some help or a solution would be SUPER appreciated.

This is my code:

import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  Image,
  ScrollView,
} from 'react-native';
import React, { useState, useEffect } from 'react';
import { Audio } from 'expo-av';
import Sura from '../components/sura';
import Data from '../data/data';
import englishTranslation from '../data/english';

const bookPage = (props) => {
  const [listView, setListView] = useState(true);
  const [ayahData, setAyahData] = useState([]);
  const [sound, setSound] = useState(null);
  const [number, setNumber] = useState(null);

  useEffect(() => {
    return sound
      ? () => {
          sound.unloadAsync();
        }
      : undefined;
  }, [sound]);

  async function playSound(soundURL) {
    const { sound } = await Audio.Sound.createAsync({
      uri: soundURL,
    });
    setSound(sound);

    await sound.playAsync();
  }

  return (
    <View style={styles.background}>
      <View
        style={{ flex: 0.47, justifyContent: 'center', alignItems: 'center' }}
      >
        <Image style={styles.image} source={require('../public/read.png')} />
        <TouchableOpacity
          onPress={() => {
            props.modal(false);
          }}
          style={styles.goBackTouchable}
        >
          <Image
            style={styles.goBackView}
            source={require('../public/incorrect-button-compressed.png')}
          />
        </TouchableOpacity>
      </View>
      <View style={{ flex: 0.55 }}>
        <ScrollView
          style={styles.list}
          showsVerticalScrollIndicator={false}
          showsHorizontalScrollIndicator={false}
        >
          {listView
            ? Data.data.surahs.map((el, i) => {
                return (
                  <Sura
                    setAyahData={setAyahData}
                    listView={setListView}
                    ayahs={el.ayahs}
                    setNumber={setNumber}
                    number={el.number}
                    title={`${el.number}.  ${el.englishName}`}
                    arabic={`${el.name}`}
                    key={i}
                  />
                );
              })
            : ayahData[0].map((el, i) => {
                return (
                  <TouchableOpacity
                    onPress={() => {
                      playSound(el.audio);
                    }}
                    key={i}
                    style={styles.ayahText}
                  >
                    <View>
                      <Text>
                        <Text style={styles.number}>{el.number}</Text>
                        {'\n'}
                        <Text style={styles.arabic}>{el.text}</Text>
                      </Text>
                    </View>
                    <View>
                      <Text style={styles.english}>
                        {
                          englishTranslation.data.surahs[number - 1].ayahs[i]
                            .text
                        }
                      </Text>
                    </View>
                  </TouchableOpacity>
                );
              })}
        </ScrollView>
      </View>
    </View>
  );
};

export default bookPage;

const styles = StyleSheet.create({
  background: {
    flex: 1,
    position: 'relative',
    backgroundColor: '#3f1f7e',
    justifyContent: 'center',
    alignItems: 'center',
  },
  goBackView: {
    top: 30,
    right: 15,
    position: 'absolute',
    width: 25,
    height: 25,
  },
  goBackTouchable: {
    position: 'absolute',
    top: 25,
    right: '-50%',
  },
  image: {
    position: 'absolute',
    width: '100%',
    height: '75%',
  },
  list: {
    width: '100%',
    height: '100%',
  },
  ayahText: {
    width: '100%',
    height: 150,
    marginBottom: 25,
    backgroundColor: '#2a195b',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 5 },
    shadowOpacity: 0.25,
    shadowRadius: 2,
  },
  arabic: {
    color: 'white',
    marginLeft: 50,
    marginTop: 25,
    fontSize: 15,
    fontWeight: 'bold',
  },
  number: {
    color: 'white',
    marginLeft: 25,
    marginTop: 25,
    fontSize: 20,
    fontWeight: '100',
  },
  english: {
    color: 'white',
    fontSize: 10,
    fontWeight: 'bold',
  },
});

Example for the data:

                "ayahs": [
                    {
                        "number": 1,
                        "audio": "https://cdn.islamic.network/quran/audio/128/ar.alafasy/1.mp3",
                        "audioSecondary": [
                            "https://cdn2.islamic.network/quran/audio/128/ar.alafasy/1.mp3",
                            "https://cdn.islamic.network/quran/audio/64/ar.alafasy/1.mp3",
                            "https://cdn2.islamic.network/quran/audio/64/ar.alafasy/1.mp3"
                        ],
                        "text": "بِسْمِ ٱللَّهِ ٱلرَّحْمَٰنِ ٱلرَّحِيمِ",
                        "numberInSurah": 1,
                        "juz": 1,
                        "manzil": 1,
                        "page": 1,
                        "ruku": 1,
                        "hizbQuarter": 1,
                        "sajda": false
                    },
                    {
                        "number": 2,
                        "audio": "https://cdn.islamic.network/quran/audio/128/ar.alafasy/2.mp3",
                        "audioSecondary": [
                            "https://cdn2.islamic.network/quran/audio/128/ar.alafasy/2.mp3",
                            "https://cdn.islamic.network/quran/audio/64/ar.alafasy/2.mp3",
                            "https://cdn2.islamic.network/quran/audio/64/ar.alafasy/2.mp3"
                        ],
                        "text": "ٱلْحَمْدُ لِلَّهِ رَبِّ ٱلْعَٰلَمِينَ",
                        "numberInSurah": 2,
                        "juz": 1,
                        "manzil": 1,
                        "page": 1,
                        "ruku": 1,
                        "hizbQuarter": 1,
                        "sajda": false
                    }
]
ashketchup
  • 21
  • 5
  • I dont know if this bumps up the question, but any help? :/ – ashketchup Aug 02 '22 at 22:09
  • https://stackoverflow.com/a/73095201/10718641 I answered a similar question here – Abe Aug 03 '22 at 07:10
  • hey @Abe, thank you so much for your answer. I looked through the documentation you provided, and it seems like its exactly what I need, however I just cant seem to implement it in my codebase Could you give me a few hints or push me in the right direction please? It would be super appreciated – ashketchup Aug 03 '22 at 09:58
  • could you edit your post with the problems you're having? hard to help without knowing where you're getting stuck – Abe Aug 03 '22 at 15:58
  • I basically just don't know how to listen for sound ending with my current code base, I added some comments in the code @Abe – ashketchup Aug 03 '22 at 16:04
  • https://github.com/expo/playlist-example/blob/cde89fd6fa338d263370e39dabbaa83fa80e13df/App.js#L215 – Abe Aug 03 '22 at 16:06
  • this is the part I dont get, since I'm using functional react components, do I create useStates instead of constructors then? (For the this._onPlaybackStatusUpdate) and even if I do, how would it be able to set my PlayBackStatus useState once it finished playing – ashketchup Aug 03 '22 at 18:03
  • I'm not sure what you mean about constructors. You can use useState to keep track of the playback status. You pass your callback to Audio.Sound.createAsync, and then set the state from inside the callback, like they do here: https://github.com/expo/playlist-example/blob/cde89fd6fa338d263370e39dabbaa83fa80e13df/App.js#L250 – Abe Aug 03 '22 at 19:24
  • It makes a little bit more sense to me now thank you, I added some code to my inital post, could you please check @Abe so much appreciated – ashketchup Aug 04 '22 at 11:39
  • it's saying status.isLoaded is undefined – ashketchup Aug 04 '22 at 11:45
  • 1
    The problem is that you're invoking the function when you mean to pass it. You're writing `onPlaybackStatusUpdate(status)` when you pass the function to loadAsync. Putting the `(status)` after the name of the function invokes it. The callback you pass will be called with the status, so don't call it here. The line should read instead `const { status } = await playbackObj.loadAsync({ uri: soundURL }, { shouldPlay: true }, onPlaybackStatusUpdate);` – Abe Aug 04 '22 at 19:27
  • 1
    thank you so much for your continous help @Abe, I dont have an error anymore and I feel like its very close but the audio isn't playing atm – ashketchup Aug 05 '22 at 11:20
  • 1
    I changed the code to what it looks like currently (so added your implementation without calling the function) – ashketchup Aug 05 '22 at 11:21
  • @Abe I updated my code again, it works when I manually click on the Touchable now, but it still wont work automatically. (I took out the playback part) – ashketchup Aug 08 '22 at 10:14
  • Nice! It's probably not playing because it's not loaded yet. – Abe Aug 08 '22 at 21:01
  • no thats not the reason, the logic for the automatically playing them after each other just isnt there yet. I give up for now – ashketchup Aug 09 '22 at 17:16

0 Answers0