iOS 10中如何搭建一个语音转文字框架

在2016WWDC大会上,Apple公司介绍了一个很好的语音识别的API,那就是Speech framework。事实上,这个Speech Kit就是Siri用来做语音识别的框架。如今已经有一些可用的语音识别框架,但是它们要么太贵要么不好。在今天的教程里面,我会教你怎样创建一个使用Speech Kit来进行语音转文字的类似Siri的app。

设计App UI

前提:你需要Xcode 8 beta版本和一个运行iOS 10 beta系统版本的iOS 设备。
先从创建一个新的命名为SpeechToTextDemo的单视图工程开始。接下来,到 Main.storyboard 中添加一个 UILabel,一个 UITextView, 和一个 UIButton,你的storyboard应该看起来如下图:

接下来在 ViewController.swift文件中为UITextView 和UIButton 定义outlet变量。在这个demo当中,我设置UITextView 的名称为“textView”,UIButton的名称为“microphoneButton”。然后创建一个当microphone按钮被点击时会触发的空的按钮执行方法。


1

2

3

@IBAction func microphoneTapped(_ sender: AnyObject) {

}

如果你不想从创建最原始工程开始,你可以在 在这里下载原始工程 然后继续下面的教学指导。

使用Speech Framework

为了能使用Speech framework, 你必须首先导入它然后遵循 SFSpeechRecognizerDelegate 协议。因此让我们导入这个框架,然后在 ViewController 文件中加上它的协议。现在你的 ViewController.swift 文件应该如下图所示:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import UIKit

import Speech

 

class ViewController: UIViewController, SFSpeechRecognizerDelegate {

    

    

    @IBOutlet weak var textView: UITextView!

    @IBOutlet weak var microphoneButton: UIButton!

    

    override func viewDidLoad() {

        super.viewDidLoad()

        

    }

 

    @IBAction func microphoneTapped(_ sender: AnyObject) {

 

    }

 

}

用户授权

在使用speech framework做语音识别之前,你必须首先得到用户的允许,因为不仅仅只有本地的ios设备会进行识别,苹果的服务器也会识别。所有的语音数据都会被传递到苹果的后台进行处理。因此,获取用户授权是强制必须的。

让我们在 viewDidLoad 方法里授权语音识别。用户必须允许app使用话筒和语音识别。首先,声明一个speechRecognizer变量:


1

private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US"))  //1

然后如下图更新 viewDidLoad 方法:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

override func viewDidLoad() {

    super.viewDidLoad()

   

    microphoneButton.isEnabled = false  //2

   

    speechRecognizer.delegate = self  //3

   

    SFSpeechRecognizer.requestAuthorization { (authStatus) in  //4

       

        var isButtonEnabled = false

       

        switch authStatus {  //5

        case .authorized:

            isButtonEnabled = true

           

        case .denied:

            isButtonEnabled = false

            print("User denied access to speech recognition")

           

        case .restricted:

            isButtonEnabled = false

            print("Speech recognition restricted on this device")

           

        case .notDetermined:

            isButtonEnabled = false

            print("Speech recognition not yet authorized")

        }

       

        OperationQueue.main.addOperation() {

            self.microphoneButton.isEnabled = isButtonEnabled

        }

    }

}

  1. 首先,我们创建一个带有标识符en-US 的 SFSpeechRecognizer实例,这样语音识别API就能知道用户说的是哪一种语言。这个实例就是处理语音识别的对象。
  2. 我们默认让microphone按钮失效直到语音识别功能被激活。
  3. 接下来,把语音识别的代理设置为 self 也就是我们的ViewController.
  4. 之后,我们必须通过调用SFSpeechRecognizer.requestAuthorization方法来请求语音识别的授权。
  5. 最后,检查验证的状态。如果被授权了,让microphone按钮有效。如果没有,打印错误信息然后让microphone按钮失效。

现在如果你认为app跑起来之后你会看到一个授权弹出窗口,那你就错了。如果运行,app会崩溃。好吧,既然知道结果为什么还要问呢?(别打我),看看下面解决方法。

