iOS10语音识别“听”音效

Avi*_*oss 5 iphone speech-recognition ios

我正在使用新的 iOS10 框架进行实时语音识别。我AVCaptureSession用来获取音频。

我有一个“聆听”蜂鸣声来通知用户他可以开始说话了。放置该声音的最佳方法是在第一次调用 时captureOutput(:didOutputSampleBuffer..),但如果我在开始会话后尝试播放声音,该声音将不会播放。并且没有抛出任何错误......它只是默默地无法播放......

我试过的:

  • 播放系统声音 ( AudioServicesPlaySystemSound...())
  • 播放资产 AVPlayer
  • 还在主队列上尝试了上述两种解决方案的异步/同步

似乎无论我在做什么,在触发识别后都不可能触发播放任何类型的音频(不确定是特定的AVCaptureSession还是SFSpeechAudioBufferRecognitionRequest/ SFSpeechRecognitionTask...)

有任何想法吗?Apple 甚至建议播放“聆听”音效(并使用 Siri 自己做),但我找不到任何参考/示例展示如何实际做到这一点......(他们的“ SpeakToMe ”示例不播放声音)

  • 我可以在触发会话之前播放声音,它确实有效(在播放声音完成后开始会话时)但有时在实际开始识别时会出现延迟(主要是在使用 BT 耳机并从不同的 AudioSession 类别切换时) - 为此我没有完成事件......) - 因此,我需要一种在录音实际开始时播放声音的方法,而不是在它触发和交叉手指之前它不会延迟开始......

Avi*_*oss 3

嗯,显然,为了成功开始语音识别会话,并且只有在识别真正开始时(之后)才能发挥“聆听”效果,必须遵循一堆“规则”。

  1. 会话设置和触发必须在主队列上调用。所以:

    DispatchQueue.main.async {
        speechRequest = SFSpeechAudioBufferRecognitionRequest()
        task = recognizer.recognitionTask(with: speechRequest, delegate: self)
        capture = AVCaptureSession()
        //.....
        shouldHandleRecordingBegan = true
        capture?.startRunning()
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. “聆听”效果应该是通过播放器播放的AVPlayer,而不是系统声音。

  3. 知道我们肯定在录制的最安全的地方是在 的委托调用中AVCaptureAudioDataOutputSampleBufferDelegate,当我们得到第一个 SampleBuffer 回调时:

    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
    
        //only once per recognition session
        if shouldHandleRecordingBegan {
            shouldHandleRecordingBegan = false
    
            player = AVPlayer(url: Bundle.main.url(forResource: "listening", withExtension: "aiff")!)
            player.play()            
    
            DispatchQueue.main.async {
                //call delegate/handler closure/post notification etc...
            }
        }
    
        // append buffer to speech recognition
        speechRequest?.appendAudioSampleBuffer(sampleBuffer)
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 识别结束效果要容易得多:

    var ended = false
    
    if task?.state == .running || task?.state == .starting {
        task?.finish() // or task?.cancel() to cancel and not get results.
        ended = true
    }
    
    if true == capture?.isRunning {
        capture?.stopRunning()
    }
    
    if ended {
        player = AVPlayer(url: Bundle.main.url(forResource: "done", withExtension: "aiff")!)
        player.play()
    }
    
    Run Code Online (Sandbox Code Playgroud)