이 예제는 아이폰의 마이크를 통해 소리가 들어오면 dBFS(위키백과 링크)를 측정하여 조용한 상태인지, 소음이 있는지를 검사합니다.
먼저 마이크 권한을 물어보는 메시지에 대한 설정이 필요합니다. 다음 마이크 녹음을 진행하면서 dBFS를 측정하고, 이 값에 따라 소음이 어떤지 메시지를 나타냅니다. dBFS가 -50을 벗어나는 경우 조용한 상태, -10 ~ 0 인 경우 시끄러운 상태이며 0을 초과하는 경우는 측정값이 더 이상 의미가 없을 정도로 시끄러운 상태를 의미합니다.
1) Info.plist 설정 추가
Info.plist 설정을 열고 (+)
버튼을 누르면 새로운 항목 추가 란이 나타납니다. 여기서 Privacy - Microphone Usage Description
을 검색한 다음 마이크 권한을 허용할지 묻는 메시지를 추가합니다.
2) 메인 스토리보드에 레이블 배치 후 아웃렛 연결
위 lblDecibel
은 몇 dBFS 인지 나타내는 부분이며, 아래 lblLoudState
는 현재 소음의 상태를 나타내는 부분입니다.
3) ViewController 코드 작성
import Foundation import UIKit import AVFoundation import CoreAudio class ViewController: UIViewController { var recorder: AVAudioRecorder! var levelTimer = Timer() @IBOutlet weak var lblDecibel: UILabel! @IBOutlet weak var lblLoudState: UILabel! override func viewDidLoad() { super.viewDidLoad() initRecord() } func initRecord() { switch AVAudioSession.sharedInstance().recordPermission { case AVAudioSessionRecordPermission.granted: record() case AVAudioSessionRecordPermission.denied: recordNotAllowed() case AVAudioSessionRecordPermission.undetermined: AVAudioSession.sharedInstance().requestRecordPermission({ (granted) in print(granted) if granted { // timer는 main thread 에서 실행됨 // 그러나 requestRecordPermission 의 클로저 함수는 별도의 스레드에서 실행되므로 // 강제로 main 에서 실행되도록 한다. // DispatchQueue.main.sync { self.record() } } else { self.recordNotAllowed() } }) default: break } } func recordNotAllowed() { print("permission denied") } func record() { let audioSession = AVAudioSession.sharedInstance() // userDomainMask에 녹음 파일 생성 let documents = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0]) let url = documents.appendingPathComponent("record.caf") print("이 URL을 복사한 뒤 Finder - '이동' 메뉴 - '폴더로 가기'를 사용해 이동하세요.", documents.absoluteString.replacingOccurrences(of: "file://", with: "") ) // 녹음 세팅 let recordSettings: [String: Any] = [ AVFormatIDKey: kAudioFormatAppleIMA4, AVSampleRateKey: 16000, // 44100.0(표준), 32kHz, 24, 16, 12 AVNumberOfChannelsKey: 1, // 1: 모노 2: 스테레오(표준) AVEncoderBitRateKey: 9600, // 32k, 96, 128(표준), 160, 192, 256, 320 AVLinearPCMBitDepthKey: 8, // 4, 8, 11, 12, 16(표준), 18, AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue ] do { try audioSession.setCategory(AVAudioSession.Category.playAndRecord) try audioSession.setActive(true) try recorder = AVAudioRecorder(url:url, settings: recordSettings) } catch { return } recorder.prepareToRecord() recorder.isMeteringEnabled = true recorder.record() // 타이머는 main thread 에서 실행됨 levelTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(levelTimerCallback), userInfo: nil, repeats: true) } @objc func levelTimerCallback() { recorder.updateMeters() let level = recorder.averagePower(forChannel: 0) lblDecibel.text = String(format: "%.0f dBFS", level) // do whatever you want with isLoud if level < -48 { lblLoudState.text = "조용함" } else if level < -30 { lblLoudState.text = "약간의 소음 있음" } else if level < -10 { lblLoudState.text = "통상적인 소음 있음" } else { lblLoudState.text = "시끄러움" } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
Foundation
,AVFoundation
,CoreAudio
를 추가로import
합니다.initRecord
함수는 현재 마이크 권한 상태를 가져와, 마이크가 허용(granted
)이 되어있으면record
함수를 실행하고, 거부(denied
)되었다면recordNotAllowed
함수를 실행합니다.undetermined
는 앱을 최초로 실행한 상태라 마이크 허용 여부가 결정되지 않은 상태로, 이 때 허용이 되었다면 별도의 부분에 작성을 해야 합니다.undetermined
부분에서 주의할 점은record
함수의timer
부분인데 이 부분은 메인 스레드에서만 동작됩니다. 그러나requestRecordPermission
의 클로저 함수는 별도의 스레드에서 실행되므로 스레드를 정해주지 않는 경우timer
가 실행이 되지 않습니다.- 따라서 위의
requestRecordPermission
에서granted
된 경우에는 강제적으로record
함수를main
스레드에서 실행시켜줘야 할 필요가 있습니다. (DispatchQueue.main.sync
) – 참고 levelTimerCallback
에서 일정 인터벌마다 dBFS를 측정합니다. (참고: Swift(스위프트): 타이머(Timer) 만들기)
0개의 댓글