Tag Archives: AVSpeechSynthesizer

How to use a specific voice for text-to-speech in iOS

There are two ways of creating voices with which we can make iOS talk: creating a voice using a locale (classic), or creating a voice using a specific voice identifier (futuristic).

Let me show you both options here.

Classic and Easy

In this snipped we’re creating a voice with a certain dialect, British English in this case:

NSString *phrase = @"I'm listening.";

AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc]init];

AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc]initWithString:phrase];

AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-GB"];

utterance.voice = voice;

[synthesizer speakUtterance:utterance];

While very straightforward, we don’t know if the voice is going to be male or female. All we can specify is the language and dialect.

Futuristic and Specific

The above works fine and probably was enough when the speech synthesiser framework was introduced in iOS 7, but since then there are a myriad of other voices we can use in our applications. To specify one of them, we need a voice identifier.

Here’s how to do it:

NSString *phrase = @"I'm listening.";

AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc]init];

AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc]initWithString:phrase];

AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithIdentifier:@"com.apple.ttsbundle.Karen-compact"];

utterance.voice = voice;

[synthesizer speakUtterance:utterance];

The setup is almost the same, but instead of the voiceWithLanguage method, we’re using the voiceWithIdentifier method here.

Finding Voices

To see a list of all available voices on the device, we can access the speechVoices method of the AVSpeechVoices class. This will return an array of AVSpeechVoices, all of which have a name, quality and identifier property. The latter is what we’re looking for so we can create a specific voice.

Here’s a method lists all available voices on the current device:

- (void)listVoices {

    NSArray *allVoices = [AVSpeechSynthesisVoice speechVoices];

    for (AVSpeechSynthesisVoice *voice in allVoices) {
        
        NSLog(@"Voice Name: %@, Identifier: %@, Quality: %ld", voice.name, voice.identifier, (long)voice.quality);

    }
}

Not all voices may be installed on all devices. For example, Alex is an optional high quality voice that the user needs to download first before he will show up in this array.

The quality parameter either returns 1 for standard/low-res, or 2 for enhanced/hi-res voices. Again it is up to the user to enable the hi-res quality of a voice under Settings.





How to check if the Alex Voice is installed in iOS

Talk-Icon-512Alex is Apple’s high-quality voice that users can download and use with VoiceOver and any text-to-speech application, including the native AVSpeechSynthesizer methods.

However, Alex is an optional file that users can download if they wish, and he’s rather large too (about 900MB). Hence we can’t rely on this voice being available.

While your iOS app can request to play an utterance using Alex, iOS will default to the low-quality standard voice if Alex is not available. So how can we check if he’s around?

Here’s a quick method that lets us do just that: it checks if the Alex voice as been downloaded to the device, and lets you react accordingly – perhaps by letting the user know that an enhancement is available and how to get it:

- (void)checkForAlex {
    
    // is Alex installed?
    BOOL alexInstalled = NO;
    NSArray *voices = [AVSpeechSynthesisVoice speechVoices];
    
    for (id voiceName in voices) {
        
        if ([[voiceName valueForKey:@"name"] isEqualToString:@"Alex"]) {
            alexInstalled = YES;
        }
    }
    
    // react accordingly
    if (alexInstalled) {
        NSLog(@"Alex is installed on this device.");
    } else {
        NSLog(@"Alex is not installed on this device.");
    }
}

This method loops through all installed voices and queries each voice’s name. If Alex is among them, he’s installed. You can also query other names (each voice has a name identifier), and if an “enhanced” version is available using the “quality” parameter ( 1 = standard, 2 = enhanced).

Query the “language” parameter to see which languages are available for speech on the current device (returns a language code like en-US).

For testing: here’s a quick method that writes all available voice names and parameters out as log messages:

- (void)showAllVoices {
    
    // display a list of all available voices
    NSArray *voices = [AVSpeechSynthesisVoice speechVoices];
    for (id voiceName in voices) {

        NSLog(@"Language Code: %@", [voiceName valueForKey:@"language"]);
        NSLog(@"Name: %@", [voiceName valueForKey:@"name"]);
        NSLog(@"Quality: %@", [voiceName valueForKey:@"quality"]);
        NSLog(@"Identifier: %@", [voiceName valueForKey:@"identifier"]);
        NSLog(@"-----------------------");
    }
}




How to use the speech synthesiser in iOS 7

New to iOS 7’s AVFoundation Framework is the speech synthesiser. It’s what Siri is using when he/she speaks to you. We can use this as well in our apps by passing a string into an AVSpeechUtterance object, which in turn we pass into an AVSpeechSynthesizer instance.

Here’s how:

- (IBAction)sayThis:(id)sender {
    
    AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc]init];
    AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc]initWithString:self.textField.text];
    AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-GB"];
    
    // all these values are optional
    utterance.rate = 0.2f;
    utterance.pitchMultiplier = 0.8f;
    utterance.voice = voice;
    
    [synthesizer speakUtterance:utterance];
}

To tweak the voice of the synthesiser you can specify the language (defaults to the user locale if not specified), as well as pitchMultiplier and rate values. It’s quite fun to play around with!