AVSpeechSynthesizer iOS 13 write to file

Hi,


I have been trying to use the new method that writes a speech utterance to file:


func write(AVSpeechUtterance, toBufferCallback: AVSpeechSynthesizer.BufferCallback)



I have tried looking for examples on how to save audio that is return in the callback to file and so far I have done this.


func setupAudioFile() {
        var filePath = containerUrl
        filePath?.appendPathComponent("save.wav", isDirectory: false)
        guard let file = try? AVAudioFile(forWriting: filePath!, settings: outputFormatSettings, commonFormat: .pcmFormatInt16, interleaved: true) else {
            print("unable to create new audio file")
            return
        }
            newAudio = file
    }

func convertTextToSpeech(text: String) {
        let utterance = AVSpeechUtterance(string: text)
        utterance.voice = AVSpeechSynthesisVoice.speechVoices().first(where: { (voice) -> Bool in
            return voice.name.compare("Lee (Enhanced)") == .orderedSame
        })
        let synthesis = AVSpeechSynthesizer()
        synthesis.write(utterance) { (buffer) in
            let audioB = buffer as! AVAudioPCMBuffer
            if self.audioBuffer == nil {
//                self.audioBuffer = AVAudioPCMBuffer(pcmFormat: AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 16000, channels: 1, interleaved: true)!, frameCapacity: AVAudioFrameCount(audioB.frameCapacity))
//                self.result =  Array(repeating: [Float](arrayLiteral: Float(audioB.frameCapacity)), count: 1)
            }
            if audioB.frameLength == 0 {
                print("Finished")
            }
            else {
                self.writeToFile(buffer: audioB)
            }
        }
    }
    
    private func writeToFile(buffer:AVAudioPCMBuffer) {
        let audioBuffer = buffer
        do {
            try self.newAudio?.write(from: audioBuffer)
        } catch let e {
            print(e.localizedDescription)
        }
        
        if audioBuffer.frameLength == 0 {
            print("Finished writting buffer")
        }
    }

I think I need to be doing something like appending the buffer but I am not sure how to do this.


Any help would be appreciated.


Cheers.

Replies

Although a bit late, but


func saveAVSpeechUtteranceToFile(utterance: AVSpeechUtterance, fileURL: URL) throws {
   let synthesizer = AVSpeechSynthesizer()
   
   var output: AVAudioFile?
   Logger.shared.debug("Saving region utterance to file: \(fileURL.absoluteString)")
   try? FileManager.default.removeItem(at: fileURL)
   synthesizer.write(utterance) { buffer in
      guard let pcmBuffer = buffer as? AVAudioPCMBuffer else {
         Logger.shared.error("unknow buffer type: \(buffer)")
         return
      }
      if pcmBuffer.frameLength == 0 {
         // no length
      }else{
         if output == nil {
            output = try! AVAudioFile(forWriting: fileURL, settings: pcmBuffer.format.settings, commonFormat: .pcmFormatInt16, interleaved: false)
         }
         try! output!.write(from: pcmBuffer)
      }
   }
}


This came from https://stackoverflow.com/questions/25965601/avspeechsynthesizer-output-as-file

Has anyone been able to get this to work on macOS? It just seems to read out the text as a call to speak would, not save to a file.


This was also noticed by Vasily and Glotcha a little while back, in the comments over at that Stack Overflow post: https://stackoverflow.com/a/58118583/4775982