将音频从 Watch 流式传输到 iPhone 以使用 SFSpeechRecognizer

问题描述 投票:0回答:2

我想在我的 Watch 应用程序中进行语音识别,显示实时转录。由于

SFSpeechRecognizer
在 WatchOS 上不可用,我将应用程序设置为使用
WatchConnectivity
将音频流式传输到 iOS 伴侣。在尝试这个之前,我在 iPhone 上尝试了相同的代码,但不涉及 Watch - 它在那里工作。

通过我的流式传输尝试,伴侣将接收音频块并且不会抛出任何错误,但它也不会转录任何文本。我怀疑我做错了什么,从

AVAudioPCMBuffer
转换回来,但我可以'由于我缺乏使用原始数据和指针的经验,所以我不太确定。

现在,整个过程如下:

  1. 用户按下按钮,触发 Watch 要求 iPhone 设置
    recognitionTask
  2. iPhone 设置
    recognitionTask
    并回答 ok 或一些错误:
guard let speechRecognizer = self.speechRecognizer else {
    WCManager.shared.sendWatchMessage(.speechRecognitionRequest(.error("no speech recognizer")))
    return
}
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
guard let recognitionRequest = recognitionRequest else {
    WCManager.shared.sendWatchMessage(.speechRecognitionRequest(.error("speech recognition request denied by ios")))
    return
}
recognitionRequest.shouldReportPartialResults = true
if #available(iOS 13, *) {
    recognitionRequest.requiresOnDeviceRecognition = true
}

recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in
    if let result = result {
        let t = result.bestTranscription.formattedString
        WCManager.shared.sendWatchMessage(.recognizedSpeech(t))
    }
    
    if error != nil {
        self.recognitionRequest = nil
        self.recognitionTask = nil
        WCManager.shared.sendWatchMessage(.speechRecognition(.error("?")))
    }
}
WCManager.shared.sendWatchMessage(.speechRecognitionRequest(.ok))
  1. Watch 设置一个音频会话,在音频引擎的输入节点上安装一个 tap,并将音频格式返回给 iPhone:
do {
    try startAudioSession()
} catch {
    self.state = .error("couldn't start audio session")
    return
}

let inputNode = audioEngine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat)
    { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
        let audioBuffer = buffer.audioBufferList.pointee.mBuffers
        let data = Data(bytes: audioBuffer.mData!, count: Int(audioBuffer.mDataByteSize))
        if self.state == .running {
            WCManager.shared.sendWatchMessage(.speechRecognition(.chunk(data, frameCount: Int(buffer.frameLength))))
        }
    }
audioEngine.prepare()

do {
    let data = try NSKeyedArchiver.archivedData(withRootObject: recordingFormat, requiringSecureCoding: true)
    WCManager.shared.sendWatchMessage(.speechRecognition(.audioFormat(data)),
        errorHandler: { _ in
            self.state = .error("iphone unavailable")
    })
    self.state = .sentAudioFormat
} catch {
    self.state = .error("could not convert audio format")
}
  1. iPhone保存音频格式并返回
    .ok
    .error()
guard let format = try? NSKeyedUnarchiver.unarchivedObject(ofClass: AVAudioFormat.self, from: data) else {
    // ...send back .error, destroy the recognitionTask
}
self.audioFormat = format
// ...send back .ok
  1. Watch启动音频引擎
try audioEngine.start()
  1. iPhone 接收音频块并将它们附加到
    recognitionRequest
    :
guard let pcm = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: AVAudioFrameCount(frameCount)) else {
    // ...send back .error, destroy the recognitionTask
}

let channels = UnsafeBufferPointer(start: pcm.floatChannelData, count: Int(pcm.format.channelCount))
let data = chunk as NSData
data.getBytes(UnsafeMutableRawPointer(channels[0]), length: data.length)
recognitionRequest.append(pcm)

任何想法都非常感谢。感谢您抽出宝贵时间!

ios swift apple-watch watchconnectivity sfspeechrecognizer
2个回答
1
投票

我强烈怀疑问题是由于链接速度太慢,您甚至无法跟上实时。您要附加由长时间静音分隔的微小(可能短至 20 毫秒)声音样本。即使是人耳也无法识别。

我将从探索 CMSampleBuffers 开始,因为您可以设置它们的时间戳。这将使识别器知道何时记录此缓冲区并消除静音。

如果这不起作用,您需要进行缓冲以积累足够的 AVAudioPCMBuffers 来执行分析。这将变得更加复杂,因此希望 CMSampleBuffers 能够代替它工作。

无论哪种情况,您都可以考虑以压缩格式传输数据。我不确定 watchOS 支持哪些格式,但您可以显着降低手表和手机之间的带宽要求。请注意不要让手表的 CPU 不堪重负。您想要易于计算的压缩,而不是您可以获得的最严格的压缩。

此外,我看不到您在此处配置的采样频率。确保它很低。大概是 8kHz。绝对没有理由仅仅为了语音转录而录制 CD 质量的声音。实际上更糟,因为它包含了太多不在人声范围内的频率。


0
投票

复制内存后忘记更新

AVAudioPCMBuffer.frameLength
了。它现在可以完美地工作,没有任何明显的延迟:)

// ...
data.getBytes(UnsafeMutableRawPointer(channels[0]), length: data.length)
pcm.frameLength = AVAudioFrameCount(frameCount)
// ...
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.