本文是Intermediate iOS 11 Programming with Swift 4系列?的 第 十 篇.
iOS SDK提供了多種框架即彪,可以讓你在應(yīng)用中使用聲音。你可以用來(lái)播放和錄制音頻文件的框架之一是AV Foundation框架授段。在這一章史煎,我將帶你通過(guò)基本的框架,并向你展示如何管理音頻回放和錄音.
AV Foundation為開(kāi)發(fā)者提供了必要的api來(lái)處理iOS上的音頻。在這個(gè)demo中堪遂,我們主要使用框架的這兩個(gè)類:
AVAudioPlayer - 把它想象成一個(gè)播放聲音文件的音頻播放器尉尾。通過(guò)使用播放器爆阶,您可以在iOS中播放任何持續(xù)時(shí)間和音頻格式的聲音
AVAudioRecorder - 一個(gè)用來(lái)記錄音頻的錄音機(jī)。
Demo
為了了解如何使用API沙咏,我們將構(gòu)建一個(gè)簡(jiǎn)單的音頻應(yīng)用程序辨图,允許用戶錄制和播放音頻。我們的主要關(guān)注點(diǎn)是演示AVFoundation框架芭碍,這樣應(yīng)用程序的用戶界面就會(huì)非常簡(jiǎn)單徒役。
首先,使用 Single View Application 模板創(chuàng)建一個(gè)應(yīng)用程序窖壕,并將其命名為RecordPro. 您可以自己設(shè)計(jì)一個(gè)類似于圖10.1的用戶界面忧勿。然而杉女,要讓您免于設(shè)置用戶界面和自定義類,您可以從這里下載模板!? 界面很簡(jiǎn)單, 只有3個(gè)按鈕: 錄音, 暫停, 和播放.?
它也有一個(gè)計(jì)時(shí)器來(lái)顯示錄制期間經(jīng)過(guò)的時(shí)間鸳吸。這些按鈕已經(jīng)連接到RecordProController類中相應(yīng)的動(dòng)作方法熏挎,而RecordProController類是UIViewController的一個(gè)子類.?
在我們進(jìn)入到實(shí)現(xiàn)之前,讓我給你一個(gè)更好的關(guān)于演示應(yīng)用程序如何工作的想法:
?
* 當(dāng)用戶點(diǎn)擊錄制按鈕時(shí)晌砾,應(yīng)用程序啟動(dòng)計(jì)時(shí)器并開(kāi)始錄制音頻坎拐。然后,記錄按鈕被暫停按鈕取代养匈。如果用戶點(diǎn)擊暫停按鈕哼勇,應(yīng)用程序?qū)和d浿疲钡接脩粼俅吸c(diǎn)擊按鈕呕乎。在編碼方面积担,它調(diào)用記錄操 作方法
* 當(dāng)用戶點(diǎn)擊停止按鈕時(shí),應(yīng)用程序會(huì)停止錄制猬仁。我已經(jīng)把按鈕和RecordProController中的stop動(dòng)作方法連接起來(lái)了帝璧。
* 要播放錄音,用戶可以點(diǎn)擊播放按鈕湿刽,這是與播放方法相關(guān)聯(lián)的的烁。
AVAudioRecorder
AVAudioRecorder類的AV Foundation框架允許您的應(yīng)用程序提供音頻錄制功能。在iOS中诈闺,錄制的音頻來(lái)自iOS設(shè)備的內(nèi)置麥克風(fēng)或耳機(jī)麥克風(fēng)渴庆。這些設(shè)備包括iPhone、iPad或iPod touch买雾。
首先把曼,讓我們看看如何使用AVAudioRecorder類來(lái)記錄音頻。與SDK中的大多數(shù)api一樣漓穿,AVAudioRecorder使用了delegate模式嗤军。您可以為音頻記錄器實(shí)現(xiàn)delegate對(duì)象,以響應(yīng)音頻中斷并完成錄制晃危。AVAudioRecorder對(duì)象的委托必須采用AVAudioRecorderDelegate協(xié)議.?
可以寫個(gè)Extension:
extension RecorderProController:?AVAudioRecorderDelegate {?
}?
記得導(dǎo)入AV Foundation 框架 !? ?
聲明 AVAudioRecorder 和 AVAudioPlayer的實(shí)例變量
var audioRecorder:??AVAudioRecorder ?
var audioPlayer: AVAudioPlayer?
讓我們先關(guān)注AVAudioRecorder叙赚。稍后我們將使用audioPlayer變量。AVAudioRecorder類提供了一種在應(yīng)用程序中記錄聲音的簡(jiǎn)單方法僚饭。
?* 指定一個(gè)聲音文件URL
?* 設(shè)置一個(gè)音頻會(huì)話
?* 配置錄音機(jī)的初始狀態(tài)
我們將創(chuàng)建一個(gè)名為configure()的私有方法來(lái)進(jìn)行設(shè)置震叮。將代碼插入RecordProController類中
直接上代碼:?
private func configure() {
? ? // Disable Stop/Play button when application launches
? ? stopButton.isEnabled = false
? ? playButton.isEnabled = false
? ? // Get the document directory. If fails, just skip the rest of the code
? ? guard let directoryURL = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first else {
? ? ? ? let alertMessage = UIAlertController(title: "Error", message: "Failed to get the document directory for recording the?“audio. Please try again later.", preferredStyle: .alert)
? ? ? ? alertMessage.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
? ? ? ? present(alertMessage, animated: true, completion: nil)
? ? ? ? return
? ? }
? ? // Set the default audio file
? ? let audioFileURL = directoryURL.appendingPathComponent("MyAudioMemo.m4a")
? ? // Setup audio session
? ? let audioSession = AVAudioSession.sharedInstance()
? ? do {
? ? ? ? try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, with: AVAudioSessionCategoryOptions.defaultToSpeaker)
? ? ? ? // Define the recorder setting
? ? ? ? let recorderSetting: [String: Any] = [
? ? ? ? ? ? AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
“ AVSampleRateKey: 44100.0,
? ? ? ? ? ? AVNumberOfChannelsKey: 2,
? ? ? ? ? ? AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
? ? ? ? ]
? ? ? ? // Initiate and prepare the recorder
? ? ? ? audioRecorder = try AVAudioRecorder(url: audioFileURL, settings: recorderSetting)
? ? ? ? audioRecorder.delegate = self
? ? ? ? audioRecorder.isMeteringEnabled = true
? ? ? ? audioRecorder.prepareToRecord()
? ? } catch {
? ? ? ? print(error)
? ? }
}
接下來(lái)看一看 錄音按鈕的 實(shí)現(xiàn)
當(dāng)用戶點(diǎn)擊錄制按鈕時(shí),應(yīng)用程序?qū)㈤_(kāi)始錄制鳍鸵。錄制按鈕將被更改為暫停按鈕苇瓣。如果用戶點(diǎn)擊暫停按鈕,應(yīng)用程序?qū)和R纛l錄制偿乖,直到再次點(diǎn)擊按鈕击罪。當(dāng)用戶點(diǎn)擊“停止”按鈕時(shí)哲嘲,錄音就會(huì)停止
代碼如下 :
@IBAction func record(sender: UIButton) {
? ? // Stop the audio player before recording
? ? if let player = audioPlayer, player.isPlaying {
? ? ? ? player.stop()
? ? }
?if !audioRecorder.isRecording {
? ? ? ? let audioSession = AVAudioSession.sharedInstance()
? ? ? ? do {
? ? ? ? ? ? try audioSession.setActive(true)
? ? ? ? ? ? // Start recording
? ? ? ? ? ? audioRecorder.record()
? ? ? ? ? ? // Change to the Pause image
? ? ? ? ? ? recordButton.setImage(UIImage(named: "Pause"), for: UIControlState.normal)
? ? ? ? } catch {
? ? ? ? ? ? print(error)
? ? ? ? }
? ? } else {
? ? ? ? // Pause recording
? ? ? ? audioRecorder.pause()
? ? ? // Change to the Record image
? ? ? ? recordButton.setImage(UIImage(named: "Record"), for: UIControlState.normal)
? ? }
? ? stopButton.isEnabled = true
? ? playButton.isEnabled = false
}
當(dāng)然別忘 在 info.plist 里面 加入訪問(wèn)權(quán)限??
暫停按鈕的實(shí)現(xiàn)??
當(dāng)用戶點(diǎn)擊停止按鈕時(shí),會(huì)調(diào)用停止動(dòng)作方法媳禁。這個(gè)方法很簡(jiǎn)單眠副。我們首先重置按鈕的狀態(tài),然后調(diào)用AVAudioRecorder對(duì)象的stop方法來(lái)停止錄制竣稽。最后囱怕,我們關(guān)閉音頻會(huì)話
代碼如下 :?
@IBAction func stop(sender: UIButton) {
? ? recordButton.setImage(UIImage(named: "Record"), for: UIControlState.normal)
? ? recordButton.isEnabled = true
? ? stopButton.isEnabled = false
? ? playButton.isEnabled = true
? ? // Stop the audio recorder
? ? audioRecorder?.stop()
? ? let audioSession = AVAudioSession.sharedInstance()
? ? do {
? ? ? ? try audioSession.setActive(false)
? ? } catch {
? ? ? ? print(error)
? ? }
}
實(shí)現(xiàn) AVAudioRecorderDelegate 協(xié)議
你可以使用AVAudioRecorderDelegate協(xié)議來(lái)處理音頻中斷(比如,錄音期間的電話)以及完成錄音毫别。在本例中娃弓,RecordProController是委托。AVAudioRecorderDelegate協(xié)議中定義的方法是可選的岛宦。為了演示目的忘闻,我們將只實(shí)現(xiàn)audioRecorderDidFinishRecording(_:successful:)方法來(lái)處理記錄的完成.
代碼如下 :?
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
? ? ? ? if flag {
? ? ? ? ? ? let alertMessage = UIAlertController(title: "Finish Recording", message: "Successfully recorded the audio!", preferredStyle: .alert)
? ? ? ? ? ? alertMessage.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
? ? ? ? ? ? present(alertMessage, animated: true, completion: nil)
? ? ? ? }
? ? }
實(shí)現(xiàn) AVAudioPlayerDelegate 協(xié)議
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
? ? ? ? playButton.isSelected = false
? ? ? ? let alertMessage = UIAlertController(title: "Finish Playing", message: "Finish playing the recording!", preferredStyle: .alert)
? ? ? ? alertMessage.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
? ? ? ? present(alertMessage, animated: true, completion: nil)
? ? }
最后實(shí)現(xiàn) 定時(shí)器?
private var timer: Timer?
private var elapsedTimeInSecond: Int = 0
func startTimer() {
? ? timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { (timer) in
? ? ? ? self.elapsedTimeInSecond += 1
? ? ? ? self.updateTimeLabel()
? ? })
}
func pauseTimer() {
? ? timer?.invalidate()
}
func resetTimer() {
timer?.invalidate()
? ? elapsedTimeInSecond = 0
? ? updateTimeLabel()
}
func updateTimeLabel() {
? ? let seconds = elapsedTimeInSecond % 60
? ? let minutes = (elapsedTimeInSecond / 60) % 60
? ? timeLabel.text = String(format: "%02d:%02d", minutes, seconds)
}