1/* Implementation of class NSSpeechRecognizer 2 Copyright (C) 2019 Free Software Foundation, Inc. 3 4 By: Gregory John Casamento 5 Date: Fri Dec 6 04:55:59 EST 2019 6 7 This file is part of the GNUstep Library. 8 9 This library is free software; you can redistribute it and/or 10 modify it under the terms of the GNU Lesser General Public 11 License as published by the Free Software Foundation; either 12 version 2 of the License, or (at your option) any later version. 13 14 This library is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 Lesser General Public License for more details. 18 19 You should have received a copy of the GNU Lesser General Public 20 License along with this library; if not, write to the Free 21 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 Boston, MA 02110 USA. 23*/ 24 25#import <AppKit/NSSpeechRecognizer.h> 26#import <AppKit/NSApplication.h> 27#import <Foundation/NSDistantObject.h> 28#import <Foundation/NSString.h> 29#import <Foundation/NSDictionary.h> 30#import <Foundation/NSArray.h> 31#import <Foundation/NSThread.h> 32#import <Foundation/NSError.h> 33#import <Foundation/NSConnection.h> 34#import <Foundation/NSDistributedNotificationCenter.h> 35#import <Foundation/NSDebug.h> 36#import <Foundation/NSUUID.h> 37#import "GSFastEnumeration.h" 38#import "AppKit/NSWorkspace.h" 39#import "AppKit/NSWindow.h" 40 41id _speechRecognitionServer = nil; 42BOOL _serverLaunchTested = NO; 43 44#define SPEECH_RECOGNITION_SERVER @"GSSpeechRecognitionServer" 45 46@interface NSObject (SpeechRecognitionServerPrivate) 47 48- (void) addToBlockingRecognizers: (NSString *)s; 49- (void) removeFromBlockingRecognizers: (NSString *)s; 50- (BOOL) isBlocking: (NSString *)s; 51- (void) addClient; 52 53@end 54 55@implementation NSSpeechRecognizer 56 57+ (void) initialize 58{ 59 if (self == [NSSpeechRecognizer class]) 60 { 61 // Test for an existant server... 62 _speechRecognitionServer = 63 [NSConnection rootProxyForConnectionWithRegisteredName: SPEECH_RECOGNITION_SERVER 64 host: nil]; 65 66 // if none exists, start one. We will connect with it in init. 67 if (nil == _speechRecognitionServer) 68 { 69 NSWorkspace *ws = [NSWorkspace sharedWorkspace]; 70 [ws launchApplication: SPEECH_RECOGNITION_SERVER 71 showIcon: NO 72 autolaunch: NO]; 73 } 74 } 75} 76 77- (void) _restartServer 78{ 79 if (nil == _speechRecognitionServer && !_serverLaunchTested) 80 { 81 unsigned int i = 0; 82 83 // Wait for up to five seconds for the server to launch, then give up. 84 for (i = 0 ; i < 50 ; i++) 85 { 86 _speechRecognitionServer = [NSConnection 87 rootProxyForConnectionWithRegisteredName: SPEECH_RECOGNITION_SERVER 88 host: nil]; 89 RETAIN(_speechRecognitionServer); 90 if (nil != _speechRecognitionServer) 91 { 92 NSDebugLog(@"Server found!!!"); 93 break; 94 } 95 else 96 { 97 NS_DURING 98 { 99 NSWorkspace *ws = [NSWorkspace sharedWorkspace]; 100 [ws launchApplication: SPEECH_RECOGNITION_SERVER 101 showIcon: NO 102 autolaunch: NO]; 103 } 104 NS_HANDLER 105 { 106 } 107 NS_ENDHANDLER; 108 } 109 [NSThread sleepForTimeInterval: 0.1]; 110 } 111 112 // Set a flag so we don't bother waiting for the speech recognition server to 113 // launch the next time if it didn't work this time. 114 _serverLaunchTested = YES; 115 } 116 117 if (_speechRecognitionServer == nil) 118 { 119 NSLog(@"Cannot restart speech recognition server."); 120 } 121} 122 123- (void) processNotification: (NSNotification *)note 124{ 125 NSString *word = (NSString *)[note object]; 126 NSDebugLog(@"Notified"); 127 if (_listensInForegroundOnly) 128 { 129 if (_appInForeground == NO) 130 { 131 NSDebugLog(@"Only in foreground.."); 132 return; 133 } 134 } 135 136 if (_blocksOtherRecognizers) 137 { 138 NS_DURING 139 { 140 /* 141 if ([_speechRecognitionServer isBlocking: [_uuid UUIDString]] == NO) 142 { 143 // If we are not a blocking recognizer, then we are blocked... 144 NSDebugLog(@"Blocked..."); 145 return; 146 } 147 */ 148 } 149 NS_HANDLER 150 { 151 NSLog(@"%@", localException); 152 [self _restartServer]; 153 } 154 NS_ENDHANDLER; 155 } 156 157 word = [word lowercaseString]; 158 FOR_IN(NSString*, obj, _commands) 159 { 160 if ([[obj lowercaseString] isEqualToString: word]) 161 { 162 [_delegate speechRecognizer: self 163 didRecognizeCommand: word]; 164 } 165 } 166 END_FOR_IN(_commands); 167} 168 169- (void) processAppStatusNotification: (NSNotification *)note 170{ 171 NSString *name = [note name]; 172 173 if ([name isEqualToString: NSApplicationDidBecomeActiveNotification] || 174 [name isEqualToString: NSApplicationDidFinishLaunchingNotification] || 175 [name isEqualToString: NSWindowDidBecomeKeyNotification]) 176 { 177 _appInForeground = YES; 178 } 179 else 180 { 181 _appInForeground = NO; 182 } 183} 184 185// Initialize 186- (instancetype) init 187{ 188 self = [super init]; 189 if (self != nil) 190 { 191 [[NSNotificationCenter defaultCenter] 192 addObserver: self 193 selector: @selector(processAppStatusNotification:) 194 name: NSApplicationDidFinishLaunchingNotification 195 object: nil]; 196 197 [[NSNotificationCenter defaultCenter] 198 addObserver: self 199 selector: @selector(processAppStatusNotification:) 200 name: NSWindowDidBecomeKeyNotification 201 object: nil]; 202 203 [[NSNotificationCenter defaultCenter] 204 addObserver: self 205 selector: @selector(processAppStatusNotification:) 206 name: NSApplicationDidBecomeActiveNotification 207 object: nil]; 208 209 [[NSNotificationCenter defaultCenter] 210 addObserver: self 211 selector: @selector(processAppStatusNotification:) 212 name: NSApplicationDidResignActiveNotification 213 object: nil]; 214 215 _delegate = nil; 216 _blocksOtherRecognizers = NO; 217 _listensInForegroundOnly = YES; 218 _appInForeground = YES; 219 _uuid = [NSUUID UUID]; 220 221 [self _restartServer]; 222 } 223 224 NS_DURING 225 { 226 [_speechRecognitionServer addClient]; // do this to update the client count; 227 } 228 NS_HANDLER 229 { 230 NSLog(@"%@", localException); 231 [self _restartServer]; 232 } 233 NS_ENDHANDLER; 234 235 return self; 236} 237 238- (void) dealloc 239{ 240 [[NSDistributedNotificationCenter defaultCenter] removeObserver: self]; 241 [[NSNotificationCenter defaultCenter] removeObserver: self]; 242 _delegate = nil; 243 [super dealloc]; 244} 245 246// Delegate 247- (id<NSSpeechRecognizerDelegate>) delegate 248{ 249 return _delegate; 250} 251 252- (void) setDelegate: (id<NSSpeechRecognizerDelegate>)delegate 253{ 254 _delegate = delegate; 255} 256 257// Configuring... 258- (NSArray *) commands 259{ 260 return _commands; 261} 262 263- (void) setCommands: (NSArray *)commands 264{ 265 ASSIGNCOPY(_commands, commands); 266} 267 268- (NSString *) displayCommandsTitle 269{ 270 return _displayCommandsTitle; 271} 272 273- (void) setDisplayCommandsTitle: (NSString *)displayCommandsTitle 274{ 275 ASSIGNCOPY(_displayCommandsTitle, displayCommandsTitle); 276} 277 278- (BOOL) listensInForegroundOnly 279{ 280 return _listensInForegroundOnly; 281} 282 283- (void) setListensInForegroundOnly: (BOOL)listensInForegroundOnly 284{ 285 _listensInForegroundOnly = listensInForegroundOnly; 286} 287 288- (BOOL) blocksOtherRecognizers 289{ 290 return _blocksOtherRecognizers; 291} 292 293- (void) setBlocksOtherRecognizers: (BOOL)blocksOtherRecognizers 294{ 295 NS_DURING 296 { 297 if (blocksOtherRecognizers == YES) 298 { 299 [_speechRecognitionServer addToBlockingRecognizers: [_uuid UUIDString]]; 300 } 301 else 302 { 303 [_speechRecognitionServer removeFromBlockingRecognizers: [_uuid UUIDString]]; 304 } 305 _blocksOtherRecognizers = blocksOtherRecognizers; 306 } 307 NS_HANDLER 308 { 309 NSLog(@"%@", localException); 310 [self _restartServer]; 311 } 312 NS_ENDHANDLER; 313} 314 315// Listening 316- (void) startListening 317{ 318 // Start listening to the notification being sent by the server.... 319 [[NSDistributedNotificationCenter defaultCenter] 320 addObserver: self 321 selector: @selector(processNotification:) 322 name: GSSpeechRecognizerDidRecognizeWordNotification 323 object: nil]; 324 325} 326 327- (void) stopListening 328{ 329 // Remove the observer for the notification.... 330 [[NSDistributedNotificationCenter defaultCenter] 331 removeObserver: self 332 name: GSSpeechRecognizerDidRecognizeWordNotification 333 object: nil]; 334} 335@end 336