提供授权消息

苹果要求app里所有的授权都要一个自定义的信息。例如语音授权,我们必须请求2个授权:

  1. 麦克风使用权。
  2. 语音识别。

为了自定义信息,你必须在info.plist 配置文件里提供这些自定义消息。

让我们打开 info.plist配置文件的源代码。首先,右键点击 info.plist。然后选择Open As > Source Code。最后,拷贝下面的XML代码然后在标记前插入这段代码。

现在你已经在info.plist文件里添加了两个键值:

  • NSMicrophoneUsageDescription -为获取麦克风语音输入授权的自定义消息。注意这个语音输入授权仅仅只会在用户点击microphone按钮时发生。
  • NSSpeechRecognitionUsageDescription – 语音识别授权的自定义信息

可以自行更改这些消息的内容。现在点击Run按钮,你应该可以编译和成功运行app了,不会报任何错误。

注意:如果稍后在工程运行完成时还没有看到语音输入授权框,那是因为你是在模拟器上运行的程序。iOS模拟器没有权限进入你Mac电脑的麦克风。

处理语音识别

现在我们已经实现了用户授权,我们现在去实现语音识别功能。先从在 ViewController里定义下面的对象开始:


1

2

3

private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?

private var recognitionTask: SFSpeechRecognitionTask?

private let audioEngine = AVAudioEngine()

  1. recognitionRequest对象处理了语音识别请求。它给语音识别提供了语音输入。
  2. reconition task对象告诉你语音识别对象的结果。拥有这个对象很方便因为你可以用它删除或者中断任务。
  3. audioEngine是你的语音引擎。它负责提供你的语音输入。

接下来,创建一个新的方法名叫 startRecording()。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

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(truewith: .notifyOthersOnDeactivation)

    catch {

        print("audioSession properties weren‘t set because of an error.")

    }

    

    recognitionRequest = SFSpeechAudioBufferRecognitionRequest()

    

    guard let inputNode = audioEngine.inputNode else {

        fatalError("Audio engine has no input node")

    }

    

    guard let recognitionRequest = recognitionRequest else {

        fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")

    }

    

    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.microphoneButton.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.")

    }

    

    textView.text = "Say something, I‘m listening!"

    

}

这个方法会在Start Recording按钮被点击时调用。它主要功能是开启语音识别然后聆听你的麦克风。我们一行行分析上面的代码:

  • 3-6行 – 检查 recognitionTask 是否在运行。如果在就取消任务和识别。
  • 8-15行 – 创建一个 AVAudioSession来为记录语音做准备。在这里我们设置session的类别为recording,模式为measurement,然后激活它。注意设置这些属性有可能会抛出异常,因此你必须把他们放入try catch语句里面。
  • 17行 – 实例化recognitionRequest。在这里我们创建了SFSpeechAudioBufferRecognitionRequest对象。稍后我们利用它把语音数据传到苹果后台。
  • 19-21行 – 检查 audioEngine(你的设备)是否有做录音功能作为语音输入。如果没有,我们就报告一个错误。
  • 23-25行 – 检查recognitionRequest对象是否被实例化和不是nil。
  • 27行– 当用户说话的时候让recognitionRequest报告语音识别的部分结果 。
  • 29行 – 调用 speechRecognizer的recognitionTask 方法来开启语音识别。这个方法有一个completion handler回调。这个回调每次都会在识别引擎收到输入的时候,完善了当前识别的信息时候,或者被删除或者停止的时候被调用,最后会返回一个最终的文本。
  • 31行 – 定义一个布尔值决定识别是否已经结束。
  • 35行 – 如果结果 result 不是nil, 把 textView.text 的值设置为我们的最优文本。如果结果是最终结果,设置 isFinal为true。
  • 39-47行 – 如果没有错误或者结果是最终结果,停止 audioEngine(语音输入)并且停止 recognitionRequest 和 recognitionTask.同时,使Start Recording按钮有效。
  • 50-53行 – 向 recognitionRequest增加一个语音输入。注意在开始了recognitionTask之后增加语音输入是OK的。Speech Framework 会在语音输入被加入的同时就开始进行解析识别。
  • 55行 – 准备并且开始audioEngine。

