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完整工程

相關文章
相關標籤/搜索