Sha*_*DES 0 avfoundation ios avspeechsynthesizer swift swiftui
去年我问了一个关于如何将语音保存到文件的问题。 堆栈溢出问题 - 将语音合成录制到保存的文件中
感谢 kakaiikaka 的回答。虽然它确实有效,但缓冲时出现了一些错误。以下代码隔离了该问题。在 iOS 16 中,尽管存在错误,但它确实可以按预期工作。我按预期打印的完成处理程序。以下错误打印了 20 次左右。
2023-06-17 15:35:33.811838-0400 RecordSpeechFix [3899:1958883] [AXTTSCommon] TTSPlaybackEnqueueFullAudioQueueBuffer:错误-66686排队缓冲区
iOS 17(第一个测试版)存在一个更具描述性的错误,并且它不起作用。完成处理程序不打印。以下错误打印了 20 次左右。
输入数据过程返回不一致的 512 个数据包(2,048 字节);按每个数据包 2 字节计算,实际上是 1,024 个数据包
我假设这是同一个问题。修复 iOS16 的错误也将修复 iOS17 的错误。我的这个假设可能是错误的。
//
// ContentView.swift
// RecordSpeechFix
//
// Created by Dennis Sargent on 6/16/23.
//
import AVFoundation
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Record Speech")
}
.padding()
.onTapGesture {
saveSpeechUtteranceToFile(phrase: "This produces warnings.") {
print("In iOS 16, this will print. In iOS17, this will not.")
}
}
}
func documentsDirectory(fileName: String, ext: String) -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectoryURL = paths[0]
return documentsDirectoryURL.appendingPathComponent("\(fileName).\(ext)")
}
let synthesizer = AVSpeechSynthesizer()
func saveSpeechUtteranceToFile(phrase: String, completionHandler: @escaping () -> ()) {
let fileURL = documentsDirectory(fileName: "Test", ext: ".caf")
let utterance = AVSpeechUtterance(string: phrase)
utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
utterance.rate = 0.50
utterance.volume = 1.0
var output: AVAudioFile?
synthesizer.write(utterance) {(buffer: AVAudioBuffer) in
guard let pcmBuffer = buffer as? AVAudioPCMBuffer else {
fatalError("unknown buffer type: \(buffer)")
}
if pcmBuffer.frameLength == 0 {
// Done
completionHandler()
} else {
do{
if output == nil {
try output = AVAudioFile(
forWriting: fileURL,
settings: pcmBuffer.format.settings,
commonFormat: .pcmFormatInt16,
interleaved: false)
}
try output?.write(from: pcmBuffer)
}catch {
print("Buffer has an error")
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我对一般录音不太熟悉。似乎存在某种缓冲问题。您知道需要什么设置来清除这些错误吗?
小智 8
原来,iOS 17 将 AVAudioPCMBuffer 格式从 Int16 更改为 Float32。只有苹果知道这是有意还是无意。这破坏了每个使用/sf/answers/4068300841/中的代码片段的应用程序
解决方法有两个:
这是我在 iOS 17 beta 4 和 iOS 16.6 上运行的代码:
func saveAVSpeechUtteranceToFile(utterance: AVSpeechUtterance, fileURL: URL) throws {
// init
totalBufferLength = 0
bufferWriteCount = 0
output = nil
// delete file if it already exists
try? FileManager.default.removeItem(at: fileURL)
// synthesize to buffers
synthesizer.write(utterance) { [self] buffer in
guard let pcmBuffer = buffer as? AVAudioPCMBuffer else {
return
}
// float32 buffers spit out a final buffer of length 1, contents 00
let doneLength: Int
if pcmBuffer.format.commonFormat == .pcmFormatInt16 || pcmBuffer.format.commonFormat == .pcmFormatInt32 {
doneLength = 0
} else {
doneLength = 1
}
if pcmBuffer.frameLength <= doneLength {
// done
playAudioFile(url: fileURL) // or whatever
} else {
totalBufferLength += pcmBuffer.frameLength
bufferWriteCount += 1
if output == nil {
do {
output = try AVAudioFile(forWriting: fileURL, settings: pcmBuffer.format.settings, commonFormat: pcmBuffer.format.commonFormat, interleaved: false)
} catch {
print("create AVAudioFile error: \(error.localizedDescription)")
}
}
do {
try output!.write(from: pcmBuffer)
} catch {
print("output!.write failed, error: \(error.localizedDescription)")
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
547 次 |
| 最近记录: |