闲来无聊翻看苹果官方文档的时候看到一个 Speech 框架 ,之前并没有接触过这个框架,看了一下这个框架其实不大,于是决定研究一下。 Speech API可让我们扩展和增强应用程序中的语音识别体验,而无需键盘,就可以实现语音输入这样的功能。既然涉及到语音这些敏感的数据,必然是要通过用户授权的,也就是说 info.plist 文件中需要添加响应的键值来获取用户权限。因为语音识别并不是在iOS 设备本地进行识别,而是在苹果的伺服器上进行识别的。所有的语音数据都需要传给苹果的后台服务器进行处理。因此必须得到用户的授权。
NSMicrophoneUsageDescription
当你按下 开始 按钮,我们将使用麦克风进行录音
NSSpeechRecognitionUsageDescription
当你对着麦克风说话时,我们将开始语音识别
复制代码
语音识别是基于网络的服务,因此强制执行限制,以便所有应用程序可以免费使用该服务。每个设备可能会限制每天可以执行的识别次数,并且每个应用程序可以根据每天的请求数量在全球范围内进行限制。例如,如果识别请求快速失败(在启动的一秒或两秒内),则识别服务可能暂时无法应用到您的应用程序中,您可能希望稍后再次请求用户。 此外语音识别可以为电池寿命和网络使用造成相对较高的负担。在iOS 10中,话音音频持续时间限制在约1分钟,这与键盘相关听写的限制相似。 对于Speech 的使用姿势官方是给出了案例的,可以来 这里看 。当然你也可以看我的 小 Demo ,欢迎 Star鼓励。
下面给出我的代码和一些代码注释:
//
// ViewController.swift
// YaHoYiSpeech
//
// Created by 太阳在线YHY on 2017/6/10.
// Copyright © 2017年 太阳在线. All rights reserved.
//
import UIKit
import Speech
class ViewController: UIViewController {
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var startBtn: UIButton!
// 在进行语音识别之前,你必须获得用户的相应授权,因为语音识别并不是在iOS 设备本地进行识别,而是在苹果的伺服器上进行识别的。所有的语音数据都需要传给苹果的后台服务器进行处理。因此必须得到用户的授权。
// 创建语音识别器,指定语音识别的语言环境 locale ,将来会转化为什么语言,这里是使用的当前区域,那肯定就是简体汉语啦
private let speechRecognizer = SFSpeechRecognizer(locale: Locale.autoupdatingCurrent)
// 使用 identifier 这里设置的区域是台湾,将来会转化为繁体汉语
// private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "zh-TW"))
// 发起语音识别请求,为语音识别器指定一个音频输入源,这里是在音频缓冲器中提供的识别语音。
// 除 SFSpeechAudioBufferRecognitionRequest 之外还包括:
// SFSpeechRecognitionRequest 从音频源识别语音的请求。
// SFSpeechURLRecognitionRequest 在录制的音频文件中识别语音的请求。
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
// 语音识别任务,可监控识别进度。通过他可以取消或终止当前的语音识别任务
private var recognitionTask: SFSpeechRecognitionTask?
// 语音引擎,负责提供录音输入
private let audioEngine = AVAudioEngine()
override func viewDidLoad() {
super.viewDidLoad()
// 输出一下语音识别器支持的区域,就是上边初始化SFSpeechRecognizer 时 locale 所需要的 identifier
print(SFSpeechRecognizer.supportedLocales())
startBtn.isEnabled = false
// 设置语音识别器代理
speechRecognizer?.delegate = self
// 要求用户授予您的应用许可来执行语音识别。
SFSpeechRecognizer.requestAuthorization { (status) in
var isButtonenable = false
// 识别器的授权状态
switch status {
// 经过授权
case .authorized:
// 经过授权之后就允许录音按钮点击
isButtonenable = true
// 拒绝授权
case .denied:
isButtonenable = false
print("User denied access to speech recognition")
// 保密,也就是不授权
case .restricted:
isButtonenable = false
print("Speech recognition restricted on this device")
// 未决定
case .notDetermined:
isButtonenable = false
print("Speech recognition not yet authorized")
}
OperationQueue.main.addOperation() {
self.startBtn.isEnabled = isButtonenable
}
}
}
func startRecordingPersonSpeech() {
// 检查 recognitionTask 任务是否处于运行状态。如果是,取消任务开始新的任务
if recognitionTask != nil {
// 取消当前语音识别任务。
recognitionTask?.cancel()
// 语音识别任务的当前状态 是一个枚举值
print(recognitionTask!.state)
recognitionTask = nil
}
// 建立一个AVAudioSession 用于录音
let audioSession = AVAudioSession.sharedInstance()
do {
// category 设置为 record,录音
try audioSession.setCategory(AVAudioSessionCategoryRecord)
// mode 设置为 measurement
try audioSession.setMode(AVAudioSessionModeMeasurement)
// 开启 audioSession
try audioSession.setActive(true, with: .notifyOthersOnDeactivation)
} catch {
print("audioSession properties weren't set because of an error.")
}
// 初始化RecognitionRequest,在后边我们会用它将录音数据转发给苹果服务器
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
// 检查 iPhone 是否有有效的录音设备
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方法开始识别。
recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
// 用于检查识别是否结束
var isFinal = false
// 如果 result 不是 nil,
if result != nil {
// 将 textView.text 设置为 result 的最佳音译
self.textView.text = result?.bestTranscription.formattedString
// 如果 result 是最终,将 isFinal 设置为 true
isFinal = (result?.isFinal)!
}
// 如果没有错误发生,或者 result 已经结束,停止audioEngine 录音,终止 recognitionRequest 和 recognitionTask
if error != nil || isFinal {
self.audioEngine.stop()
inputNode.removeTap(onBus: 0)
self.recognitionRequest = nil
self.recognitionTask = nil
// 开始录音按钮可用
self.startBtn.isEnabled = true
}
})
// 向recognitionRequest加入一个音频输入
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 = "请讲话!"
}
@IBAction func startRecording(_ sender: UIButton) {
if audioEngine.isRunning {
// 停止录音
audioEngine.stop()
// 表示音频源已完成,并且不会再将音频附加到识别请求。
recognitionRequest?.endAudio()
startBtn.isEnabled = false
startBtn.setTitle("开始", for: .normal)
} else {
startRecordingPersonSpeech()
startBtn.setTitle("结束", for: .normal)
}
}
}
extension ViewController: SFSpeechRecognizerDelegate {
func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
if available {
startBtn.isEnabled = true
}else {
startBtn.isEnabled = false
}
}
}
复制代码