AVSpeechSynthesizer isn't working under ios16 anymore

9.1k Views Asked by At

I wrote a little function for a text2speech function for my app. the code works fine until ios16. now I see the following console logs:

022-09-13 18:04:02.274692+0200 Blindzeln_Prototyp[47358:164517] [asset] Failed to get sandbox extensions
2022-09-13 18:04:02.314956+0200 Blindzeln_Prototyp[47358:164517] [catalog] Query for com.apple.MobileAsset.VoiceServicesVocalizerVoice failed: 2
2022-09-13 18:04:02.315688+0200 Blindzeln_Prototyp[47358:164517] [catalog] Unable to list voice folder
2022-09-13 18:04:02.333665+0200 Blindzeln_Prototyp[47358:164517] [catalog] Query for com.apple.MobileAsset.VoiceServices.GryphonVoice failed: 2
2022-09-13 18:04:02.334239+0200 Blindzeln_Prototyp[47358:164517] [catalog] Unable to list voice folder
2022-09-13 18:04:02.338622+0200 Blindzeln_Prototyp[47358:164517] [catalog] Unable to list voice folder
2022-09-13 18:04:02.355732+0200 Blindzeln_Prototyp[47358:164583] [AXTTSCommon] File did not exist at content path: (null) (null). Attempting to fallback to default voice for language: (null)
2022-09-13 18:04:02.420342+0200 Blindzeln_Prototyp[47358:164583] [AXTTSCommon] Error: Unable to speak. No speech service: voice: (null) identifier: (null), language: (null), resource: (null)

here's the simple code:

import Foundation
import AVFoundation

func sayIt(text2speech: String) {
    let utterance = AVSpeechUtterance(string: String(text2speech))
    utterance.voice = AVSpeechSynthesisVoice(language: "de-DE")
    utterance.rate = 0.5
    
    let synthesizer = AVSpeechSynthesizer()
    synthesizer.speak(utterance)
}

any hints?

7

There are 7 best solutions below

0
YodagamaHeshan On BEST ANSWER

You are declaring the AVSpeechSynthesizer as a local variable above. As soon as an AVSpeechSynthesizer goes out of scope and is deallocated, speech output is stopped.

The system doesn’t automatically retain the speech synthesizer so you need to manually retain it until speech concludes.

Simply move let synthesizer = AVSpeechSynthesizer() in to a place where it can live in the memory at least the speech is finished.

2
ShadowDES On

Try moving

let synthesizer = AVSpeechSynthesizer()

outside of the function. That worked for me.

2
Rohit Ranjan Pandey On

Code I have used :

let synth = AVSpeechSynthesizer()    
let myUtterance = AVSpeechUtterance(string: message)
myUtterance.rate = 0.4
synth.speak(myUtterance)

Can move let synth = AVSpeechSynthesizer() out of this method and declare on top for this class and use.

Settings to enable for Xcode14 & iOS 16 : If you are using XCode14 and iOS16, it may be voices under spoken content is not downloaded and you will get an error on console saying identifier, source, content nil. All you need to do is, go to accessiblity in settings -> Spoken content -> Voices -> Select any language and download any profile. After this run ur voice and you will be able to hear the speech from passed text.

0
Ed Rowlance On

The settings that Rohit describes worked for me on the iOS 16 simulator. Apparently the iOS 16 simulators are not loaded with any voices by default. The exact sequence was as follows on the simulated device itself:

Settings/Accessibility/Spoken Content Must turn on Speak Selection. That will list Voices. Touch that. Select your language. Pick a voice. Click the download button. Wait. When done, you can back out and turn off Speak Selection on the way if you want. Turning it on is necessary to be able to select and download a voice.

1
Creanomy On

I had previously outsourced let synthesizer = AVSpeechSynthesizer()

With iOS 16, the result was not nil, but my voice Over feature has changed into a degraded robotic voice, quite inaudible and unintelligible. If you were using specific versions of languages as listed : here You can forget about it. iOS 16 does not embed them anymore.

[catalog] Unable to list voice folder

You have to replace it by basic voices. For example, "de-DE" instead of "de-DE, Name: Anna, Quality: Default [com.apple.ttsbundle.Anna-compact]"

0
Alexey Grigorjev On

u need to assign a delegate

class Speaker: NSObject, AVSpeechSynthesizerDelegate {
    let synthesizer = AVSpeechSynthesizer()
    init() {
       synthesizer.delegate = self
    }

    func speak(msg: String) {
        let utterance = AVSpeechUtterance(string: msg)

        utterance.rate = 0.57
        utterance.pitchMultiplier = 0.8
        utterance.postUtteranceDelay = 0.2
        utterance.volume = 0.8

        let voice = AVSpeechSynthesisVoice(language: "en-US")

        utterance.voice = voice
        synthesizer.speak(utterance)
    }
}

now is how to use

let s = Speaker()

s.speak(msg: "The quick brown fox jumped over the lazy dog.")
0
Luat Vu Dinh On

In my opinion,

You should list all available voices on your device first. Then pick one of them by condition such as gender, quality. This is my example:

func chooseSpeechVoices() -> [AVSpeechSynthesisVoice?] {
    // List all available voices in en-US language 
    let voices = AVSpeechSynthesisVoice.speechVoices()
        .filter({$0.language == "en-US"})

    // split male/female voices
    let maleVoices = voices.filter({$0.gender == .male})
    let femaleVoices = voices.filter({$0.gender == .female})

    // pick voices
    let selectedMaleVoice = maleVoices.first(where: {$0.quality == .premium}) ?? maleVoices.first // premium is only available from iOS 16
    let selectedFemaleVoice = femaleVoices.first(where: {$0.quality == .enhanced}) ?? femaleVoices.first

    //
    if selectedMaleVoice == nil && selectedFemaleVoice == nil {
        showAlert("Text to speech feature is not available on your device")
    } else if selectedMaleVoice == nil {
        showAlert("Text to speech with Male voice is not available on your device")
    } else if selectedFemaleVoice == nil {
        showAlert("Text to speech with Female voice is not available on your device")
    }

    return [selectedMaleVoice, selectedFemaleVoice]
}

Hope this will be helpful !!!