从 AVSpeechSynthesizer.write 回调创建 AsyncStream

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

我正在尝试将

AVSpeechSynthesizer.write
的结果变成
AsyncStream
。我想知道最干净的方法是什么来确保
AVSpeechSynthesizer
保持活动状态直到流完成

这是我最初编写的代码:

func toSpeachStream(utterance: AVSpeechUtterance) -> AsyncStream<AVAudioPCMBuffer> {
    return AsyncStream { continuation in
        let synthesizer = AVSpeechSynthesizer()
    
        synthesizer.write(utterance, toBufferCallback: { buffer in
            guard let buffer = buffer as? AVAudioPCMBuffer else {
                return
            }
            
            continuation.yield(buffer)
    
            if buffer.frameCapacity <= 1 { // Check if finished
                continuation.finish()
            }
        })
    }
}

这似乎不起作用。我认为问题在于

synthesizer
write
回调完成之前被销毁。结果是一个空流,我们永远等待

我当前的解决方案是保留对

AVSpeechSynthesizer
的引用,直到整个流被消耗,如下所示:

let synthesizer = AVSpeechSynthesizer()
for await buff in toSpeachStream(synthesizer, utterance) {
    ...
}

// Make sure the synthesizer stays alive until the stream is done
synthesizer.stopSpeaking(at: .immediate) 

有更惯用的方法吗?我更喜欢保留简单的签名

toSpeachStream

swift swift-concurrency
1个回答
0
投票

您可以将语音合成器的实例化移出此函数。例如,我可能只编写一个返回

AsyncStream
作为
AVSpeechSynthesizer
:

的方法的函数
extension AVSpeechSynthesizer {
    func buffers(for utterance: AVSpeechUtterance) -> AsyncStream<AVAudioPCMBuffer> {
        AsyncStream { continuation in
            write(utterance) { buffer in
                guard let buffer = buffer as? AVAudioPCMBuffer else {
                    return
                }

                continuation.yield(buffer)

                if buffer.frameCapacity <= 1 { // Check if finished
                    continuation.finish()
                }
            }
        }
    }
}

然后您可以实例化合成器,在其中迭代缓冲区,确保

AVSpeechSynthesizer
不会被释放,直到您完成异步序列的迭代:

func toSpeechStream(for utterance: AVSpeechUtterance) async {
    let synthesizer = AVSpeechSynthesizer()

    for await buffer in synthesizer.buffers(for: utterance) {
        …
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.