user3801877
user3801877

Reputation: 53

Wait for AVSpeechSynthesiser to finish utterance

I'm writing an app that allows users to press a button when they hear a web link they want to follow.

The problem is that the for loop that I am using adds all the text to the list of utterances and does not wait for an utterance to complete before continuing, meaning that I can't tell what link they want to follow.

I have a class called

Speech

which is a delegate of AVSpeechSynthesiser and have tried to create my own way of determining when an utterance has concluded:

-(id)init {
self = [super init];
if (self) {
    _synthesiser = [[AVSpeechSynthesizer alloc]init];
    [self setSpeaking:NO];
}
return self; 
}

-(void)outputAsSpeech:(NSString *)text
{
[self setSpeaking:YES];
[[self synthesiser]speakUtterance:[[AVSpeechUtterance alloc]initWithString:text]];
}

-(BOOL)isSpeaking
{
return [self speaking];
}

-(void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
{
[self setSpeaking:NO];
}

And in the class

viewController

-(void)readBookmarks
{
[[self speech]continueSpeech];
[[self speech]outputAsSpeech:@"Bookmarks,"];
for ([self bookmarksPointer]; [self bookmarksPointer] < [[self bookmarks]count]; _bookmarksPointer++) {
    NSDictionary* dictionary = [[self bookmarks]objectAtIndex:[self bookmarksPointer]];
    [[self speech]outputAsSpeech:[dictionary objectForKey:@"title"]];
    while ([[self speech]isSpeaking]) {}
    }
}

The idea was that the app should wait until the utterance has occurred and then continue. But at the moment it reads out "Bookmarks," and stops, it doesn't even read out the first bookmark, I have also tried putting the while loop at the beginning of the for loop.

Can anyone help me please, I would really appreciate it.

Thanks

Upvotes: 1

Views: 690

Answers (1)

user3801877
user3801877

Reputation: 53

So after tearing my hair out looking for an answer I realised that I hadn't set the synthesizer's delegate to 'self'. (I hate myself!)

However, that didn't solve my problem, for some reason this still didn't work. I discovered that speechSynthesiser:didFinishSpeakingUtterance: was never being called.

So instead I sent my Speech object an array of the strings I wanted it to say and kept track of them in that object, I then added a method to return the position in the array of the text that is currently being spoken by the synthesiser:

Speech

-(id)init
{
self = [super init];

if (self) {
    _synthesiser = [[AVSpeechSynthesizer alloc]init];
    [[self synthesiser]setDelegate:self];
    [self setSpeaking:NO];
    }
return self;
}

-(void)outputAsSpeech:(NSArray*)textArray
{
[self setTextToBeSpoken:textArray];
[self setArrayPointer:0];
[[self synthesiser]speakUtterance:[[AVSpeechUtterance alloc]initWithString:[[self textToBeSpoken]objectAtIndex:[self arrayPointer]]]];
}

-(void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
{
_arrayPointer++;
if ([self arrayPointer] < [[self textToBeSpoken]count]) {
    [[self synthesiser]speakUtterance:[[AVSpeechUtterance alloc]initWithString:[[self textToBeSpoken]objectAtIndex:[self arrayPointer]]]];
    }
}

-(int)stringBeingSpoken
{
return [self arrayPointer];
}

viewController

-(void)readBookmarks
{
[[self speech]continueSpeech];
NSMutableArray* textToSpeak = [[NSMutableArray alloc]init];
for (int i = 0; i < [[self bookmarks]count]; i++) {
    NSDictionary* dictionary = [[self bookmarks]objectAtIndex:i];
    NSString* textToRead = [dictionary objectForKey:@"title"];
    [textToSpeak addObject:textToRead];
    }
[[self speech]outputAsSpeech:textToSpeak];
}

-(void)currentlyBeingSpoken
{
    NSDictionary* dictionary = [[self bookmarks]objectAtIndex:[[self speech]stringBeingSpoken]];
    NSLog([dictionary objectForKey:@"title"]);
}

Upvotes: 1

Related Questions