Intermediate iOS 11 Programming with Swift(十): 音頻錄制和回放

本文是Intermediate iOS 11 Programming with Swift 4系列?的 第 十 篇.

10.1

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è)子類.?

10.1 demo

在我們進(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)限??

訪問(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)

}


demo 截圖?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市恋博,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌私恬,老刑警劉巖债沮,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異本鸣,居然都是意外死亡疫衩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門荣德,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)闷煤,“玉大人,你說(shuō)我怎么就攤上這事涮瞻±鹉茫” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵署咽,是天一觀的道長(zhǎng)近顷。 經(jīng)常有香客問(wèn)我,道長(zhǎng)宁否,這世上最難降的妖魔是什么窒升? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮慕匠,結(jié)果婚禮上饱须,老公的妹妹穿的比我還像新娘。我一直安慰自己台谊,他們只是感情好蓉媳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布譬挚。 她就那樣靜靜地躺著,像睡著了一般督怜。 火紅的嫁衣襯著肌膚如雪殴瘦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天号杠,我揣著相機(jī)與錄音蚪腋,去河邊找鬼。 笑死姨蟋,一個(gè)胖子當(dāng)著我的面吹牛屉凯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播眼溶,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼悠砚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了堂飞?” 一聲冷哼從身側(cè)響起灌旧,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绰筛,沒(méi)想到半個(gè)月后枢泰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铝噩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年衡蚂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骏庸。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡毛甲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出具被,到底是詐尸還是另有隱情玻募,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布一姿,位于F島的核電站补箍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏啸蜜。R本人自食惡果不足惜坑雅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衬横。 院中可真熱鬧裹粤,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至矮锈,卻和暖如春霉翔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苞笨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工债朵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瀑凝。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓序芦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親粤咪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谚中,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容