10

我正在使用 AVSpeechSynthesizer 播放文本。我有一系列的话语要播放。

    NSMutableArray *utterances = [[NSMutableArray alloc] init];
    for (NSString *text in textArray) {
        AVSpeechUtterance *welcome = [[AVSpeechUtterance alloc] initWithString:text];
        welcome.rate = 0.25;
        welcome.voice = voice;
        welcome.pitchMultiplier = 1.2;
        welcome.postUtteranceDelay = 0.25;
        [utterances addObject:welcome];
    }
    lastUtterance = [utterances lastObject];
    for (AVSpeechUtterance *utterance in utterances) {
        [speech speakUtterance:utterance];
    }

我有一个取消按钮来停止讲话。当我说出第一个话语时单击取消按钮时,语音停止并清除队列中的所有话语。如果我在说出第一个话语(即第二个话语)后按下取消按钮,那么停止语音不会刷新话语队列。我为此使用的代码是:

  [speech stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];

有人可以确认这是 API 中的错误还是我错误地使用了 API?如果是错误,是否有任何解决方法可以解决此问题?

4

5 回答 5

24

我找到了一种解决方法:

- (void)stopSpeech
{
    if([_speechSynthesizer isSpeaking]) {
        [_speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
        AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:@""];
        [_speechSynthesizer speakUtterance:utterance];
        [_speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];    
    }
}

调用stopSpeakingAtBoundary:,将一个空的话语排入队列并stopSpeakingAtBoundary:再次调用以停止并清理队列。

于 2014-03-06T15:40:02.127 回答
5

很可能是一个错误,因为在第一次发言后没有调用委托方法合成器 didCancelSpeechUtterance ;

一种解决方法是将话语链接起来,而不是将它们放在一个数组中并立即将它们排队。

使用委托方法合成器 didFinishSpeechUtterance递增数组指针并说出该数组中的下一个文本。然后在尝试停止讲话时,设置一个在此委托方法中检查的 BOOL,然后再尝试说出下一个文本。

例如:

1) 在执行语音合成的视图控制器中实现协议

#import <UIKit/UIKit.h>
@import AVFoundation;
@interface ViewController : UIViewController <AVSpeechSynthesizerDelegate>

@end

2) 实例化 AVSpeechSynthesizer 并将其委托设置为 self

speechSynthesizer   = [AVSpeechSynthesizer new];
speechSynthesizer.delegate = self;

3)使用话语计数器,在说话开始时设置为零

4) 使用文本数组说话

textArray           = @[@"Mary had a little lamb, its fleece",
                        @"was white as snow",
                        @"and everywhere that Mary went",
                        @"that sheep was sure to go"];

5) 添加委托方法 didFinishSpeechUtterance 从文本数组中说出下一个话语并增加话语计数器

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance{
    if(utteranceCounter < utterances.count){
        AVSpeechUtterance *utterance = utterances[utteranceCounter];
        [synthesizer speakUtterance:utterance];
        utteranceCounter++;
    }
}

5)停止说话,将话语计数器设置为文本数组的计数并尝试让合成器停止

utteranceCounter = utterances.count;

BOOL speechStopped =  [speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
if(!speechStopped){
    [speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryWord];
}

6)再次说话时,将话语计数器重置为零

于 2013-11-18T17:32:19.323 回答
5

这里的所有答案都失败了,我想出的是停止合成器然后重新实例化它:

- (void)stopSpeech
{
    if([_speechSynthesizer isSpeaking]) {
        [_speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
        _speechSynthesizer = [AVSpeechSynthesizer new];
        _speechSynthesizer.delegate = self;
    }
}
于 2016-09-10T03:24:13.020 回答
1

我做了类似于SPA提到的事情。从一个循环中一次说一个项目。这是想法..

NSMutableArray *arr; //array of NSStrings, declared as property
AVSpeechUtterance *currentUtterence;  //declared as property
AVSpeechSynthesizer *synthesizer; //property

- (void) viewDidLoad:(BOOL)anim
{
    [super viewDidLoad:anim];
    synthesizer = [[AVSpeechSynthesizer alloc]init];

    //EDIT -- Added the line below
    synthesizer.delegate = self;

    arr = [self populateArrayWithString]; //generates strings to speak
}

//assuming one thread will call this
- (void) speakNext
{
   if (arr.count > 0)
   {
        NSString *str = [arr objectAtIndex:0];
        [arr removeObjectAtIndex:0];
        currentUtterence = [[AVSpeechUtterance alloc] initWithString:str];

        //EDIT -- Commentted out the line below
        //currentUtterence.delegate = self;
        [synthesizer speakUtterance:utteranc];
    }
}

- (void)speechSynthesizer:(AVSpeechSynthesizer *)avsSynthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
{
    if ([synthesizer isEqual:avsSynthesizer] && [utterance isEqual:currentUtterence])
        [self speakNext];
}

- (IBOutlet) userTappedCancelledButton:(id)sender
{
    //EDIT <- replaced the object the method gets called on.
    [synthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
    [arr removeAllObjects];
}
于 2013-11-21T01:34:55.817 回答
1

didCancelSpeechUtterance 不适用于相同的 AVSpeechSynthesizer 对象,即使话语链接在 didFinishSpeechUtterance 方法中。

-(void)speakInternal
{
    speech = [[AVSpeechSynthesizer alloc] init];
    speech.delegate = self;
    [speech speakUtterance:[utterances objectAtIndex:position++]];
}

在 speakInternal 中,我多次创建 AVSpeechSynthesizer 对象以确保 didCancelSpeechUtterance 有效。一种解决方法。

于 2013-11-21T17:39:39.983 回答