在 2016
年的
WWDC
上,
Apple
介紹了一個十分有用的語音識別
API
,那就是
Speech
框架。事實上,
Siri
的語音識別正是由
Speech Kit
提供支持。就目前來說,可用的語音識別框架并非沒有,但是它們要么太貴,要么不夠好。在本
iOS教程中,我將會向你演示如何使用 Speech Kit
來創(chuàng)建一個像
Siri
一樣的應(yīng)用來進行語音到文本的轉(zhuǎn)換。
應(yīng)用界面設(shè)計
事前準(zhǔn)備: Xcode 8 beta
版和一臺運行
iOS 10 beta
版的設(shè)備
.
首先,讓我們來創(chuàng)建一個 iOS Single View Application
工程,并將其命名為
SpeechToTextDemo
。然后在Main.storyboard
上添加
UILabel
、
UITextView
和
UIButton
各一個。
此時 storyboard
應(yīng)該看起來像這樣:
下一步,在 ViewController.swift
文件中為
UITextView
和
UIButton
定義
outlet
變量,將
UITextView
命名為
“textView”
,
UIButton
命名為
“microphoneButton”
之后,再創(chuàng)建一個空
action
方法來監(jiān)聽麥克風(fēng)按鈕
(microphoneButton)
的點擊事件:
@IBAction func microphoneTapped(_ sender: AnyObject) {
}
使用 Speech
框架
要使用 Speech
框架,第一件要做的事自然是引入這個框架,并遵循
SFSpeechRecognizerDelegate
協(xié)議。所以,我們先引入該框架,然后將它的協(xié)議添加到
ViewController.swift
類中。此時
ViewController.swift
應(yīng)該是這樣的:
import UIKitimport Speech
class ViewController: UIViewController, SFSpeechRecognizerDelegate {
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var microphoneButton: UIButton!
func viewDidLoad() {
sup
overrideer.viewDidLoad()
}
@IBAction func microphoneTapped(_ sender: AnyObject) {
}
}
用戶權(quán)限
在使用 Speech
框架進行語音識別之前,你必須先請求用戶許可,原因是識別不僅發(fā)生在
iOS
設(shè)備本地,還需要依賴
Apple
的服務(wù)器。具體來說,所有音頻數(shù)據(jù)都會被傳輸?shù)教O果后臺進行處理。因此需要獲取用戶的權(quán)限。
我們將在 ViewDidLoad
方法中處理授權(quán)。其中包括用戶必須允許應(yīng)用使用的音頻輸入和語音識別權(quán)限。首先,聲明一個名為
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)建一個區(qū)域標(biāo)志符 (locale identifier)
為
en-US
的
SFSpeechRecognizer
實例,這時候語音識別就會知道用戶錄入的語種。簡單說,這就是語音識別的處理對象。
在語音識別被激活之前,默認設(shè)置麥克風(fēng)按鈕為禁用狀態(tài)。
然后,將語音識別的 delegate
設(shè)置為
ViewController
中的
self
。
之后,就到了請求語音識別權(quán)限的階段了,這時我們通過調(diào)用 SFSpeechRecognizer.requestAuthorization
來達到目的。
最后,檢查驗證狀態(tài),如果得到了授權(quán),則啟用麥克風(fēng)按鈕。否則,打印錯誤信息,繼續(xù)禁用麥克風(fēng)按鈕。
你可能會認為,現(xiàn)在我們啟動應(yīng)用將會看到一個授權(quán)提示框,很遺憾你錯了。運行應(yīng)用帶來的是崩潰。你可能會想問,這是為什么?
提供授權(quán)信息
Apple
要求應(yīng)用為所有請求的權(quán)限提供自定義消息,對于語音權(quán)限的情況,我們必須為兩個行為請求授權(quán):
麥克風(fēng)的使用
語音的識別
要自定義消息,你需要在 info.plist
文件中定義這些消息。
讓我們打開 info.plist
文件的源代碼。方法是在
info.plist
上點擊右鍵。然后選擇
Open As > Source Code
。最后,復(fù)制下面的
XML
代碼并將它們插入到
標(biāo)簽前。
xmlNSMicrophoneUsageDescription Your microphone will be used to record your speech when you press the "Start Recording" button.
NSSpeechRecognitionUsageDescription Speech recognition will be used to determine which words you speak into this device's microphone.
好了,現(xiàn)在你已經(jīng)將兩個 key
添加到
info.plist
中了:
NSMicrophoneUsageDescription –
音頻輸入授權(quán)請求的自定義信息。注意,音頻輸入授權(quán)請求只發(fā)生在用戶點擊麥克風(fēng)按鈕的時候。
NSSpeechRecognitionUsageDescription –
語音識別授權(quán)請求的自定義信息。
你可以隨意修改這些記錄的值。一切就緒,現(xiàn)在可以運行程序了,不出意外的話,編譯并運行應(yīng)用不會報任何錯。
注意:如果工程完成之后你沒有看到音頻輸入授權(quán)請求的話,首先務(wù)必確認你是否正在模擬器上運行應(yīng)用。iOS
模擬器并不會連接
Mac
的麥克風(fēng)。
處理語音識別
現(xiàn)在用戶授權(quán)已經(jīng)完成了,讓我們一鼓作氣,接著來實現(xiàn)語音識別。首先,在 ViewController
中定義下述對象:
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?private var recognitionTask: SFSpeechRecognitionTask?private let audioEngine = AVAudioEngine()
recognitionRequest
對象用于處理語音識別請求,為語音識別提供音頻輸入。
recognitionTask
可以將識別請求的結(jié)果返回給你,它帶來了極大的便利,必要時,可以取消或停止任務(wù)。
最后的 audioEngine
是音頻引擎。它的存在使得你能夠進行音頻輸入。
接下來,讓我們創(chuàng)建一個名為 startRecording()
的新函數(shù):
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!"
}
上述函數(shù)被調(diào)用的時機是開始錄音按鈕被按下的瞬間。它的主要功能是啟動語音識別并開始監(jiān)聽麥克風(fēng)。讓我們來逐行剖析一下這段代碼:
3-6
行
–
檢查
recognitionTask
的運行狀態(tài),如果正在運行,取消任務(wù)。
8-15
行
–
創(chuàng)建一個
AVAudioSession
對象為音頻錄制做準(zhǔn)備。這里我們將錄音分類設(shè)置為
Record
,模式設(shè)為
Measurement
,然后啟動。注意,設(shè)置這些屬性有可能會拋出異常,因此你必須將其置于
try catch
語句中。
17
行
–
實例化
recognitionResquest
。創(chuàng)建
SFSpeechAudioBufferRecognitionRequest
對象,然后我們就可以利用它將音頻數(shù)據(jù)傳輸?shù)?nbsp;
Apple
的服務(wù)器。
19-21
行
–
檢查
audioEngine (
你的設(shè)備
)
是否支持音頻輸入以錄音。如果不支持,報一個
fatal error
。
23-25
行
–
檢查
recognitionRequest
對象是否已被實例化,并且值不為
nil
。
27
行
–
告訴
recognitionRequest
不要等到錄音完成才發(fā)送請求,而是在用戶說話時一部分一部分發(fā)送語音識別數(shù)據(jù)。
29
行
–
在調(diào)用
speechRecognizer
的
recognitionTask
函數(shù)時開始識別。該函數(shù)有一個完成回調(diào)函數(shù),每次識別引擎收到輸入時都會調(diào)用它,在修改當(dāng)前識別結(jié)果,亦或是取消或停止時,返回一個最終記錄。
31
行
–
定義一個
boolean
變量來表示識別是否已結(jié)束。
35
行
–
倘若結(jié)果非空,則設(shè)置
textView.text
屬性為結(jié)果中的最佳記錄。同時若為最終結(jié)果,將
isFinal
置為
true
。
39-47
行
–
如果請求沒有錯誤或已經(jīng)收到最終結(jié)果,停止
audioEngine (
音頻輸入
)
,
recognitionRequest
和
recognitionTask
。同時,將開始錄音按鈕的狀態(tài)切換為可用。
50-53
行
–
向
recognitionRequest
添加一個音頻輸入。值得留意的是,在
recognitionTask
啟動后再添加音頻輸入
完全沒有問題
。
Speech
框架會在添加了音頻輸入之后立即開始識別任務(wù)。
55
行
–
將
audioEngine
設(shè)為準(zhǔn)備就緒狀態(tài),并啟動引擎。
觸發(fā)語音識別
在創(chuàng)建語音識別任務(wù)時,我們首先得確保語音識別的可用性,因此,需要向 ViewController
添加一個
delegate
方法。如果語音識別不可用,或是改變了狀態(tài),應(yīng)隨之設(shè)置
microphoneButton.enable
屬性。針對這個方案,我們實現(xiàn)了
SFSpeechRecognizerDelegate
協(xié)議的
availabilityDidChange
方法。詳細實現(xiàn)如下所示:
func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
if available {
microphoneButton.isEnabled = true
} else {
microphoneButton.isEnabled = false
}
}
這個方法會在按鈕的可用性改變時被調(diào)用。如果語音識別可用,錄音按鈕也將被啟用。
最后,我們還需要更新一下 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)
}
}
這個函數(shù)的用途是檢查 audioEngine
是否在運行。如果正在運行,停止
audioEngine
,終止
recognitionRequest
的音頻輸入,禁用
microphoneButton
,并將按鈕的文字改為
“
開始錄音
”
。
若 audioEngine
正在工作,則應(yīng)用應(yīng)該調(diào)用
startRecording()
,以及設(shè)置按鈕的文字為
“
停止錄音
”
。
太棒了!一切就緒,可以準(zhǔn)備測試一下應(yīng)用了。首先將該應(yīng)用布署到一臺 iOS 10
的設(shè)備上,然后點擊
“
開始錄制
”
按鈕。來吧,說點什么試試!
[]( http://www.appcoda.com/wp-con...
注意:
Apple
對每臺設(shè)備的識別有限制。詳情未知,不過你可以嘗試聯(lián)系
Apple
獲得更多信息。
Apple
對每個應(yīng)用的識別也有限制。
如果你總是遭遇限制,務(wù)必聯(lián)系 Apple
,他們或許可以解決這個問題。
語音識別會消耗不少電量和流量。
語音識別每次只能持續(xù)大概一分鐘。
總結(jié)
在本教程中,我們講解了如何利用 Apple
向開發(fā)者開放的新語音
API
來識別語音并將其轉(zhuǎn)換為文本,你應(yīng)該對這些
API
的優(yōu)點深有體會并能夠合理使用了吧。
Speech
框架使用的語音識別框架與
Siri
相同。該
API
雖小,但功能十分強大,為開發(fā)者創(chuàng)造像是獲取音頻文件記錄一樣震撼的東西提供了極大的便利。
文章來源:SegmentFault