ios 将语音语句保存到文件-缓冲错误- Swift

p4tfgftt  于 2023-08-08  发布在  iOS
关注(0)|答案(1)|浏览(135)

去年,我问了一个关于如何将演讲保存到文件中的问题。Stack Overflow Question - Recording speech synthesis to a saved file
感谢kakaiikaka的回答。虽然它确实工作,但缓冲时有一点错误。下列程式码会隔离问题。在iOS 16中,虽然有一个错误,但它确实能按预期工作。我所拥有的完成处理程序按预期打印。下面的错误会打印20次左右。
2023-06-17 15:35:33.811838-0400录音语音修复[3899:1958883] [AXTTSComon] TTSP延迟回退入队完整音频队列缓冲区:错误-66686将缓冲区入队
iOS 17(第一个测试版)有一个更多描述性的错误,它不起作用。完成处理程序不打印。下面的错误会打印大约20次。
输入数据处理器返回了不一致的512个数据包,长度为2,048字节;每个数据包2个字节,实际上是1,024个数据包
我假设这是同一个问题。修复iOS 16的错误也会修复iOS 17的错误。我的假设可能是错的。

//
//  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")
                }
            }
        }
    }
}

字符串
我对一般的录音不太熟悉。似乎存在某种缓冲问题。你知道需要什么设置来清除这些错误吗?

eivnm1vs

eivnm1vs1#

iOS 17将AVAudioPCMBuffer格式从Int16更改为Float32。只有苹果知道这是否是故意的。这破坏了所有使用https://stackoverflow.com/a/58118583/5280117代码片段的应用程序
解决方法有两种:
1.将AVAudioFile创建从固定的.pcmFormatInt16更改为与AVAudioPCMBuffer格式匹配的格式。
1.对于Float32格式的缓冲区,将done test从bufferLength 0更改为<= 1。它们显然吐出了长度为1的最终缓冲区(内容为00)。
以下是我在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)")
            }
        }
    }
}

字符串

相关问题