I'm trying to get Text-to-Speech output written to a sound file. I basically copied the code from this stackoverflow answer and I found that it worked in a storyboard application but no longer worked when I migrated to SwiftUI.
The following is a complete application, just paste it into ContentView in a new SwiftUI app and hit run.
import SwiftUI
import AVFoundation
struct ContentView: View {
var body: some View {
Button(action: runSpeech) {
Text("Button")
}
}
func runSpeech() {
let fileURL = generateSpeech("hello world", 1.0)
if FileManager.default.fileExists(atPath: fileURL!.path) {
print(" Speech file exists at \(fileURL!)")
} else {
print(" no file exists at \(fileURL!)")
}
return
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
func generateSpeech(_ speech: String, _ speechRate: Float) -> URL? {
let synth = AVSpeechSynthesizer()
let myUtterance = AVSpeechUtterance(string: speech)
myUtterance.rate = speechRate
myUtterance.voice = AVSpeechSynthesisVoice(language: "en")!
myUtterance.postUtteranceDelay = 2
let dataPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
do {
try FileManager.default.createDirectory(atPath: dataPath?.path ?? "", withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
print("Error creating directory: \(error.localizedDescription)")
}
let fileName = "myspeech.caf"
let fileURL = dataPath?.appendingPathComponent(fileName)
if FileManager.default.fileExists(atPath: fileURL!.path) {
print("Speech file exists at \(fileURL!)")
return fileURL
}
print("Speech file does not exist at \(fileURL!)")
var output: AVAudioFile?
synth.write(myUtterance) { (buffer: AVAudioBuffer) in
print("callback")
guard let pcmBuffer = buffer as? AVAudioPCMBuffer else {
fatalError("unknown buffer type: \(buffer)")
}
if pcmBuffer.frameLength == 0 {
// done
} else {
// append buffer to file
do {
if output == nil {
try output = AVAudioFile(
forWriting: fileURL!,
settings: pcmBuffer.format.settings,
commonFormat: .pcmFormatInt16,
interleaved: false)
}
try output?.write(from: pcmBuffer)
} catch {
print("error")
}
}
}
print("return")
return fileURL!
}
Expected output:
Speech file does not exist at file:///var/mobile/Containers/Data/Application/A5E69CE0-FB83-481C-89B2-26C802446A03/Documents/myspeech.caf
callback
return
Speech file exists at file:///var/mobile/Containers/Data/Application/A5E69CE0-FB83-481C-89B2-26C802446A03/Documents/myspeech.caf
Actual Output:
Speech file does not exist at file:///var/mobile/Containers/Data/Application/A5E69CE0-FB83-481C-89B2-26C802446A03/Documents/myspeech.caf
return
no file exists at file:///var/mobile/Containers/Data/Application/A5E69CE0-FB83-481C-89B2-26C802446A03/Documents/myspeech.caf
Note particularly that callback
never gets run, something is going wrong there.