iOS 10 的 Speech 框架實現語音識別 (Swift)

什麼都不說先上效果緩存

輸入圖片說明

早在2011年iPhone4s 的上,iOS 5系統就有了語音識別. 但有如下缺陷安全

  • 須要- 彈出鍵盤
  • 只支持實時語音
  • 沒法自定義錄音
  • 單一的輸出結果
  • 不開放

在 2016 年的 WWDC 上,Apple 終於開放了語音識別 Speech Recognition API,那就是 Speech 框架。事實上,Siri 的語音識別正是由 Speech Kit 提供支持。服務器

輸入圖片說明

  • 超過50種語言得到支持
  • 任何運行iOS10的設備均可用
  • 加入用戶受權使其更安全
  • 能夠轉化音頻文件和實時語音 輸入圖片說明

下面經過一個語音轉換爲文本介紹Speech 框架的使用app

##界面設計 首先,讓咱們來建立一個 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 協議。ide

##用戶權限 在使用 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 的麥克風。this

處理語音識別設計

// 能夠將識別請求的結果返回給你,它帶來了極大的便利,必要時,能夠取消或中止任務。
    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

歡迎打賞 點贊,收藏,關注博主

相關文章
相關標籤/搜索