我正在尝试使用 Apple 的语音框架在 macOS 10.15.1 上进行语音识别。在 macOS 10.15 之前,语音识别只能在 iOS 上使用,但根据文档和这个谈话,现在应该也可以在 macOS 上使用。
但是,我所有使用它的尝试都导致SFSpeechRecognizer
'isAvailable
属性被设置为 false。根据那次谈话和文档,我启用了 Siri,并确保我的应用程序将"Privacy - Speech Recognition Usage Description"
键设置为 Info.plist 中的字符串值。
我还尝试启用代码签名(这个问题暗示可能是必要的),在系统偏好设置中的键盘 > 听写下启用听写。
这是一些示例代码,尽管细节可能并不重要;我已经尝试使用 Storyboard 而不是 SwiftUI,将回调SFSpeechRecognizer
内部和外部的实例化,requestAuthorization
未指定语言环境等。似乎没有任何效果:
import SwiftUI
import Speech
struct ContentView: View {
func tryAuth() {
SFSpeechRecognizer.requestAuthorization { authStatus in
switch authStatus {
case .authorized:
print("authorized")
case .denied:
print("denied")
case .restricted:
print("restricted")
case .notDetermined:
print("notDetermined")
@unknown default:
print("unanticipated auth status encountered")
}
}
}
func speechTest() {
guard let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US")) else {
// Not supported for device's locale
print("couldnt get recognizer")
return
}
if !recognizer.isAvailable {
print("not available")
return
}
print("Success")
}
var body: some View {
VStack {
Button("Try auth") {
self.tryAuth()
}
Button("Test") {
self.speechTest()
}
}
}
}
特别奇怪的是,如果我运行应用程序然后单击“Try auth”按钮,authStatus
回调返回的始终是.authorized
. 但是,从来没有出现过要求我授权该应用程序的对话框,并且该应用程序未显示在系统偏好设置 > 安全和隐私 > 隐私 > 语音识别下的授权应用程序列表中。
尽管如此,之后单击“测试”按钮会导致打印not available
。
It seems like there's some hole in my understanding of the macOS privacy/permissions system, but I'm not sure how to debug further. I also think it should be possible to get this working, because I've seen other questions on StackOverflow suggesting that people have done so, for example here, here.
EDIT: At the suggestion of a comment, I tried simply ignoring the fact that isAvailable
is false by replacing my check for it with code to actually try to transcribe a file, e.g.:
let request = SFSpeechURLRecognitionRequest(url: URL(fileURLWithPath: "/Users/james/Downloads/test.wav"))
recognizer.recognitionTask(with: request) { (result, error) in
guard let result = result else {
print("There was an error transcribing that file")
print("print \(error!.localizedDescription)")
return
}
if result.isFinal {
print(result.bestTranscription.formattedString)
}
}
Then it fails, printing: The operation couldn’t be completed. (kAFAssistantErrorDomain error 1700.)
. So it seems like it really is necessary to check for isAvailable
, and my question remains: how to get it to be true
?