speak.m 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #import <AppKit/AppKit.h>
  2. #include <sys/types.h>
  3. #include <sys/un.h>
  4. #include <sys/socket.h>
  5. // The speach synth is global
  6. NSSpeechSynthesizer *synth = NULL;
  7. @interface SpeechDelegate : NSObject <NSSpeechSynthesizerDelegate>
  8. @end
  9. @implementation SpeechDelegate
  10. - (void)speechSynthesizer: (NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)success {
  11. if (!success) {
  12. NSLog(@"Unable to speak");
  13. }
  14. }
  15. @end
  16. @interface Socket : NSObject <NSStreamDelegate>
  17. - (BOOL)listen:(NSString *)path;
  18. static void sockCallback (CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
  19. @property NSString *path;
  20. @property CFSocketRef unixSocket;
  21. @end
  22. @implementation Socket
  23. - (BOOL)listen:(NSString *)path {
  24. self.path = path;
  25. unlink([self.path cStringUsingEncoding:NSUTF8StringEncoding]);
  26. CFSocketContext CTX = { 0, (__bridge void *)(self), NULL, NULL, NULL };
  27. self.unixSocket = CFSocketCreate(NULL, PF_UNIX, SOCK_DGRAM, 0,
  28. kCFSocketDataCallBack, (CFSocketCallBack)sockCallback, &CTX);
  29. if (self.unixSocket == NULL) {
  30. return NO;
  31. }
  32. struct sockaddr_un addr;
  33. memset(&addr, 0, sizeof(addr));
  34. addr.sun_family = AF_UNIX;
  35. strncpy(addr.sun_path, [self.path cStringUsingEncoding:NSUTF8StringEncoding], sizeof(addr.sun_path) - 1);
  36. addr.sun_len = SUN_LEN(&addr);
  37. NSData *address = [ NSData dataWithBytes: &addr length: sizeof(addr) ];
  38. if (CFSocketSetAddress(self.unixSocket, (__bridge CFDataRef) address) != kCFSocketSuccess) {
  39. CFRelease(self.unixSocket);
  40. return NO;
  41. }
  42. CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, self.unixSocket, 0);
  43. CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
  44. CFRelease(sourceRef);
  45. return YES;
  46. }
  47. static void sockCallback (CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
  48. switch(type) {
  49. case kCFSocketDataCallBack: {
  50. // Wait for other apps to stop speaking before we start
  51. // This provides no cordination with other waiting speakers
  52. while( [NSSpeechSynthesizer isAnyApplicationSpeaking] ) {
  53. [NSThread sleepForTimeInterval: 0.25];
  54. }
  55. // Speak
  56. NSString *str = [[NSString alloc] initWithData:(__bridge NSData*)data encoding:NSUTF8StringEncoding];
  57. [synth startSpeakingString: str];
  58. break;
  59. }
  60. case kCFSocketNoCallBack:
  61. case kCFSocketAcceptCallBack:
  62. case kCFSocketConnectCallBack:
  63. case kCFSocketWriteCallBack:
  64. case kCFSocketReadCallBack: {
  65. break;
  66. }
  67. }
  68. }
  69. @end
  70. int main(int argc, const char *argv[]) {
  71. // Construct a default socket path or use the provided one
  72. NSString *str;
  73. if (argc > 1) {
  74. str = [NSString stringWithUTF8String: argv[1]];
  75. }
  76. if ([str length] < 1) {
  77. NSString *tmpDir = NSTemporaryDirectory();
  78. str = [NSString stringWithFormat:@"%@plexMonitor/SPEAK.socket", tmpDir];
  79. }
  80. // Init the speech engine
  81. synth = [[NSSpeechSynthesizer alloc] init];
  82. [synth setDelegate: [SpeechDelegate new]];
  83. // Allow changes to the overall speech volume
  84. if (argc > 2) {
  85. NSString *val = [NSString stringWithUTF8String: argv[2]];
  86. NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
  87. [f setNumberStyle:NSNumberFormatterDecimalStyle];
  88. NSNumber *volume = [f numberFromString: val];
  89. if ([volume floatValue] == 0.25 || [volume floatValue] == 0.5 || \
  90. [volume floatValue] == 0.75 || [volume floatValue] == 1.0) {
  91. [synth setObject: volume forProperty: NSSpeechVolumeProperty error: nil];
  92. } else {
  93. NSLog(@"Invalid volume: %f", [volume floatValue]);
  94. }
  95. }
  96. // Open the socket
  97. Socket *sock = [[Socket alloc] init];
  98. if (![sock listen:str]) {
  99. NSLog(@"Unable to open socket");
  100. exit(-1);
  101. }
  102. // Run
  103. [[NSRunLoop currentRunLoop] run];
  104. // Cleanup (not reached)
  105. return 0;
  106. }