Programmieren mit Swift - Für macOS und iOS
Programmieren mit Swift - Für macOS und iOS
Auswahl von Stimmen

In OS X gibt es nicht nur eine Stimme zur Sprachausgabe, sondern mehrere, die alle individuell ausgewählt werden können. Im folgenden Schritt soll auch Ihre Anwendung die Möglichkeit bekommen, eine andere Stimme auszuwählen.

Verändern Sie das Programm wie in der folgenden Abbildung zu sehen. Die Werkzeugleiste (Toolbar) wird um eine NSComboBox erweitert, in der später die zur Verfügung stehenden Stimmen angezeigt werden sollen. Ebenso ändert sich das Textfeld. Es ist jetzt kein NSTextField mehr, sondern ein NSTextView.
stacks_image_C602446C-DA0D-4F9C-8582-1CA788ADD468
Den Lorem ipsum Fülltext sollten Sie aus dem NSTextView entfernen. Dazu müssen Sie lediglich mehrfach in die obere Hälfte des Steuerelements klicken, bis das TextView und nicht mehr das Scroll View ausgewählt ist. Ebenfalls sollten Sie die Attribute des Eingabefeldes so ändern, dass es sich der Größe des Programmfensters anpasst. Auch diese Eigenschaft ist mit dem Inspector des Interface Builder einstellbar.
stacks_image_1FE0D9DF-8A3C-490D-A27A-79BBE3D9CBE6
Nun gilt es, die Funktionalität der Anwendung soweit wieder herzustellen, wie sie zuvor war. Einen kleinen Unterschied gibt es, da hier der zu sprechende Text aus einem NSTextView geholt werden muss. Klassen und Bezeichner müssen angepasst werden. Ansonsten sollte Ihnen der restliche Code bekannt vorkommen. Die Verbindung des Outlet zum Steuerelement muss natürlich auch neu gezeichnet werden. Gleiches gilt für die NSComboBox, für die auch schon ein Outlet erzeugt wurde.

Die MyController.h
#import <Cocoa/Cocoa.h>

@interface MyController : NSObject {

    NSSpeechSynthesizer *mySpeechSynth;
    IBOutlet NSTextView *inputTextView;
    IBOutlet NSButton *speakButton;
    IBOutlet NSButton *silenceButton;
    IBOutlet NSComboBox *voicesCombo;
}

-(
IBAction)speak:(id)sender;
-(
IBAction)silence:(id)sender;

@end
Auch die Implementierung der speak-Methode braucht kleine Anpassungen, da ein NSTextView etwas anders funktioniert.
- (IBAction)speak:(id)sender
{
    [speakButton setEnabled:
FALSE];
    [silenceButton setEnabled:
TRUE];

    NSString *aText = [[inputTextView textStorage] string];
    [mySpeechSynth startSpeakingString:aText];
}
Kommen wir nun zum ersten Teil der eigentlichen Aufgabe, die NSComboBox mit einer Liste aller im System vorhandenen Stimmen zu füllen. Ein Array mit allen Stimmen erhält man, wenn man availableVoices an die Basisklasse des NSSpeechSynthesizers sendet. Dieses Array muss dann nur noch der NSComboBox übergeben werden.
NSArray *voices = [NSSpeechSynthesizer availableVoices];
[meinVoicesCombo addItemsWithObjectValues:voices];
[meinVoicesCombo selectItemAtIndex:0];
Leider beginnt jede Stimme mit dem Präfix „com.apple.speech.synthesis.voice“. Wollte man diesen Namen so in die NSComboBox schreiben, müsste sie sehr breit sein, was sicherlich keine gute Idee ist. Ein besserer Weg ist es, sich den tatsächlichen NSVoiceName zu holen. Das ist aufwendig, aber nicht wirklich schwierig. Man muss sich nur jedes Element im Array holen, was aber mit einer Schleife schnell erledigt ist.
NSArray *voices = [NSSpeechSynthesizer availableVoices];
for (int i = 0; i < [voices count] - 1 ; i++)
{
    NSString * voiceIdentifier;
    voiceIdentifier = [voices objectAtIndex:i];
    NSString *voiceName = [[NSSpeechSynthesizer attributesForVoice:
    voiceIdentifier] valueForKey:NSVoiceName];
    [voicesCombo addItemWithObjectValue:voiceName];
}
[voicesCombo selectItemAtIndex:0];
Damit ist dieser Teil abgeschlossen. Die NSComboBox soll natürlich bei Programmstart gefüllt werden, daher gehören die soeben beschriebenen Anweisungen in die awakeFromNib-Methode, die dann so aussieht:
-(void)awakeFromNib
{
    NSArray *voices = [NSSpeechSynthesizer availableVoices];
    for (int i = 0; i < [voices count] - 1 ; i++)
    {
        NSString * voiceIdentifier;
        voiceIdentifier = [voices objectAtIndex:i];
        NSString *voiceName = [[NSSpeechSynthesizer attributesForVoice:
        voiceIdentifier] valueForKey:NSVoiceName];
        [voicesCombo addItemWithObjectValue:voiceName];
    }
    [voicesCombo selectItemAtIndex:0];

    [silenceButton setEnabled:FALSE];
    mySpeechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
    [mySpeechSynth setDelegate:self];
}
Wenn Sie Ihre Anwendung jetzt testen, werden Sie in der NSComboBox eine Liste aller Stimmen vorfinden. Auswirkungen hat diese Liste aber noch nicht, denn egal was Sie auswählen, noch wird keine Stimme zugewiesen. Das muss jedes Mal geschehen, bevor die Sprachausgabe gestartet wird, denn die Auswahl kann ja in der Zwischenzeit geändert worden sein. Zum Glück sind hier die Indexpositionen der NSComboBox und Stimmenliste gleich, so dass eine weitere Umwandlung entfällt.
- (IBAction)speak:(id)sender
{
    [speakButton setEnabled:
FALSE];
    [silenceButton setEnabled:
TRUE];

    [mySpeechSynth setVoice:[[NSSpeechSynthesizer availableVoices]
     objectAtIndex:[voicesCombo indexOfSelectedItem]]];

    NSString *aText = [[inputTextView textStorage] string];
    [mySpeechSynth startSpeakingString:aText];
}