[Swift] AVAudioPlayer을 이용한 mp3 파일 재생

건희·2024년 10월 27일
0

알람 앱 관련 프로젝트를 진행하던 중 로컬 mp3 파일 재생을 구현할 필요가 있어 이 포스트를 작성한다.

먼저 iOS 기기에서 사운드 파일을 재생하기 위해 AVAudioSession, AVAudioPlayer에 대해 살펴보아야 한다.

AVAudioSession

개념

AVAudioSession은 앱과 운영체제 사이의 중간자 역할을 수행한다. 오디오 세션을 사용하면 앱이 사용하는 오디오의 특성에 대해 운영체제에 알림으로써 특정 오디오 동작이나, 요구되는 하드웨어와의 상호작용을 설명할 필요가 사라진다. 즉, 오디오 세션의 세부사항 관리를 위임시켜 운영체제가 사용자의 오디오 경험을 최상으로 관리하도록 한다.
모든 iOS 및 tvOS 앱에는 다음과 같이 사전 설정된 기본 오디오 세션이 있다.

  • 오디오 플레이백은 가능하지만, 오디오 녹음은 허용되지 않는다.
  • iOS에서는 스위치를 통해 무음모드로 설정하면, 앱에서 재생되는 모든 오디오가 음소거 된다.
  • iOS에서는 기기 화면이 꺼지면 (잠겼을 때) 앱 오디오가 음소거된다.
  • 앱이 오디오를 재생할 때 다른 백그라운드 오디오들은 음소거 된다.

기본 오디오 세션으로도 오디오 재생이 가능하지만, 미디어 재생 앱을 제작하기에는 충분하지 않을 수 있다. 예를 들어 백그라운드에서 FCM을 통한 알람을 구현해야 하는 경우에 해당한다.

사용

AVAudioSession은 싱글톤 객체로서 오디오 세션 카테고리 및 다른 설정을 수행한다. 오디오 세션은 앱 생애주기 전체에 걸쳐 상호작용할 수 있지만, 보통 앱이 최초 실행될 때 설정된다.

AVFoundation 에는 총 일곱가지 사용가능 카테고리가 있다. 이 때 미디어 재생 앱에서 가장 많이 사용되는 것은 플레이백(playback)이다. 사운드 스위치가 무음으로 설정되어 있는 경우 소리를 재생하고 싶다면, 해당 카테고리를 설정하면 된다.

또한 백그라운드 모드(Project의 오디오, AirPlay, PIP(Picture in Picture))를 설정했을 때도 앱의 백그라운드 오디오를 재생할 수 있도록 해준다.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]?) -> Bool {
    // MARK: 싱글톤 인스턴스 획득
    let audioSession = AVAudioSession.sharedInstance()
    do {
        // MARK: 오디오 세션 카테고리, 모드, 옵션을 설정합니다.
        try audioSession.setCategory(.playback, mode: .moviePlayback, options: [])
        try audioSession.setActive(true) // MARK: 오디오 세션 활성화
    } catch {
        print("Failed to set audio session category.")
    }
    return true
}

자, 이제 오디오 세션을 목적에 맞게 설정하였다.
다음으로 AVAudioPlayer를 사용하여 mp3 파일을 재생해보도록 하자.

AVAudioPlayer

개념

AVAudioPlayer는 다음의 기능을 수행한다.

  • 파일 또는 버퍼의 모든 구간에 대해 오디오 재생
  • 재생되는 오디오의 보륨, 패닝, 속도 및 반복 동작 제어
  • 재생 수준 측정 데이터에 액세스
  • 여러 플레이어의 재생을 동기화하여 여러 사운드를 동시에 재생

먼저 AVAudioPlayer를 사용하기 위해서는 AVFoundation 프레임워크를 import해야 한다.
이 글에서는 AVAudioPlayer(contentsOf: URL) 를 사용해 플레이어를 생성한다.

사용

class SoundOnlyPlayer {
	var soundPlayer = AVAudioPlayer?
    
    func play() {
    
    	// MARK: (1) 
    	let musicFileName = "sound"
    	guard let path = Bundle.main.path(forResource: musicFileName, ofType: "mp3") else { return }
        let url = URL(filePath: path)
        
        // MARK: (2)
        do {
        	self.soundPlayer = AVAudioPlayer(contentsOf: url)
            self.soundPlayer?.volumn = 0.5 // MARK: 0~1
            self.soundPlayer?.numberOfLoops = -1
            self.soundPlayer?.prepareToPlay()
            self.soundPlayer?.play()
        } catch {
        	print("Error: mp3 파일 재생 실패")
        }
        
        // MARK: (3)
        DispatchQueue.main.asyncAfter(deadline: .now() + 10 * 60) {
        	self.soundPlayer?.stop()
        }
    }
}	

(1)

  • mp3 파일 이름을 지정한다. (이 때 mp3 파일은 프로젝트 폴더 내에 있어야 하며, Copy Bundle에 추가되어있는지 확인해야 한다.)
  • Bundle.main.path : mp3 파일 이름에 해당하는 mp3 파일이 번들 내 있는지 확인하고, 있다면 파일 경로를 얻는다.
  • url로 변환한다.

(2)

  • AVAudioPlayer의 컨텐츠를 url로 지정해준다.
  • 플레이어의 설정을 진행한다. (volume, numberOfLoops, ..)
  • prepareToPlay() : 오디오 재생 전 딜레이를 최소화하는 메서드
  • play() : 오디오 재생

(3)

  • DispatchQueue.main.asyncAfter(deadline: DispatchTime) : 오디오가 10분 후 꺼지도록 설정

주의

만약, play() 메서드가 play(playerSetting: PlayerSetting) 과 같이 매개변수로 볼륨과 loop 횟수 등의 정보를 가지고 있는 구조체를 넘겨받도록 구현되어있다면, 함수가 종료됨과 동시에 해당 구조체는 메모리에서 해제되므로 오디오가 재생되지 않는 문제가 발생한다.

profile
💻 🍎

0개의 댓글