녹음

박정빈·2024년 11월 4일

React Native 사용기

목록 보기
5/28

공식문서

설치

npx expo install expo-av
yarn add expo-module-scripts

음성 녹음 , 재생 예제

import { useEffect, useState } from 'react';
import { View, Button } from 'react-native';
import { Audio, InterruptionModeAndroid, InterruptionModeIOS } from 'expo-av';
import { AndroidOutputFormat, IOSOutputFormat } from 'expo-av/build/Audio';
import { AndroidAudioEncoder, IOSAudioQuality } from 'expo-av/build/Audio';

// 앱의 메인 컴포넌트
const RecordScreen = () => {
  const [recording, setRecording] = useState<Audio.Recording | undefined>();  // 녹음 상태 관리
  const [permissionResponse, requestPermission] = Audio.usePermissions();  // 오디오 권한 요청 및 응답 관리
  const [uri, setUri] = useState<string | null>(null);  // 녹음 파일의 URI 관리
  const [sound, setSound] = useState<Audio.Sound | undefined>();  // 오디오 객체 관리
  const [isPaused, setIsPaused] = useState<boolean>(false);  // 녹음 일시정지 상태 관리
  
  const newAudioOptions = {
    isMeteringEnabled: true,
    android: {
      extension: '.wav',
      outputFormat: AndroidOutputFormat.MPEG_4,
      audioEncoder: AndroidAudioEncoder.AAC,
      sampleRate: 44100,
      numberOfChannels: 2,
      bitRate: 128000,
    },
    ios: {
      extension: '.wav',
      outputFormat: IOSOutputFormat.MPEG4AAC,
      audioQuality: IOSAudioQuality.MAX,
      sampleRate: 44100,
      numberOfChannels: 2,
      bitRate: 128000,
      linearPCMBitDepth: 16,
      linearPCMIsBigEndian: false,
      linearPCMIsFloat: false,
    },
    web: {
      mimeType: 'audio/webm',
      bitsPerSecond: 128000,
    },
  };
  
  // 녹음을 시작하는 비동기 함수
  const startRecording = async () => {
    try {
      // 녹음이 이미 진행 중인지 확인
      if (recording)return; 

      // 권한이 없으면 권한 요청
      if (!permissionResponse || permissionResponse.status !== 'granted') await requestPermission();

      // 오디오 모드 설정
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: true,// 녹음 가능
        interruptionModeAndroid: InterruptionModeAndroid.DoNotMix, // 다른 앱 소리 안나게함 // test: X
        interruptionModeIOS: InterruptionModeIOS.DoNotMix, // 다른 앱 소리 안나게함 
        // playThroughEarpieceAndroid: true, // 이어폰을 통해 오디오 재생 가능  // test: 녹음시 다른 앱 소리 안나게함
        // playsInSilentModeIOS: false, // 무음모드에서 오디오 재생 가능 
        shouldDuckAndroid: false, // 백그라운드 오디오 음량 조절 가능 // test: true: 다른앱 duck -> 재생 끝나면 다른앱 정지 false: 다른앱 정지
        staysActiveInBackground: false, // 앱이 백그라운드에 있을 때 오디오(재생,녹음) 비활성 // test: O
      });
                                
      console.log('Starting recording..');
      // 녹음 시작
      const recordingObject = await Audio.Recording.createAsync(newAudioOptions);
      setRecording(recordingObject.recording);
      console.log('Recording started');
    } catch (err) {
      console.error('Failed to start recording', err);
    }
  }

 
  // 녹음을 일시정지 또는 재개
  const togglePauseRecording = async () => {
    if (recording) {
      if (isPaused) {
        console.log('Resuming recording..');
        await recording.startAsync();
        setIsPaused(false);
      } else {
        console.log('Pausing recording..');
        await recording.pauseAsync();
        setIsPaused(true);
      }
    }
  }

  // 녹음을 중지
  const stopRecording = async () => {
    console.log('Stopping recording..');
    setRecording(undefined);
    // 녹음 중지 및 언로드
    await recording?.stopAndUnloadAsync();
    // 오디오 모드를 녹음 불가로 설정
    await Audio.setAudioModeAsync({
      allowsRecordingIOS: false,
    });
    // 녹음 파일의 URI를 가져옴
    const uri = recording?.getURI() ?? null;
    setUri(uri);
    console.log('Recording stopped and stored at', uri);
  }

  // 녹음 파일을 재생
  const playSound = async () => {
    if (uri) {
      const { sound } = await Audio.Sound.createAsync({ uri });
      setSound(sound);
      console.log('Playing sound');
      await sound.playAsync();
    }
  }

  // 파일을 서버로 업로드하는 함수
  const uploadRecording = async () => {
    if (uri) {
      const formData = new FormData();
      formData.append("uri",uri);
      formData.append('name','recording.wav');
      formData.append('type','audio/wav');

      console.log("formData",formData);
;      try {
        const response = await fetch('https://your-backend-server.com/upload', {
          method: 'POST',
          body: formData,
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        });

        if (response.ok) {
          console.log('File uploaded successfully');
        } else {
          console.error('File upload failed');
        }
      } catch (error) {
        console.error('Error uploading file:', error);
      }
    } else {
      console.error('No recording available to upload');
    }
  }

  return (
    <View className="flex-1 justify-center bg-gray-200 p-2.5">
      <Button
        title={recording ? (isPaused ? 'Resume Recording' : 'Pause Recording') : 'Start Recording'}
        onPress={recording ? togglePauseRecording : startRecording}
      />
      {recording && (
        <Button
          title="Stop Recording"
          onPress={stopRecording}
        />
      )}
      {uri && (
        <>
          <Button
            title="Play Recording"
            onPress={playSound}
          />
          <Button
            title="Submit"
            onPress={uploadRecording}
          />
        </>
      )}
    </View>
  );
}
export default RecordScreen;

0개의 댓글