반응형
LinkedIn 개발자로 성장하면서 남긴 발자취들을 확인하실 수 있습니다.
Github WWDC Student Challenge 및 Cherish, Tiramisul 등 개발한 앱들의 코드를 확인하실 수 있습니다.
개인 앱 : Cherish 내 마음을 들여다보는 시간, 체리시는 디자이너와 PM과 함께 진행 중인 1인 개발 프로젝트입니다.
10년 후, 20년 후 나는 어떤 스토리 텔러가 되어 있을지 궁금하다. 내가 만약에 아직 조금 더 탐구하고 싶은 게 있고, 궁금한 게 있다면, 그게 설사 지금 당장의 내 인생에 도움이 안 되는 것 같더라도 경험해보자. 그 경험들을 온전히 즐기며 내 것으로 만들고, 내 일에 녹여내고... 그러다보면 그 점들이 모여 나란 사람을 그려내는 선이 될 테니까.

Recent Posts
Recent Comments
Total
관리 메뉴

꿈꾸는리버리

AVFoundation, 음악 재생, MP3 (반복재생) 본문

오뚝이 개발자/iOS

AVFoundation, 음악 재생, MP3 (반복재생)

rriver2 2022. 8. 11. 09:43
반응형

이번에 프로젝트를 하면서 음악을 재생하는 거에 대해 공부를 했다

필요했던 기능은 다음과 같았다.

  • mainView + 기록 시에 소리 on off 가능
  • 이전에 소리 on off 했던 걸 기억해두기 ( userDefault 사용 )
  • 무음 모드에서도 재생 가능하게 설정하기
  • 음악이 끝나면 다시 재생 시키기 (무한 재생)

 

  1️⃣ 음악 넣기  

 

  2️⃣ AVFoundation import하기  

AVFoundation은 QuickTime 동영상 및 MPEG-4 파일을 쉽게 재생, 생성 및 편집 등이 가능하도록 기능을 제공해주는 프레임워크이다 :)

  3️⃣ AVAudioPlayer 선언하기  

AVAudioPlayer는 파일 또는 버퍼에서 오디오 데이터를 재생하는 개체이다. 

AVAudioPlayer는 재생된 오디오의 볼륨, 패닝, 속도 및 반복 동작 제어를 할 수 있으며, 여러 플레이어의 재생을 동기화하여 여러 사운드를 동시에 재생할 수 있는 기능을 담고 있다.

파일에서 로드가 가능한 거 같은데, 나는 asset에 둔 오디오를  재생 시킬 거기 때문에 다음과 같은 init을 사용하려 한다.

@State var audio : AVAudioPlayer?

이렇게 @State로 audio를 선언했다. (이후에 음악이 바뀔 수도 있기 때문에..)

 

// audio: AVAudioPlayer
.onAppear {
            let song = NSDataAsset (name: "AfterTheRain")
            if let data = song?.data {
                self.audio = try? AVAudioPlayer(data: data, fileTypeHint: "mp3")
                playSound() // 이후 설명..
            }
        }

그리고 View가 onAppear될 때 해당 audio에 asset에 있는 "AfterTheRain" audio를 불러와줬다.

 

  4️⃣ 재생, 멈추기 기능  

Controlling PlayBack에는 다음과 같은 함수들이 있다 !

차례대로 알아보자면, 

func prepareToPlay() -> Bool

prepareToPlay()는 버퍼를 미리 로드하여 재생할 오디오 플레이어를 준비하는 함수이다. 이 함수를 호출하면, 버퍼를 미리 로드하고 재생에 필요한 오디오 하드웨어를 가져오기 때문에, play()메소드를 호출하는 시점과 사운드 출력을 시작하는 시점사이의 지연을 최소화 시킨다. 


func play() -> Bool

play()은 음악이 나오도록 하는 함수이다. 해당 함수를 재생하면 음악이 재생되는데 이번에 prepareToPlay() 함수를 호출하지 않으면, play()를 호출시에 암시적으로 prepareToPlay() 함수를 호출한다고 한다.

하지만 WWDC 준비할 때 매번 노래가 나오기 전에 버벅거리는 기계음이 나왔었는데, 이번에 공부하면서 play()전에 prepareToPlay() 함수를 호출하니까 매끄럽게 노래가 나오는 것을 확인할 수 있었다. prepareToPlay()을 통해 미리 준비해 두는 게 좋은 것 같다 ㅎㅎ


func play(atTime: TimeInterval) -> Bool

