文档章节

iOS 10 的 Speech 框架实现语音识别 (Swift)

她吃西红柿
 她吃西红柿
发布于 2016/12/05 15:44
字数 1911
阅读 1260
收藏 6

什么都不说先上效果

输入图片说明

早在2011年iPhone4s 的上,iOS 5系统就有了语音识别. 但有以下缺陷

  • 需要- 弹出键盘
  • 只支持实时语音
  • 无法自定义录音
  • 单一的输出结果
  • 不开放

在 2016 年的 WWDC 上,Apple 终于开放了语音识别 Speech Recognition API,那就是 Speech 框架。事实上,Siri 的语音识别正是由 Speech Kit 提供支持。

输入图片说明

  • 超过50种语言获得支持
  • 任何运行iOS10的设备都可用
  • 加入用户授权使其更安全
  • 可以转化音频文件和实时语音 输入图片说明

下面通过一个语音转换为文本介绍Speech 框架的使用

##界面设计 首先,让我们来创建一个 iOS Single View Application 工程。然后在 Main.storyboard 上添加 UILabel用于标题 UITextView用于显示识别内容 UIButton 用于触发 输入图片说明 下一步,连线 textView变量,Button变量和事件

    @IBOutlet weak var textView: UITextView!

    @IBOutlet weak var speakerBtn: UIButton!
    @IBAction func speakAction(_ sender: Any) {
        
    }

##使用 Speech 框架 import这个框架,并遵循 SFSpeechRecognizerDelegate 协议。

##用户权限 在使用 Speech 框架进行语音识别之前,你必须先请求用户许可,原因是识别不仅发生在 iOS 设备本地,还需要依赖 Apple 的服务器。具体来说,所有音频数据都会被传输到苹果后台进行处理。因此需要获取用户的权限,其中包括用户必须允许应用使用的音频输入和语音识别权限。

//用于apple语言识别的变量
    private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "zh-CN"))
 // MARK: - *** 获取用户权限 ***
    func authRequest(){
        
        speakerBtn.isEnabled = false
        
        speechRecognizer?.delegate = self
        
        SFSpeechRecognizer.requestAuthorization { (authStatus) in
            var isBtnEndable = false
            
            switch authStatus{
            case.authorized:
                isBtnEndable = true
            case .denied:
                isBtnEndable = false
                print("User denied access to speech recognition")
                
            case .restricted:
                isBtnEndable = false
                print("Speech recognition restricted on this device")
                
            case .notDetermined:
                isBtnEndable = false
                
                
                
            }
            
            OperationQueue.main.addOperation {
                self.speakerBtn.isEnabled = isBtnEndable
            }
            
        }
    
    }
  • 创建一个区域标志符 (locale identifier) 为 zh-CN 的 SFSpeechRecognizer 实例,这时候语音识别就会知道用户录入的语种。简单说,这就是语音识别的处理对象。
  • 在语音识别被激活之前,默认设置麦克风按钮为禁用状态。
  • 然后,将语音识别的 delegate 设置为 ViewController 中的 self。
  • 之后,就到了请求语音识别权限的阶段了,这时我们通过调用 SFSpeechRecognizer.requestAuthorization 来达到目的。
  • 最后,检查验证状态,如果得到了授权,则启用麦克风按钮。否则,打印错误信息,继续禁用麦克风按钮。 你可能会认为,现在我们启动应用将会看到一个授权提示框,很遗憾你错了。运行应用带来的是崩溃。你可能会想问,这是为什么?

提供授权信息

Apple 要求应用为所有请求的权限提供自定义消息,对于语音权限的情况,我们必须为两个行为请求授权: 麦克风的使用 语音的识别 要自定义消息,你需要在 info.plist 文件中定义这些消息。 让我们打开 info.plist 文件的源代码。方法是在 info.plist 上点击右键。然后选择 Open As > Source Code。最后,复制下面的 XML 代码并将它们插入到 </dict> 标签前。

<key>NSMicrophoneUsageDescription</key>  
<string>麦克风输入请求信息</string>
 
<key>NSSpeechRecognitionUsageDescription</key>
<string>语音识别请求信息</string>

注意:务必在IPhone真机上运行测试,iOS 模拟器并不会连接 Mac 的麦克风。

