xcode AVSpeechSynthesizer 的问题,有什么解决方法吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19672814/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
An issue with AVSpeechSynthesizer, Any workarounds?
提问by vijayst
I am using AVSpeechSynthesizer to play text. I have an array of utterances to play.
我正在使用 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];
}
I have a cancel button to stop speaking. When I click the cancel button when the first utterance is spoken, the speech stops and it clears all the utterances in the queue. If I press the cancel button after the first utterance is spoken (i.e. second utterance), then stopping the speech does not flush the utterances queue. The code that I am using for this is:
我有一个取消按钮可以停止说话。当我在说出第一句话时单击取消按钮,语音将停止并清除队列中的所有话语。如果我在说出第一个话语(即第二个话语)后按下取消按钮,则停止演讲不会刷新话语队列。我为此使用的代码是:
[speech stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
Can someone confirm if this is a bug in the API or am I using the API incorrectly? If it is a bug, is there any workaround to resolve this issue?
有人可以确认这是 API 中的错误还是我使用的 API 不正确?如果是错误,是否有任何解决方法可以解决此问题?
采纳答案by SPA
quite likely to be a bug, in that the delegate method synthesizer didCancelSpeechUtteranceisn't called after the first utterance;
很可能是一个错误,因为在第一次说话后不会调用委托方法合成器 didCancelSpeechUtterance;
A workaround would be to chain the utterances rather than have them in an array and queue them up at once.
一种解决方法是将话语链接起来,而不是将它们放在一个数组中并立即将它们排队。
Use the delegate method synthesizer didFinishSpeechUtteranceto increment an array pointer and speak the the next text from that array. Then when trying to stop the speech, set a BOOL that is checked in this delegate method before attempting to speak the next text.
使用委托方法合成器 didFinishSpeechUtterance来增加数组指针并说出该数组中的下一个文本。然后当试图停止演讲时,在尝试朗读下一个文本之前设置一个在此委托方法中检查的 BOOL。
For example:
例如:
1) implement the protocol in the view controller that is doing the speech synthesis
1) 在进行语音合成的视图控制器中实现协议
#import <UIKit/UIKit.h>
@import AVFoundation;
@interface ViewController : UIViewController <AVSpeechSynthesizerDelegate>
@end
2) instantiate the AVSpeechSynthesizer and set its delegate to self
2) 实例化 AVSpeechSynthesizer 并将其委托设置为 self
speechSynthesizer = [AVSpeechSynthesizer new];
speechSynthesizer.delegate = self;
3) use an utterance counter, set to zero at start of speaking
3)使用话语计数器,在说话开始时设置为零
4) use an array of texts to speak
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) add delegate method didFinishSpeechUtterance to speak the next utterance from the array of texts and increment the utterance counter
5) 添加委托方法 didFinishSpeechUtterance 以说出文本数组中的下一个话语并增加话语计数器
- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance{
if(utteranceCounter < utterances.count){
AVSpeechUtterance *utterance = utterances[utteranceCounter];
[synthesizer speakUtterance:utterance];
utteranceCounter++;
}
}
5) to stop speaking, set the utterance counter to the count of the texts array and attempt to get the synthesizer to stop
5)停止说话,将话语计数器设置为文本数组的计数并尝试让合成器停止
utteranceCounter = utterances.count;
BOOL speechStopped = [speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
if(!speechStopped){
[speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryWord];
}
6) when speaking again, reset the utterance counter to zero
6)再次说话时,将话语计数器重置为零
回答by Antoine
I found a workaround :
我找到了一个解决方法:
- (void)stopSpeech
{
if([_speechSynthesizer isSpeaking]) {
[_speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:@""];
[_speechSynthesizer speakUtterance:utterance];
[_speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
}
}
Call stopSpeakingAtBoundary:
, enqueue an empty utterance and call stopSpeakingAtBoundary:
again to stop and clean the queue.
调用stopSpeakingAtBoundary:
,将一个空语句stopSpeakingAtBoundary:
加入队列,然后再次调用以停止并清理队列。
回答by technophyle
All answers here failed, and what I came up with is stopping the synthesizer and then re-instantiate it:
这里的所有答案都失败了,我想出的是停止合成器然后重新实例化它:
- (void)stopSpeech
{
if([_speechSynthesizer isSpeaking]) {
[_speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
_speechSynthesizer = [AVSpeechSynthesizer new];
_speechSynthesizer.delegate = self;
}
}
回答by chuthan20
I did something similar to what SPA mentioned. Speaking one item at a time from a loop. Here is the idea..
我做了类似于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];
}
回答by vijayst
didCancelSpeechUtterance does not work with the same AVSpeechSynthesizer object even though the utterances are chained in the didFinishSpeechUtterance method.
didCancelSpeechUtterance 不适用于同一个 AVSpeechSynthesizer 对象,即使话语在 didFinishSpeechUtterance 方法中链接。
-(void)speakInternal
{
speech = [[AVSpeechSynthesizer alloc] init];
speech.delegate = self;
[speech speakUtterance:[utterances objectAtIndex:position++]];
}
In speakInternal, I am creating AVSpeechSynthesizer object multiple times to ensure that didCancelSpeechUtterance works. Kind of a workaround.
在 speakInternal 中,我多次创建 AVSpeechSynthesizer 对象以确保 didCancelSpeechUtterance 有效。一种解决方法。