Intermediate iOS 11 Programming with Swift(十): 音频录制和回放

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

10.1

iOS SDK提供了多种框架,可以让你在应用中使用声音。你可以用来播放和录制音频文件的框架之一是AV Foundation框架。在这一章,我将带你通过基本的框架,并向你展示如何管理音频回放和录音.

AV Foundation为开发者提供了必要的api来处理iOS上的音频。在这个demo中,我们主要使用框架的这两个类:

AVAudioPlayer - 把它想象成一个播放声音文件的音频播放器。通过使用播放器,您可以在iOS中播放任何持续时间和音频格式的声音

AVAudioRecorder - 一个用来记录音频的录音机。


Demo

为了了解如何使用API,我们将构建一个简单的音频应用程序,允许用户录制和播放音频。我们的主要关注点是演示AVFoundation框架,这样应用程序的用户界面就会非常简单。

首先,使用 Single View Application 模板创建一个应用程序,并将其命名为RecordPro. 您可以自己设计一个类似于图10.1的用户界面。然而,要让您免于设置用户界面和自定义类,您可以从这里下载模板!  界面很简单, 只有3个按钮: 录音, 暂停, 和播放. 

它也有一个计时器来显示录制期间经过的时间。这些按钮已经连接到RecordProController类中相应的动作方法,而RecordProController类是UIViewController的一个子类. 

10.1 demo

在我们进入到实现之前,让我给你一个更好的关于演示应用程序如何工作的想法:

 

* 当用户点击录制按钮时,应用程序启动计时器并开始录制音频。然后,记录按钮被暂停按钮取代。如果用户点击暂停按钮,应用程序将暂停录制,直到用户再次点击按钮。在编码方面,它调用记录操 作方法

* 当用户点击停止按钮时,应用程序会停止录制。我已经把按钮和RecordProController中的stop动作方法连接起来了。

* 要播放录音,用户可以点击播放按钮,这是与播放方法相关联的。


AVAudioRecorder

AVAudioRecorder类的AV Foundation框架允许您的应用程序提供音频录制功能。在iOS中,录制的音频来自iOS设备的内置麦克风或耳机麦克风。这些设备包括iPhone、iPad或iPod touch。

首先,让我们看看如何使用AVAudioRecorder类来记录音频。与SDK中的大多数api一样,AVAudioRecorder使用了delegate模式。您可以为音频记录器实现delegate对象,以响应音频中断并完成录制。AVAudioRecorder对象的委托必须采用AVAudioRecorderDelegate协议. 

可以写个Extension:

extension RecorderProController: AVAudioRecorderDelegate { 

记得导入AV Foundation 框架 !   

声明 AVAudioRecorder 和 AVAudioPlayer的实例变量

var audioRecorder:  AVAudioRecorder ?

var audioPlayer: AVAudioPlayer?

让我们先关注AVAudioRecorder。稍后我们将使用audioPlayer变量。AVAudioRecorder类提供了一种在应用程序中记录声音的简单方法。

 * 指定一个声音文件URL

 * 设置一个音频会话

 * 配置录音机的初始状态

我们将创建一个名为configure()的私有方法来进行设置。将代码插入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)

    }

}

接下来看一看 录音按钮的 实现

当用户点击录制按钮时,应用程序将开始录制。录制按钮将被更改为暂停按钮。如果用户点击暂停按钮,应用程序将暂停音频录制,直到再次点击按钮。当用户点击“停止”按钮时,录音就会停止

代码如下 :

@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

}



当然别忘 在 info.plist 里面 加入访问权限  

访问权限



暂停按钮的实现  

当用户点击停止按钮时,会调用停止动作方法。这个方法很简单。我们首先重置按钮的状态,然后调用AVAudioRecorder对象的stop方法来停止录制。最后,我们关闭音频会话

代码如下 : 

@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)

    }

}

实现 AVAudioRecorderDelegate 协议

你可以使用AVAudioRecorderDelegate协议来处理音频中断(比如,录音期间的电话)以及完成录音。在本例中,RecordProController是委托。AVAudioRecorderDelegate协议中定义的方法是可选的。为了演示目的,我们将只实现audioRecorderDidFinishRecording(_:successful:)方法来处理记录的完成.

代码如下 : 

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)

        }

    }

实现 AVAudioPlayerDelegate 协议

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)

    }


最后实现 定时器 

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 截图 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容