NSSpeechRecognizer 和 .delegate=self;问题

NSSpeechRecognizer and .delegate=self; Problems

我运行遇到了这个little Objective-C project我正在做的问题,事实证明这是一个障碍。我正在 El Capitan 上玩 Apple 的 NSSpeechRecognizer 软件,我正试图让这个人 运行 正确地玩,这样当我给它的谜语被提出给用户时,用户可以用一句话回应 "do something cool"。就目前而言,委托方法:

-(void) speechRecognizer:(NSSpeechRecognizer *)sender didRecognizeCommand:(NSString *)command { ... }`

甚至从未被调用过,即使识别图标看起来正确地检测到了谜语的答案。

问题是您的 main 函数有一个循环,不断检查语音是否已被识别。您没有给 NSSpeechRecognizer 实际向您发送任何消息的机会。

您的应用需要让主要 "run loop" 运行,以便它可以传递消息。通常,在 OS X 应用程序中,您的 main 只会调用 NSApplicationMain,它会为您执行此操作。

您的代码实际上是这样的:

@interface RecognizerDelegate : NSObject <NSSpeechRecognizerDelegate>

@property (nonatomic) NSSpeechRecognizer *recognizer;
@property (nonatomic) BOOL didRecognize;

@end

@implementation RecognizerDelegate

- (id)init
{
    if ((self = [super init])) {
        self.didRecognize = NO;

        self.recognizer = [[NSSpeechRecognizer alloc] init];
        self.recognizer.listensInForegroundOnly = NO;
        self.recognizer.blocksOtherRecognizers = YES;
        self.recognizer.delegate = self;
        self.recognizer.commands = @[ @"hello" ];
        [self.recognizer startListening];
    }

    return self;
}

- (void)speechRecognizer:(NSSpeechRecognizer *)sender didRecognizeCommand:(NSString *)command
{
    self.didRecognize = YES;
}

@end

int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        RecognizerDelegate *recognizerDelegate = [[RecognizerDelegate alloc] init];

        while (recognizerDelegate.didRecognize == NO) {
            // do nothing
        }

        NSLog(@"Recognized!");
    }

    return 0;
}

那个 while 循环没有做任何有用的事情,只是 运行 使您的 CPU 循环并浪费时间和精力。您不会让 NSSpeechSynthesizer 中的任何其他代码或任何系统框架(如 Foundation 或 AppKit)有机会做任何事情。所以,什么也没有发生。

要在短期内解决此问题:您可以让主 运行 循环 运行 在每个循环中运行一小会儿。此代码会让系统 运行 一秒钟,然后 return 到您的代码,因此您可以再次检查:

while (recognizerDelegate.didRecognize == NO) {
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}

长期的解决方法是将您的代码移出 main 并将其构造为真正的 OS X 应用程序。不是使用循环来轮询 recognizerDelegate.didRecognize 之类的条件,而是直接从 -speechRecognizer:didRecognizeCommand: 之类的委托方法中触发 "next thing",或者使用 NSTimer 之类的方法 运行 定期编码。

有关详细信息,请参阅 Apple 文档 Cocoa Application Competencies for OS X,特别是 "Main Event Loop" 部分。

我在使用 NSSpeechRecognizer 时遇到了同样的问题。回调函数:

func speechRecognizer(_ sender: NSSpeechRecognizer,
                      didRecognizeCommand command: String) {}

...从未被调用,尽管一切似乎都在工作。

为了让代码正常工作,我改变了三件事。

1) 我必须在我的 "sandboxed" 模式应用程序中启用权利以允许使用麦克风。

...我也做了另外两件事

2) 我在info.pList中添加了"Privacy - Microphone Usage Description",并将字符串值设置为"I want to listen to you speak"

3) 我在info.pList中添加了"Privacy - Speech Recognition Usage Description",并将字符串值设置为"I want to write down what you say"