触发语音识别

我们需要保证当创建一个语音识别任务的时候语音识别功能是可用的,因此我们必须给ViewController添加一个代理方法。如果语音输入不可用或者改变了它的状态,那么 microphoneButton.enable属性就要被设置。针对这种情况,我们实现了SFSpeechRecognizerDelegate 协议的 availabilityDidChange 方法。实现内容看下面:


1

2

3

4

5

6

7

func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {

    if available {

        microphoneButton.isEnabled = true

    else {

        microphoneButton.isEnabled = false

    }

}

这个方法会在可用性状态改变时被调用。如果语音识别可用,那么记录按钮record会被设为可用状态。

最后一件事就是我们必须更新响应方法microphoneTapped(sender:):


1

2

3

4

5

6

7

8

9

10

11

@IBAction func microphoneTapped(_ sender: AnyObject) {

    if audioEngine.isRunning {

        audioEngine.stop()

        recognitionRequest?.endAudio()

        microphoneButton.isEnabled = false

        microphoneButton.setTitle("Start Recording"for: .normal)

    else {

        startRecording()

        microphoneButton.setTitle("Stop Recording"for: .normal)

    }

}

在这个方法中,我们必须检查 audioEngine是否正在工作。如果是,app应该停止 audioEngine, 中止向recognitionRequest输入音频,让microphoneButton按钮不可用,并且设置按钮的标题为 “Start Recording”

如果 audioEngine 正在工作,app应该调用 startRecording() 并且设置按钮的标题为 “Stop Recording”。

非常好!现在可以准备测试app了。把app部署到一个iOS10的设备,然后点击“Start Recording”按钮。去说些什么吧!

注意:

  1. 苹果公司对每个设备的识别功能都有限制。具体的限制并不知道,但是你可以联系苹果公司了解更多信息。
  2. 苹果公司对每个app也有识别功能限制。
  3. 如果你经常遇到限制,请一定联系苹果公司,他们应该可以解决问题。
  4. 语音识别会很耗电以及会使用很多数据。
  5. 语音识别一次只持续大概一分钟时间。

总结

在这个教程中,你学习到了怎样好好的利用苹果公司开放给开发者的惊人的新语言API,用于语音识别并且转换到文本。Speech framework 使用了跟Siri相同的语音识别框架。这是一个相对小的API。但是,它非常强大可以让开发者们开发非凡的应用比如转换一个语音文件到文本文字。

我推荐你看WWDC 2016 session 509去获取更多有用信息。希望你喜欢这篇文章并且在探索这个全新API中获得乐趣。

作为参考,你可以在这里查看Github完整工程

时间: 2024-12-28 22:28:06

iOS 10中如何搭建一个语音转文字框架的相关文章

关于 iOS 10 中 ATS 的问题

WWDC 15 提出的 ATS (App Transport Security) 是 Apple 在推进网络通讯安全的一个重要方式.在 iOS 9 和 OS X 10.11 中,默认情况下非 HTTPS 的网络访问是被禁止的.当然,因为这样的推进影响面非常广,作为缓冲,我们可以在 Info.plist 中添加 NSAppTransportSecurity 字典并且将 NSAllowsArbitraryLoads 设置为 YES 来禁用 ATS.相信大家都已经对这个非常熟悉了,因为我自己也维护了一

Android中如何搭建一个WebServer

对于面向对象的程序设计语言而言,继承和多态是两个最基本的概念.Hibernate 的继承映射可以理解持久化类之间的继承关系.例如:人和学生之间的关系.学生继承了人,可以认为学生是一个特殊的人,如果对人进行查询,学生的实例也将被得到. Hibernate支持三种继承映射策略: 使用 subclass 进行映射:将域模型中的每一个实体对象映射到一个独立的表中,也就是说不用在关系数据模型中考虑域模型中的继承关系和多态. 使用 joined-subclass 进行映射: 对于继承关系中的子类使用同一个表

如何搭建一个合理的数值框架

转自:http://www.gameres.com/749634.html 如何搭建一个合理的数值框架? 1.拍脑袋定下一系列的数值设定,完成所有数值框架的设定 2.数值检验,模拟计算重要参数,来确定数值框架是否合理 针对第一个要点,关键在于我们需要定哪些关键数值设定,以及如何来“拍” 针对第二个要点,关键在于我们需要哪些参数来验证数值框架是否合理,以及这些参数是否足以验证数值框架的合理性 一.我提出一些我们可能需要“拍”的数值设定 1.主线成长与付费成长比例 2.主线成长和付费成长分别包含了几

使用idea搭建一个简单的SSM框架:(3)配置spring+mybatis

在此之前请先查看: 使用idea搭建一个简单的SSM框架:(1)使用idea创建maven项目 使用idea搭建一个简单的SSM框架:(2)配置springMVC 1 配置spring和mybatis整合文件 spring和mybatis整合分为三个步骤:(1)配置数据库,(2)配置SqlSessionFactoryBean (来自mybatis-spring),(3)配置MapperScannerConfigurer,该配置就我们通过接口定义的方法,就是mybatis的xml对应的namesp

如何搭建一个简易的Web框架

Web框架本质 什么是Web框架, 如何自己搭建一个简易的Web框架?其实, 只要了解了HTTP协议, 这些问题将引刃而解. 简单的理解:  所有的Web应用本质上就是一个socket服务端, 而用户的浏览器就是一个socket客户端. 用户在浏览器的地址栏输入网址, 敲下回车键便会给服务端发送数据, 这个数据是要遵守统一的规则(格式)的, 这个规则便是HTTP协议. HTTP协议主要规定了客户端和服务器之间的通信格式 浏览器收到的服务器响应的相关信息可以在浏览器调试窗口(F12键开启)的Net

使用 Swift 在 iOS 10 中集成 Siri —— SiriKit 教程

下载 Xcode 8,配置 iOS 10 和 Swift 3 (可选)通过命令行编译 除 非你想使用命令行编译,使用 Swift 3.0 的工具链并不需要对项目做任何改变.如果你想的话,打开 Xcode-beta,然后从顶部菜单栏中选择 Xcode > Preferences,接着选择 Location,在页面的底部,你会看到「Command Line Tool」这行设置,请在这里选择 Xcode 8.0. 现在,在 Terminal 使用命令行找到工程所在的文件夹,调用 xcodebuild

Linux中如何搭建一个ftp服务服务器-超详细

ftp工作是会启动两个通道: 控制通道 , 数据通道 在ftp协议中,控制连接均是由客户端发起的,而数据连接有两种模式:port模式(主动模式)和pasv(被动模式) PORT模式: 在客户端需要接收数据时,ftp_client(大于1024的随机端口)-PORT命令->ftp_server(21)  发送PORT命令,这个PORT命令包含了客户端是用什么端口来接收数据(大于1024的随机端口),在传送数据时,ftp_server将通过自己的TCP 20 端口和PORT中包含的端口建立新的连接来

搭建一个简单的mybatis框架

一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录. 1.Mybatis实现了接口绑定,使用更加方便. 在ibatis2.x中我们需要在DAO的实现类中指定具体对应哪个xml映射文件, 而Myb

iOS中 语音识别功能/语音转文字教程具体解释 韩俊强的博客

原文地址:http://blog.csdn.net/qq_31810357/article/details/51111702 前言:近期研究了一下语音识别,从百度语音识别到讯飞语音识别:首先说一下个人针对两者的看法,讯飞毫无疑问比較专业.识别率也非常高真对语音识别是比較精准的,可是非常多开发人员和我一样期望离线识别,而讯飞离线是收费的:请求次数来讲.两者都能够申请高配额,针对用户较多的差点儿都一样. 基于免费而且支持离线我选择了百度离线语音识别.比較简单,UI设计多一点,以下写一下教程: 1.首