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

056.jpg

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

设计App UIswift

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

5A036019-64F0-4038-9D10-5CF14E8F7608.png

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

1app

2框架

3ide

@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代码而后在77.png标记前插入这段代码。

78.png

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

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

  • NSSpeechRecognitionUsageDescription – 语音识别受权的自定义信息

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

E99E1089-66BF-4E06-9F72-7864FD838383.png

注意:若是稍后在工程运行完成时尚未看到语音输入受权框,那是由于你是在模拟器上运行的程序。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”按钮。去说些什么吧!

98.png

注意:

  1. 苹果公司对每一个设备的识别功能都有限制。具体的限制并不知道,可是你能够联系苹果公司了解更多信息。

  2. 苹果公司对每一个app也有识别功能限制。

  3. 若是你常常遇到限制,请必定联系苹果公司,他们应该能够解决问题。

  4. 语音识别会很耗电以及会使用不少数据。

  5. 语音识别一次只持续大概一分钟时间。

总结

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

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

做为参考,你能够在这里查看Github完整工程

相关文章
相关标签/搜索