0

I am trying to use the AVFoundation Text to Speech feature in Swift to speak out a custom String that changes based on a parameter. How can I implement pauses between words?

Let's say this is my String:

var spokenSentence = "I like Toast with lots of Butter, but banana is nice"

How can I make TTS pause for example 3 seconds after "Butter"?

This is my code regarding TTS:

var spokenSentence = "I like Toast with lots of Butter, but banana is nice"
let synth = AVSpeechSynthesizer()
var utterance = AVSpeechUtterance(string: spokenSentence)

and later

synth.speak(utterance)

Outside of swift on MacOS I heard you can use [[slnc 1000]] is there a similar function in Swift?

Pilot_Tim
  • 3
  • 3
  • I don't think there's a way, and I would suggest submitting this as an enhancement request to Apple. It was once possible to make the speech synthesizer obey all sorts of meta-commands (you could even make it sing a tune) and that is no longer possible. – matt Apr 13 '19 at 21:37
  • You can introduce your own delays, like [I did here](https://stackoverflow.com/a/37287623/1630618) – vacawama Apr 13 '19 at 21:43

1 Answers1

0

Seems AVSpeechUtterance has properties like preUtteranceDelay or postUtteranceDelay. You can utilize such functionalities with writing some preprocessing code:

extension AVSpeechSynthesizer {
    func speekWithDelay(_ text: String) {
        let pattern = #"([^{]*)(?:\{([0-9]+(?:\.[0-9]+))\})?"#
        let regex = try! NSRegularExpression(pattern: pattern)
        let matches = regex.matches(in: text, options: .anchored, range: NSRange(0..<text.utf16.count))
        for match in matches {
            let utteranceText = text[Range(match.range(at: 1), in: text)!]
            let utterance = AVSpeechUtterance(string: String(utteranceText))
            if let range = Range(match.range(at: 2), in: text) {
                let delay = TimeInterval(text[range])!
                utterance.postUtteranceDelay = delay
            }
            speak(utterance)
        }
    }
}

Use it as:

let synth = AVSpeechSynthesizer()

@IBAction func speakButtonPressed(_ sender: Any) {
    let spokenSentence = "I like Toast with lots of Butter,{3} but banana is nice"
    synth.speekWithDelay(spokenSentence)
}

Please remember that the instance of AVSpeechSynthesizer needs to be held in a strong reference until the last utterance is spoken, so you should better hold it as an instance property.

OOPer
  • 47,149
  • 6
  • 107
  • 142
  • Unfortunately when I try this code out, it stops speaking after "Butter" when I have a {} in the string. also I get these error messages: [AudioHAL_Client] HALC_ProxyIOContext.cpp:1399:IOWorkLoop: HALC_ProxyIOContext::IOWorkLoop: failed to send the final message to the server, Error: 0x10000003 2019-04-14 16:28:09.091519+0200 myproject[18457:474227] [AudioHAL_Client] HALB_IOThread.cpp:251:_Start: HALB_IOThread::_Start: there already is a thread 2019-04-14 16:28:09.158372+0200 myproject[18457:474242] [aqme] 1396: AQDefaultDevice: Abandoning I/O cycle because reconfig pending (1) – Pilot_Tim Apr 14 '19 at 14:30
  • @Pilot_Tim, thanks for reporting, but that does not happen in my testing project, which works as expected in my environment. If you can provide more info to reproduce the issue you have shown, I will check on it. – OOPer Apr 14 '19 at 14:52