处理语音识别

    // 可以将识别请求的结果返回给你,它带来了极大的便利,必要时,可以取消或停止任务。
    private var recognitionTask: SFSpeechRecognitionTask?
    //对象用于处理语音识别请求,为语音识别提供音频输入
    private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
    
    // 音频引擎 用于进行音频输入
    private let audioEngine = AVAudioEngine()
    
// MARK: - *** 处理语音识别 ***
    func startRecording(){
    
        if recognitionTask != nil{
            recognitionTask?.cancel()
            recognitionTask = nil
 
        }
    let audioSession = AVAudioSession.sharedInstance()
        
        do {
            try audioSession.setCategory(AVAudioSessionCategoryRecord)
            try audioSession.setMode(AVAudioSessionModeMeasurement)
            try audioSession.setActive(true, with: .notifyOthersOnDeactivation)

        
        }catch{
        
        fatalError("会话创建失败")
        }
        
        recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
        
        guard let inputNode = audioEngine.inputNode else {
            fatalError("音频引擎 没有输入节点")
        }
        
        guard let recognitionRequest = recognitionRequest else {
            fatalError("创建音频缓存失败")
        }
        //结果报告
        recognitionRequest.shouldReportPartialResults = true
        
        //开启授权任务
        recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
            
            var isFinal = false
            
            if result != nil {
                
                self.textView.text = result?.bestTranscription.formattedString
                isFinal = (result?.isFinal)!
            }
            
            if error != nil || isFinal {
                self.audioEngine.stop()
                inputNode.removeTap(onBus: 0)
                
                self.recognitionRequest = nil
                self.recognitionTask = nil
                
                self.speakerBtn.isEnabled = true
            }
        })
        let recordingFormat = inputNode.outputFormat(forBus: 0)
        inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
            self.recognitionRequest?.append(buffer)
            
        }
        audioEngine.prepare()
        
        do {
            try audioEngine.start()
        } catch {
            print("audioEngine couldn't start because of an error.")
        }

    }



}
  • 检查 recognitionTask 的运行状态,如果正在运行,取消任务。
  • 创建一个 AVAudioSession 对象为音频录制做准备。这里我们将录音分类设置为 Record,模式设为 Measurement,然后启动。注意,设置这些属性有可能会抛出异常,因此你必须将其置于 try catch 语句中。
  • 实例化 recognitionResquest。创建 SFSpeechAudioBufferRecognitionRequest 对象,然后我们就可以利用它将音频数据传输到 Apple 的服务器。
  • 检查 audioEngine (你的设备)是否支持音频输入以录音。如果不支持,报一个 fatal error。
  • 检查 recognitionRequest 对象是否已被实例化,并且值不为 nil。
  • 告诉 recognitionRequest 不要等到录音完成才发送请求,而是在用户说话时一部分一部分发送语音识别数据。
  • 在调用 speechRecognizer 的 recognitionTask 函数时开始识别。该函数有一个完成回调函数,每次识别引擎收到输入时都会调用它,在修改当前识别结果,亦或是取消或停止时,返回一个最终记录。
  • 定义一个 boolean 变量来表示识别是否已结束。
  • 倘若结果非空,则设置 textView.text 属性为结果中的最佳记录。同时若为最终结果,将 isFinal 置为 true。
  • 如果请求没有错误或已经收到最终结果,停止 audioEngine (音频输入),recognitionRequest 和 recognitionTask。同时,将开始录音按钮的状态切换为可用。
  • 向 recognitionRequest 添加一个音频输入。值得留意的是,在 recognitionTask 启动后再添加音频输入完全没有问题。Speech 框架会在添加了音频输入之后立即开始识别任务。
  • 将 audioEngine 设为准备就绪状态,并启动引擎。

触发语音识别

在创建语音识别任务时,我们首先得确保语音识别的可用性,需要实现delegate 方法。如果语音识别不可用,或是改变了状态,应随之设置 按钮的enable ,我们通过扩展来实现代理

// MARK: - *** delegate ***
//这个方法会在按钮的可用性改变时被调用。如果语音识别可用,录音按钮也将被启用。
extension ViewController: SFSpeechRecognizerDelegate{

    func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
        if available {
            speakerBtn.isEnabled = true
        } else {
            speakerBtn.isEnabled = false
        }
    }

    }

最后,我们还需要更新一下 按钮的点击方法:

@IBAction func speakAction(_ sender: Any) {
       if audioEngine.isRunning {
           audioEngine.stop()
           recognitionRequest?.endAudio()
           speakerBtn.isEnabled = false
           speakerBtn.setTitle("开始说话", for: .normal)
       } else {
           startRecording()
           speakerBtn.setTitle("说完了", for: .normal)
       }
   }

