SFSpeechRecognizer 属于 Speech 框架,在 iOS 10 首次出现,并在 iOS13 中进行了比较重大的更新,在 iOS 13 上支持离线语音识别以及语音分析。WWDC2019 展示了其在 AI 领域的进步,其中 iOS 13 设备内置语音识别就是一项比较不错功能。
1. 都做了哪些升级
移动端上的离线语音识别模型,减少用户泄露风险,增加了用户隐私。新 API 支持了很多新功能,例如使用语音分析指标跟踪语音质量和语音模式。
同时设备上的离线模型,可以支持无限期地语音识别。与iOS 10 的早期版本只能识别一分钟相比,这是一个很巨大的提升。当然也存在一丢丢的弊端,它并不会像云端模型一样可以在线学习。会导致设备的准确性有些降低。
iOS 13 SFSpeechRecognizer 相比也智能了很多,可以识别语音中的标点符号。比如说句号,它会识别一个句号,同样其他符号也可以支持识别,比如逗号、破折号等等。但是还存在不足的一点就是目前它还不能自己帮助识别的文字添加标点符号,不过这一点已经在 iOS 16 上得到了处理,准确率也做了相应提升,已经可以替换很多付费的语音识别框架了。
2. 实现效果
可以先看效果,可以注意一下,屏幕截图是在飞行模式下拍摄的,完全离线。
这次是通过麦克风进行语音识别的,SFSpeechRecognizer 也可以支持语音文件来识别,有兴趣可以自己了解一下。
3. 工作原理
上图是实时语音识别的内部实现,语音识别依赖于以下四个:
- AVAudioEngine
- SFSpeechRecognizer
- SFRecognitionTask
- SFSpeechAudioBufferRecognitionRequest
接下来,我们看看这些是如何工作的。
对于新手而言,我先介绍一下用户隐私权限相关的内容:
添加隐私使用说明
这里需要增加麦克风和语音识别的隐私使用说明, 如下所示。
请求权限
SFSpeechRecognizer.requestAuthorization { authStatus in
switch authStatus {
case .authorized:
case .restricted:
case .notDetermined:
case .denied:
}
}
初始化
var speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: “zh-CN”))
4. 实现
4.1 配置 AVAudioEngine
AVAudioEngine 负责采集来自麦克风的音频信号,然后将音频信号传入 SFSpeechAudioBufferRecognitionRequest。
let audioEngine = AVAudioEngine()
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
let inputNode = audioEngine.inputNode
// 在安装 tap 之前先移除上一个 否则可能报
// "*** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: nullptr == Tap()"之类的错误
inputNode.removeTap(onBus: 0)
let recordingFormat = inputNode.outputFormat(forBus: 0)
// bufferSize:传入缓冲区的请求大小