【问题标题】:Recording speech synthesis to a saved file将语音合成录制到保存的文件中
【发布时间】:2022-12-21 11:36:01
【问题描述】:

下面是我放在一起的代码,用于尝试获取一个短语,将其保存到一个文件中,然后播放该保存的文件。不确定哪个区域不起作用(文件名不正确、未保存文件、未找到文件)。任何帮助,将不胜感激。 (speakPhrase 只是一个帮助函数,让我知道语音合成器确实在工作,它确实在工作)。

import AVFoundation
import Foundation

class Coordinator {

    let synthesizer: AVSpeechSynthesizer
    var player: AVAudioPlayer?

    init() {
        let synthesizer = AVSpeechSynthesizer()
        self.synthesizer = synthesizer
    }
    
    var recordingPath:  URL {
        let soundName = "Finally.caf"
        // I've tried numerous file extensions.  .caf was in an answer somewhere else.  I would think it would be
        // .pcm, but that doesn't work either.
        
        // Local Directory
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0].appendingPathComponent(soundName)
    }

    func speakPhrase(phrase: String) {
        let utterance = AVSpeechUtterance(string: phrase)
        utterance.voice = AVSpeechSynthesisVoice(language: "en")
        synthesizer.speak(utterance)
    }
    
    func playFile() {
        print("Trying to play the file")
        
        do {
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
            try AVAudioSession.sharedInstance().setActive(true)
            
            player = try AVAudioPlayer(contentsOf: recordingPath, fileTypeHint: AVFileType.caf.rawValue)
            guard let player = player else {return}
                
                player.play()
        } catch {
            print("Error playing file.")
        }
    }
    
    func saveAVSpeechUtteranceToFile() {
        
        let utterance = AVSpeechUtterance(string: "This is speech to record")
        utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
        utterance.rate = 0.50
        
        synthesizer.write(utterance) { [self] (buffer: AVAudioBuffer) in
            guard let pcmBuffer = buffer as? AVAudioPCMBuffer else {
                fatalError("unknown buffer type: \(buffer)")
            }
            if pcmBuffer.frameLength == 0 {
                // Done
            } else {
                // append buffer to file
                do {
                    let audioFile = try AVAudioFile(forWriting: recordingPath, settings: pcmBuffer.format.settings, commonFormat: .pcmFormatInt16, interleaved: false)
                    try audioFile.write(from: pcmBuffer)
                } catch {
                    print(error.localizedDescription)
                }
            }
        }
    }
}

【问题讨论】:

  • 你为什么不直接播放文件?

标签: swift avspeechsynthesizer ios16


【解决方案1】:

你注意到下面函数中的bufferCallback被多次调用了吗?

func write(_ utterance: AVSpeechUtterance,toBufferCallback bufferCallback: @escaping AVSpeechSynthesizer.BufferCallback)

所以根本原因很简单:AVSpeechUtterance 的音频被分成了多个部分。在我的 iPhone 上,回调调用了大约 20 次。 所以如果你每次都在闭包中创建一个新的音频文件,你会得到一个非常小的音频文件(在我的 iPhone 上它是一个 6kb 的音频文件)。如果您播放该音频,则不会引起注意。

所以将函数替换为

func saveAVSpeechUtteranceToFile() {
    
    let utterance = AVSpeechUtterance(string: "This is speech to record")
    utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
    utterance.rate = 0.50
    
    // Only create new file handle if `output` is nil.
    var output: AVAudioFile?
    
    synthesizer.write(utterance) { [self] (buffer: AVAudioBuffer) in
        guard let pcmBuffer = buffer as? AVAudioPCMBuffer else {
            fatalError("unknown buffer type: (buffer)")
        }
        if pcmBuffer.frameLength == 0 {
            // Done
        } else {
            
            do{
                // this closure is called multiple times. so to save a complete audio, try create a file only for once.
                if output == nil {
                    try  output = AVAudioFile(
                        forWriting: recordingPath,
                        settings: pcmBuffer.format.settings,
                        commonFormat: .pcmFormatInt16,
                        interleaved: false)
                }
                try output?.write(from: pcmBuffer)
            }catch {
                print(error.localizedDescription)
            }
            
        }
        
    }
}

顺便说一句,我在这里上传了Github Demo

最后,告诉您如何在 iOS 设备上检查文件内容。

Xcode Window Menu -> Device and Simulators,按如下方式复制您应用的内容。

【讨论】:

  • 不适用于 XCode 14.0.1:TTSPlaybackEnqueueFullAudioQueueBuffer:错误 -66686 排队缓冲区
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-07
  • 1970-01-01
相关资源
最近更新 更多