在2016 WWDC大會上,Apple公司發(fā)布了iOS10,并介紹了一個很好的語音識別的API,那就是Speech framework。事實(shí)上,這個Speech Kit就是Siri用來做語音識別的框架。如今已經(jīng)有一些可用的第三方語音識別框架,但是它們要么太貴要么不好。現(xiàn)在就讓我們開始創(chuàng)建一個使用Speech Kit來進(jìn)行語音轉(zhuǎn)文字的類似Siri的app。
先從創(chuàng)建一個新的命名為SpeechToTextDemo的單視圖工程開始。接下來,到 Main.storyboard 中添加一個 UILabel,一個 UITextView, 和一個 UIButton,
你的storyboard應(yīng)該看起來如下圖:
接下來在 ViewController.swift文件中為UITextView 和UIButton 定義outlet變量。在這個demo當(dāng)中,我設(shè)置UITextView 的名稱為“textView”,UIButton的名稱為“microphoneButton”。然后創(chuàng)建一個當(dāng)microphone按鈕被點(diǎn)擊時會觸發(fā)的空的按鈕執(zhí)行方法
@IBAction func microphoneTapped(_ sender: AnyObject) {
}
使用Speech Framework
為了能使用Speech framework, 你必須首先導(dǎo)入它然后遵循 SFSpeechRecognizerDelegate 協(xié)議。因此讓我們導(dǎo)入這個框架,然后在 ViewController 文件中加上它的協(xié)議。現(xiàn)在你的 ViewController.swift 文件應(yīng)該如下所示:
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) {
}
}
用戶授權(quán)
在使用speech framework做語音識別之前,你必須首先得到用戶的允許,因?yàn)椴粌H僅只有本地的ios設(shè)備會進(jìn)行識別,蘋果的服務(wù)器也會識別。所有的語音數(shù)據(jù)都會被傳遞到蘋果的后臺進(jìn)行處理。因此,獲取用戶授權(quán)是強(qiáng)制必須的。
讓我們在 viewDidLoad 方法里授權(quán)語音識別。用戶必須允許app使用話筒和語音識別。首先,聲明一個speechRecognizer變量:
private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US")) //1
然后如下更新 viewDidLoad 方法:
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
}
}
}
首先,創(chuàng)建一個帶有標(biāo)識符en-US 的 SFSpeechRecognizer實(shí)例,這樣語音識別API就能知道用戶說的是哪一種語言。這個實(shí)例就是處理語音識別的對象。
我們默認(rèn)讓microphone按鈕失效直到語音識別功能被激活。
接下來,把語音識別的代理設(shè)置為 self 也就是我們的ViewController.
之后,我們必須通過調(diào)用SFSpeechRecognizer.requestAuthorization方法來請求語音識別的授權(quán)。
最后,檢查驗(yàn)證的狀態(tài)。如果被授權(quán)了,讓microphone按鈕有效。如果沒有,打印錯誤信息然后讓microphone按鈕失效。
現(xiàn)在如果你認(rèn)為app跑起來之后你會看到一個授權(quán)彈出窗口,那你就錯了。如果運(yùn)行,app會崩潰。好吧,既然知道結(jié)果為什么還要問呢?看看下面解決方法。
提供授權(quán)消息
iOS10發(fā)布后蘋果要求app里所有的授權(quán)都要一個自定義的信息。例如語音授權(quán),我們必須請求2個授權(quán):
麥克風(fēng)使用權(quán)
語音識別
為了自定義信息,你必須在info.plist 配置文件里提供這些自定義消息。
讓我們打開 info.plist配置文件的源代碼。首先,右鍵點(diǎn)擊 info.plist。然后選擇Open As > Source Code。最后,拷貝下面的XML代碼然后在</dict>標(biāo)記前插入這段代碼(提示語言可以自定義)。
<key>NSMicrophoneUsageDescription</key>
<string>獲取麥克風(fēng)權(quán)限!!</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>獲取語音識別權(quán)限</string>
2.png現(xiàn)在你已經(jīng)在info.plist文件里添加了兩個鍵值:
(1) NSMicrophoneUsageDescription -為獲取麥克風(fēng)語音輸入授權(quán)的自定義消息。注意這個語音輸入授權(quán)僅僅只會在用戶點(diǎn)擊microphone按鈕時發(fā)生。
(2) NSSpeechRecognitionUsageDescription – 語音識別授權(quán)的自定義信息
可以自行更改這些消息的內(nèi)容。現(xiàn)在點(diǎn)擊Run按鈕,你應(yīng)該可以編譯和成功運(yùn)行app了,不會報(bào)任何錯誤。
注意:如果稍后在工程運(yùn)行完成時還沒有看到語音輸入授權(quán)框,那是因?yàn)槟闶窃谀M器上運(yùn)行的程序。iOS模擬器沒有權(quán)限進(jìn)入你Mac電腦的麥克風(fēng)。
處理語音識別
現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了用戶授權(quán),我們現(xiàn)在去實(shí)現(xiàn)語音識別功能。先從在 ViewController里定義下面的對象開始:
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
private let audioEngine = AVAudioEngine()
recognitionRequest對象處理了語音識別請求。它給語音識別提供了語音輸入。
reconition task對象告訴你語音識別對象的結(jié)果。擁有這個對象很方便因?yàn)槟憧梢杂盟鼊h除或者中斷任務(wù)。
audioEngine是你的語音引擎。它負(fù)責(zé)提供你的語音輸入。
接下來,創(chuàng)建一個新的方法名叫 startRecording()。
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 {
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按鈕被點(diǎn)擊時調(diào)用。它主要功能是開啟語音識別然后聆聽你的麥克風(fēng)。我們一行行分析上面的代碼:
3-6行 – 檢查 recognitionTask 是否在運(yùn)行。如果在就取消任務(wù)和識別。
8-15行 – 創(chuàng)建一個 AVAudioSession來為記錄語音做準(zhǔn)備。在這里我們設(shè)置session的類別為recording,模式為measurement,然后激活它。注意設(shè)置這些屬性有可能會拋出異常,因此你必須把他們放入try catch語句里面。
17行 – 實(shí)例化recognitionRequest。在這里我們創(chuàng)建了SFSpeechAudioBufferRecognitionRequest對象。稍后我們利用它把語音數(shù)據(jù)傳到蘋果后臺。
19-21行 – 檢查 audioEngine(你的設(shè)備)是否有做錄音功能作為語音輸入。如果沒有,我們就報(bào)告一個錯誤。
23-25行 – 檢查recognitionRequest對象是否被實(shí)例化和不是nil。
27行– 當(dāng)用戶說話的時候讓recognitionRequest報(bào)告語音識別的部分結(jié)果 。
29行 – 調(diào)用 speechRecognizer的recognitionTask 方法來開啟語音識別。這個方法有一個completion handler回調(diào)。這個回調(diào)每次都會在識別引擎收到輸入的時候,完善了當(dāng)前識別的信息時候,或者被刪除或者停止的時候被調(diào)用,最后會返回一個最終的文本。
31行 – 定義一個布爾值決定識別是否已經(jīng)結(jié)束。
35行 – 如果結(jié)果 result 不是nil, 把 textView.text 的值設(shè)置為我們的最優(yōu)文本。如果結(jié)果是最終結(jié)果,設(shè)置 isFinal為true。
39-47行 – 如果沒有錯誤或者結(jié)果是最終結(jié)果,停止 audioEngine(語音輸入)并且停止 recognitionRequest 和 recognitionTask.同時,使Start Recording按鈕有效。
50-53行 – 向 recognitionRequest增加一個語音輸入。注意在開始了recognitionTask之后增加語音輸入是OK的。Speech Framework 會在語音輸入被加入的同時就開始進(jìn)行解析識別。
55行 – 準(zhǔn)備并且開始audioEngine。
觸發(fā)語音識別
我們需要保證當(dāng)創(chuàng)建一個語音識別任務(wù)的時候語音識別功能是可用的,因此我們必須給ViewController添加一個代理方法。如果語音輸入不可用或者改變了它的狀態(tài),那么 microphoneButton.enable屬性就要被設(shè)置。針對這種情況,我們實(shí)現(xiàn)了SFSpeechRecognizerDelegate 協(xié)議的 availabilityDidChange 方法。實(shí)現(xiàn)內(nèi)容看下面:
func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
if available {
microphoneButton.isEnabled = true
} else {
microphoneButton.isEnabled = false
}
}
這個方法會在可用性狀態(tài)改變時被調(diào)用。如果語音識別可用,那么記錄按鈕record會被設(shè)為可用狀態(tài)。
最后一件事就是我們必須更新響應(yīng)方法microphoneTapped(sender:):
@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應(yīng)該停止 audioEngine, 中止向recognitionRequest輸入音頻,讓microphoneButton按鈕不可用,并且設(shè)置按鈕的標(biāo)題為 “Start Recording”
如果 audioEngine 正在工作,app應(yīng)該調(diào)用 startRecording() 并且設(shè)置按鈕的標(biāo)題為 “Stop Recording”。
非常好!現(xiàn)在可以準(zhǔn)備測試app了。把a(bǔ)pp部署到一個iOS10的設(shè)備,然后點(diǎn)擊“Start Recording”按鈕。去說些什么吧!
注意:
1.蘋果公司對每個設(shè)備的識別功能都有限制。具體的限制并不知道,但是你可以
2.聯(lián)系蘋果公司了解更多信息。
3.蘋果公司對每個app也有識別功能限制。
4.如果你經(jīng)常遇到限制,請一定聯(lián)系蘋果公司,他們應(yīng)該可以解決問題。
5.語音識別會很耗電以及會使用很多數(shù)據(jù)。
6.語音識別一次只持續(xù)大概一分鐘時間。
總結(jié)
在這個教程中,你學(xué)習(xí)到了怎樣好好的利用蘋果公司開放給開發(fā)者的驚人的新語言API,用于語音識別并且轉(zhuǎn)換到文本。Speech framework 使用了跟Siri相同的語音識別框架。這是一個相對小的API。但是,它非常強(qiáng)大可以讓開發(fā)者們開發(fā)非凡的應(yīng)用比如轉(zhuǎn)換一個語音文件到文本文字。
最后,完整的項(xiàng)目地址請點(diǎn)擊這里
文/real潘(簡書作者)