How to make SFSpeechRecognizer available on macOS?

柔情痞子 提交于 2020-06-26 05:25:31

问题


I am trying to use Apple's Speech framework to do speech recognition on macOS 10.15.1. Before macOS 10.15, speech recognition was only available on iOS, but according to the documentation and this talk, should now be available on macOS as well.

However, all my my attempts to use it have resulted in the SFSpeechRecognizer's isAvailable property being set to false. Per that talk and the documentation I've enabled Siri and made sure that my app has the "Privacy - Speech Recognition Usage Description" key set to a string value in Info.plist.

I've also tried enabling code signing (which this question suggests might be necessary), enabling Dictation under Keyboard > Dictation in the System preferences.

Here's some example code, although the specifics probably aren't important; I've tried it using a Storyboard instead of SwiftUI, putting the instantiation of the SFSpeechRecognizer inside and outside the requestAuthorization callback, leaving the locale unspecified, etc. Nothing seems to have any effect:

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()
            }
        }
    }
}

What's especially odd is that if I run the app and then click the "Try auth" button, the authStatus returned from the callback is always .authorized. However, I've never been presented with a dialog asking me to authorize the app, and the app doesn't show up in the list of authorized apps under System Preferences > Security and Privacy > Privacy > Speech Recogniztion.

Nonetheless, clicking the "Test" button afterwards results in printing 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?


回答1:


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.



来源:https://stackoverflow.com/questions/59111644/how-to-make-sfspeechrecognizer-available-on-macos

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!