输入图片说明

Apple忠告

  • 确保使用语音之别之前,通过UI界面告知用户
  • 在涉及密码或者敏感信息时,请勿使用
  • 在你操作识别结果之前,请先把结果展示给用户
  • Apple 对每台设备的识别有限制。详情未知,不过你可以尝试联系 Apple 获得更多信息。
  • Apple 对每个应用的识别也有限制。
  • 如果你总是遭遇限制,务必联系 Apple,他们或许可以解决这个问题。
  • 语音识别会消耗不少电量和流量。
  • 语音识别每次只能持续大概一分钟。

参考 WWDC 2016 - Session 509 - iOS

欢迎打赏 点赞,收藏,关注博主

© 著作权归作者所有

她吃西红柿
粉丝 43
博文 52
码字总数 25812
作品 0
杭州
iOS工程师
私信 提问
【iOS10】iOS10新特性

1 Swift 3.0 优化了许多API,简化了接口,去掉了不必要的单词等,比如下面这几个例子: a b c d 2 SiriKit 通过官方文档我们可以看到SiriKit框架支持的六类服务分别是: 语音和视频通话 发送...

魔笛GNR
2016/09/13
168
0
OSChina 技术专题之 Swift 苹果全新开发语言

Swift 是苹果新推出的编程语言,专门针对 OS X 和 iOS 的应用开发。Swift 在各个方面优于 Objective-C,也不会有那么多复杂的符号和表达式。同时,Swift 更加快速、便利、高效、安全。除此之...

OSC编辑部
2014/11/10
4.2K
4
iOS游戏框架Sprite Kit基础教程第1章编写第一个Sprite Kit程序

iOS游戏框架Sprite Kit基础教程第1章编写第一个Sprite Kit程序 程序是为了实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合。本章将以编写第一个Sprite Kit程序为主线,为开发...

大学霸
2014/12/23
436
0
斯坦福大学的 Swift 教程又来了:还是免费的

美国斯坦福大学(Stanford University)曾在去年 4 月份推出过 iOS 编程教学课程 Developing iOS 9 Apps with Swift,这项课程上架 iTunes U 之后广受编程爱好者的好评。因为它不仅内容齐全,...

局长
2017/02/20
3.2K
11
苹果账号的分类以及注册免费苹果账号

苹果账号的分类以及注册免费苹果账号 苹果账号的分类 在苹果公司注册苹果账号,就可以成为开发成员。开发成员一共可以分为四种,如表1-2所示。 表1-2 苹果账号的成员 以下是注册一个免费的苹...

大学霸
2015/01/04
278
1

没有更多内容

加载失败,请刷新页面

加载更多

JVM -- Java堆结构及对象分代

Hello,今天记录下 Java虚拟机中的其中一个重点知识 --> Java堆。 一起学习,一起进步。继续沉淀,慢慢强大。希望这文章对您有帮助。若有写的不好的地方,欢迎评论给建议哈! 初写博客不久,...

猫狗熊
15分钟前
2
0
elastic-job的使用

概述: 公司用了elastic-job来执行定时任务和管理定时任务,所以最近研究了一下写了个demo,由于我是把zookeeper部署在了docker上的,所以这里简单介绍下docke的基础命令。 1、Docker基础命令...

你个小机灵鬼
16分钟前
3
0
Cadence Allegro 中skill应用教程:让代码替我们打工

SKILL语言是Candence提供给用户的一个开发接口,利用其本身提供的接口函数和SKILL语言完成自动化操作的功能。 怎么查看SKILL: 1.可以直接用写字板打开进行编辑或看功能说明。 2.想自己写或改...

demyar
16分钟前
2
0
如何看待技术债务

关于技术债务,做开发的同学对如下场景应该不陌生: 为了敢项目进度,详细设计、单元测试等过程就不写了,以后补 需求变化万千,原本架构设计无法满足新的需求,可是又不想动架构,于是绕过架...

嘿嘿嘿IT
18分钟前
2
0
深入理解 web 协议(一)- http 包体传输

本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/WlT8070LlrnSODFRDwZsUQ 作者:吴越 开坑这个系列的原因,主要是在大前端学习的过程中遇到了不少跟web协议有关的问...

vivo互联网技术
18分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部