3

我正在尝试使用 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?

4

1 回答 1

0

Had similar problems with SFSpeechRecognizer... Perhaps you can set the delegate of SFSpeechRecognizer before requesting for authorization, as shown here.

For example:

class ViewController: NSViewController {
  var speechRecognizer: SFSpeechRecognizer!

  override func viewDidLoad() {
    super.viewDidLoad()
    speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))
    speechRecognizer.delegate = self
  }

  override func viewWillAppear() {
    SFSpeechRecognizer.requestAuthorization { authStatus in
       ...
    }

    if !speechRecognizer.isAvailable {
      print("Not available!")
    }

    let url = Bundle.main.url(forResource: "sample", withExtension: "mp3")!
    let request = SFSpeechURLRecognitionRequest(url: url)

    // will now ask for authorisation
    speechRecognizer.recognitionTask(with: request) { (result, error) in
        ...
    }
  }
}

extension ViewController: SFSpeechRecognizerDelegate {

}

Then the authorisation dialog will be properly shown.

In addition, it seems that only when there is a call to recognitionTask, that the user will be asked to give permission. Instead, calling requestAuthorization alone will not have any effect.

于 2019-12-16T21:42:48.940 回答