play(atTime: TimeInterval)은 써보지는 않았지만 이 함수가 실행되고 설정한 시간 후에 음악이 나오게끔 하는 것 같다. 

func pause()

pause()은 오디오 재생을 일시 중지하는 함수이다.

func stop()

stop()은 재생을 중지하고 시스템에서 재생에 필요한 설정을 취소하는 함수이다.

사실 처음에 pause()와 stop() 설명을 읽었을 때는 음악을 5초 재생한 후에 pause()를 호출하고 다시 play()를 하면 5초부터 재생이되고 stop()을 하면, 다시 처음부터 재생이 되는 줄 알았다.

근데 실험을 해보니까 아니더라구...? ㅎㅅㅎ

그래서 구글링을 해보니까 stop()은 prepareToPlay()때 세팅했던 하드웨어를 해제하고 pause()는 해제하지 않는다고 했다. 유저가 다시 play()를 할 수 있다면 pause()가, 한번 멈춘 후에 다시 재생시킬 확률이 작다면 prepareToPlay()를 쓰는 게 현명한 듯 싶다.

 

var isPlaying: Bool

isPlaying 변수는 현재 음악이 재생되고 있는지 아닌지를 판단해주는 변수이다. 

 

// audio: AVAudioPlayer
private func playSound() {
        if audio?.isPlaying {
        	self.audio?.prepareToPlay()
            self.audio?.play()
        } else {
            self.audio?.pause()
        }
    }

 

  5️⃣ 무음모드에도 재생하기  

노트북에서는 소리가 났는데.. 왜 디바이스에 연결하니까 소리가 안나지 ? 해서 삽질을 했더니.. 폰이 무음모드로 설정되어 있어서 그렇다는 것을 확인했다 ㅋㅋ 

기본적으로 iOS에서는 스위치를 통해서 무음모드로 설정하면 앱에서 재생되는 모든 오디오가 음소거된다. 그렇기 때문에 무음모드일 때도 재생이 되게끔 설정을 해야 했다 !

 

이렇게 AVAudioSession.Category을 .playback으로 설정해주면 된다 :)

do {
    	try AVAudioSession.sharedInstance().setCategory(.playback)
    } catch {
        print("Failed to set audio session category.")
}

 

  6️⃣ 음악 반복하기 ( 횟수 or 무한 반복 )  

AVAudioPlayer의 numberOfLoops의 숫자를 설정하면 된다. 

0이 기본값이고, 이거는 사운드가 한 번 재생이 된다. 만약 소리의 반복 횟수를 지정하고 싶다면 "양"의 정수를 사용하면 된다. ( 0이 1번 반복이니까, 1이 2번 반복, 2가 3번 반복)

 

만약 무한 반복을 하고 싶다면 "음"의 정수 값을 설정하면 된다. (stop()이나 plase()를 하면 정지됨)

 

  전체 코드  

import SwiftUI
import AVFoundation

struct SoundView: View {
    @EnvironmentObject var soundViewModel: SoundViewModel
    @State var audio : AVAudioPlayer?
    
    var body: some View {
        Image(systemName: soundViewModel.isMusicOn ? "speaker.wave.2.fill" : "speaker.wave.2")
            .onAppear {
                let song = NSDataAsset (name: "AfterTheRain")
                if let data = song?.data {
                    self.audio = try? AVAudioPlayer(data: data, fileTypeHint: "mp3")
                    self.audio?.numberOfLoops = -1
                    playSound()
                }
            }
            .onChange(of: soundViewModel.isMusicOn, perform: { newValue in
                playSound()
            })
    }
    
    private func playSound() {
        if audio != nil, audio!.isPlaying {
            self.audio?.prepareToPlay()
            self.audio?.play()
        } else {
            self.audio?.stop()
        }
    }
}

 

  참고자료  

https://developer.apple.com/av-foundation/

 

AVFoundation - Apple Developer

Find presentations, documentation, sample code, and development resources for using AVFoundation. The AVFoundation framework APIs provides essential services for working with time-based audiovisual media on Apple platforms.

developer.apple.com

https://developer.apple.com/documentation/avfaudio/avaudioplayer

 

Apple Developer Documentation

 

developer.apple.com

 

https://developer.apple.com/documentation/avfaudio/avaudioplayer/1386071-numberofloops

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/avfaudio/avaudiosession/category

 

Apple Developer Documentation

 

developer.apple.com

 

 

반응형
Comments