오디오 녹음, 재생, 저장하기
개요
AVFoundation
을 이용해서 오디오를 녹음, 재생, 저장해보자.
코드를 작성하기 전에 먼저 AVFoundation
요소 중에서 세 가지 클래스를 살펴본다.
AVAudioSession
AVAudioRecorder
AVAudioPlayer
AVAudioSession
class AVAudioSession: NSObject
An audio session acts as an intermediary between your app and the operating system—and, in turn, the underlying audio hardware.
오디오 세션은 앱과 운영 체제 사이의 중개자 역할을 하며 기본 오디오 하드웨어 역할도 합니다.
모든 iOS, tvOS, watchOS 앱에는 다음 동작으로 미리 구성된 기본 오디오 세션이 있다.
- 오디오 재생은 지원하지만 오디오 녹음은 허용하지 않는다. (tvOS는 오디오 녹음을 지원하지 않는다.)
- iOS에서 벨소리/무음 스위치를 무음 모드로 설정하면 앱에서 재생중인 모든 오디오가 무음으로 설정된다.
- iOS에서 기기를 잠그면 앱의 오디오가 음소거된다.
- 앱에서 오디오를 재생하면 다른 배경 오디오는 음소거된다.
이러한 기본 동작을 변경하려면 앱의 오디오 세션 카테고리를 구성하면 된다. 사용할 수 있는 카테고리는 7가지이고, 일반적으로 사용하는 카테고리는 playback
이다. 이 카테고리를 사용하면 무음 모드로 설정된 상태에서도 앱의 오디오를 재생할 수도 있다. 참고
AVAudioSession
객체를 사용해서 앱의 오디오 세션을 구성한다. 이 클래스는 카테고리, 모드, 기타 구성을 설정하는 데 사용되는 싱글톤 객체이다. 앱의 생명주기 동안 오디오 세션과 상호작용할 수 있지만, 앱이 시작될 때 구성하는 것이 유용한 경우가 많다.
이후, setActive(_:)
또는 setActive(_:options:)
메서드를 사용해서 활성화하면 위에서 구성한 설정이 적용된다.
AVAudioRecorder
class AVAudioRecorder: NSObject
오디오 레코더를 이용해 다음 기능들을 사용할 수 있다.
- 시스템의 활성 입력 장치에서 오디오 녹음
- 지정된 기간 또는 사용자가 녹음을 중지할 때 까지 녹음
- 녹음 일시 중지 및 다시 시작
- Access recording-level metering data
iOS 또는 tvOS에서 오디오를 녹음하려면 오디오 세션의 카테고리를 record 또는 playAndRecord로 구성해야 한다.
AVAudioPlayer
class AVAudioPlayer: NSObject
오디오 플레이어를 이용해 다음 기능들을 사용할 수 있다.
- 파일 또는 버퍼에서 모든 길이의 오디오 재생
- 재생되는 오디오의 볼륨, 패닝, 속도 및 반복 동작 제어
- Access recording-level metering data
- 여러 플레이어의 재생을 동기화하여 여러 사운드를 동시에 재생
iOS 또는 tvOS에서 오디오를 재생하려면 Configuring the Audio Playback of iOS and tvOS Apps를 참고한다.
→ 요약하자면
- 대부분의 미디어 재생 앱에는
playback
카테고리로 설정하면 된다. - 그 밖에 백그라운드에서 오디오를 활성화하려면 다음처럼 설정하면 된다.
- Xcode에서
프로젝트.xcodeproj
선택 Signing & Capabilities
탭에서Capability
추가 선택Background Mode
추가Audio, AirPlay, and Picture in Picture
체크
- Xcode에서
사용자에게 녹음 권한 요청하기
중요한 요소를 모두 살펴보았으니, 실제로 동작하는 코드를 작성해보자. AVAudioSession
의 기본 구성에 나와있듯이 기본적으로 녹음은 허용하지 않는다. 왜냐하면 녹음 기능은 개인정보에 포함되기 때문이다. 그래서 녹음을 하려면 먼저 사용자에게 마이크 사용 권한을 요청해야 한다.
Info.plist
에 위와 같이 추가한다. 사용자에게 왜 마이크 권한이 필요한 지 설명을 해야 한다.
이제 사용자에게 권한 요청하는 메서드를 작성해보자. 권한을 요청할 때 세 가지 상황이 있다.
- 한 번도 사용자에게 녹음 권한을 요청하지 않은 경우
- 사용자에게 녹음 권한을 거부당한 경우
- 사용자에게 녹음 권한을 허용받은 경우
권한 요청은 비동기로 이루어지기 때문에 completionHandler
를 이용했다.
requestMicrophoneAccess()
는 viewWillAppear(_:)
에서 호출해서 사용한다. Alert
을 띄우지 않을 거라면 viewDidLoad(_:)
에서 호출해도 무관하다.
고민인 점은, 사용자에게 녹음 권한을 거부당했을 때 사용자가 직접 설정 화면에서 권한 허용을 하게끔 유도하는 부분이다. 앱으로 다시 돌아왔을 때 권한을 허용했는지 알고 싶은데, 이 부분을 어떻게 해결해야 할지 모르겠어서 다른 분께 여쭤봤다. 앱의 생명주기 중에 백그라운드에서 포그라운드로 올 때 확인하는 방법을 제안하셨다.
그런데 앱의 생명주기를 이용하지 않고 Observable
로 만들어서 관찰하면 되지 않을까? 라는 생각이 들었다. 비슷한 생각을 한 사람이 있었던 모양인지, RxPermission
이라는 라이브러리가 있었다. (현재는 deprecated)
아직까지 이 부분은 우선순위에 밀려서 구현하지 못하고 있는데, 굳이 Rx를 쓰지 않아도 NotificationCenter
를 이용해서 구현할 수 있을 거 같다.
오디오 녹음, 저장하기
아무튼 앞선 단계에서 사용자에게 권한을 허가받았다면 녹음을 할 수 있다. 녹음을 하려면 AVAudioRecorder
를 사용한다. AVAudioSession
처럼 싱글톤 객체가 있지 않고 인스턴스를 만들어서 사용한다. 인스턴스를 만들 때 [String: Any]
에 설정 값을 담아서 생성할 수 있다.
AVAudioRecorder
를 초기화까지 하고 나면 끝이다. 이제 녹음 기능을 수행하는 메서드를 만들고, 버튼과 연결해주면 된다.
사용자가 권한을 허용했고 && 녹음 버튼을 탭 했다면
- 녹음을 하고 있지 않으면 녹음을 시작한다.
- 녹음을 하고 있다면 녹음을 정지한다.
오디오 재생하기
녹음 중일 때는 재생을 할 수 없으니(파일이 저장되지 않았으므로), 확인하는 과정이 필요하다. delegate
는 오디오 재생이 끝났을 때 동작을 처리하기 위한 것이다.
전체 코드는 jwonyLee/iOSExamples에 있다.