1

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.

1 Answers1

0
let fileName = "Content.txt";
let fileManger = FileManager.default;
let doumentDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString;
let filePath = doumentDirectoryPath.appendingPathComponent(fileName);

if fileManger.fileExists(atPath: filePath) {
    let url = URL(fileURLWithPath: filePath);
    //your file is in url
}else{
    // file not exist
}