Search code examples
react-nativeaudioexpovoice-recording

How can I play a sound after recording in React Native Expo?


I'm building a voice recording application with Expo. This is my function to start voice recording, it works properly.

  const [recording, setRecording] = React.useState();

  async function startRecording() {
    try {
      console.log('Requesting permissions..');
      await Audio.requestPermissionsAsync();
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: true,
        playsInSilentModeIOS: true,
      }); 
      console.log('Starting recording..');
      const recording = new Audio.Recording();
      await recording.prepareToRecordAsync(Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY);
      await recording.startAsync(); 
      setRecording(recording);
      console.log('Recording started');
    } catch (err) {
      console.error('Failed to start recording', err);
    }
  }

And this is my 'stop' function, Recording stopped and stored...

  async function stopRecording() {
    console.log('Stopping recording..');
    setRecording(undefined);
    await recording.stopAndUnloadAsync();
    const uri = recording.getURI(); 
    console.log('Recording stopped and stored at', uri);
  }

Now I want to play this saved sound with a play button. How can I reach this saved sound?

  return (
    <View style={styles.container}>
      <Button
        title={recording ? 'Stop Recording' : 'Start Recording'}
        onPress={recording ? stopRecording : startRecording}
      />
    </View>
  );

I want to pass this stored uri as a file location. Is it possible in this way?

async function playSound() {
    console.log('Loading Sound');
    const { sound } = await Audio.Sound.createAsync(
       require(RecordedURI)
    );
    setSound(sound);
    console.log('Playing Sound');
    await sound.playAsync();
}

This is the snack link: https://snack.expo.io/ZN9MBtpLd


Solution

  • First of all, you are assigning your state to undefined after you get the recording.

    I would suggest you to do something like this

    Create two refs for Recording and Playing audio.

    const AudioRecorder = useRef(new Audio.Recording());
    const AudioPlayer = useRef(new Audio.Sound());
    

    And some states for recordingStatus, permission etc.

    const [RecordedURI, SetRecordedURI] = useState<string>("");
    const [AudioPermission, SetAudioPermission] = useState<boolean>(false);
    const [IsRecording, SetIsRecording] = useState<boolean>(false);
    const [IsPLaying, SetIsPLaying] = useState<boolean>(false);
    

    Snack for the recording implementation is here

    Here's a GitHub Repo which has the implementation.

    Screenshot of Result

    I've added below the Rest of the implementation

    import React, { useState, useRef, useEffect } from "react";
    import { View, StyleSheet, Button, Text } from "react-native";
    import { Audio } from "expo-av";
    
    export default function App() {
      // Refs for the audio
      const AudioRecorder = useRef(new Audio.Recording());
      const AudioPlayer = useRef(new Audio.Sound());
    
      // States for UI
      const [RecordedURI, SetRecordedURI] = useState<string>("");
      const [AudioPermission, SetAudioPermission] = useState<boolean>(false);
      const [IsRecording, SetIsRecording] = useState<boolean>(false);
      const [IsPLaying, SetIsPLaying] = useState<boolean>(false);
    
      // Initial Load to get the audio permission
      useEffect(() => {
        GetPermission();
      }, []);
    
      // Function to get the audio permission
      const GetPermission = async () => {
        const getAudioPerm = await Audio.requestPermissionsAsync();
        SetAudioPermission(getAudioPerm.granted);
      };
    
      // Function to start recording
      const StartRecording = async () => {
        try {
          // Check if user has given the permission to record
          if (AudioPermission === true) {
            try {
              // Prepare the Audio Recorder
              await AudioRecorder.current.prepareToRecordAsync(
                Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY
              );
    
              // Start recording
              await AudioRecorder.current.startAsync();
              SetIsRecording(true);
            } catch (error) {
              console.log(error);
            }
          } else {
            // If user has not given the permission to record, then ask for permission
            GetPermission();
          }
        } catch (error) {}
      };
    
      // Function to stop recording
      const StopRecording = async () => {
        try {
          // Stop recording
          await AudioRecorder.current.stopAndUnloadAsync();
    
          // Get the recorded URI here
          const result = AudioRecorder.current.getURI();
          if (result) SetRecordedURI(result);
    
          // Reset the Audio Recorder
          AudioRecorder.current = new Audio.Recording();
          SetIsRecording(false);
        } catch (error) {}
      };
    
      // Function to play the recorded audio
      const PlayRecordedAudio = async () => {
        try {
          // Load the Recorded URI
          await AudioPlayer.current.loadAsync({ uri: RecordedURI }, {}, true);
    
          // Get Player Status
          const playerStatus = await AudioPlayer.current.getStatusAsync();
    
          // Play if song is loaded successfully
          if (playerStatus.isLoaded) {
            if (playerStatus.isPlaying === false) {
              AudioPlayer.current.playAsync();
              SetIsPLaying(true);
            }
          }
        } catch (error) {}
      };
    
      // Function to stop the playing audio
      const StopPlaying = async () => {
        try {
          //Get Player Status
          const playerStatus = await AudioPlayer.current.getStatusAsync();
    
          // If song is playing then stop it
          if (playerStatus.isLoaded === true)
            await AudioPlayer.current.unloadAsync();
    
          SetIsPLaying(false);
        } catch (error) {}
      };
    
      return (
        <View style={styles.container}>
          <Button
            title={IsRecording ? "Stop Recording" : "Start Recording"}
            color={IsRecording ? "red" : "green"}
            onPress={IsRecording ? StopRecording : StartRecording}
          />
          <Button
            title={IsPLaying ? "Stop Sound" : "Play Sound"}
            color={IsPLaying ? "red" : "orange"}
            onPress={IsPLaying ? StopPlaying : PlayRecordedAudio}
          />
          <Text>{RecordedURI}</Text>
        </View>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: "center",
        backgroundColor: "#ecf0f1",
        padding: 8,
      },
    });