[34] | 1 | //
|
---|
| 2 | // SUSpeaker.m
|
---|
| 3 | // SpeechTest
|
---|
| 4 | //
|
---|
| 5 | // Created by raf on Sun Jan 28 2001.
|
---|
| 6 | // Copyright (c) 2000 Raphael Sebbe. All rights reserved.
|
---|
| 7 | //
|
---|
| 8 |
|
---|
| 9 | #import "SUSpeaker.h"
|
---|
| 10 | #include <unistd.h>
|
---|
| 11 | #include <pthread.h>
|
---|
| 12 |
|
---|
| 13 | void MySpeechDoneCallback(SpeechChannel chan,SInt32 refCon);
|
---|
| 14 | void MySpeechWordCallback (SpeechChannel chan, SInt32 refCon, UInt32 wordPos,
|
---|
| 15 | UInt16 wordLen);
|
---|
| 16 |
|
---|
| 17 | @interface SUSpeaker (Private)
|
---|
| 18 | -(void) setCallbacks;
|
---|
| 19 | -(NSPort*) port;
|
---|
| 20 | -(void) setReserved1:(unsigned int)r;
|
---|
| 21 | -(void) setReserved2:(unsigned int)r;
|
---|
| 22 | -(BOOL) usesPort;
|
---|
| 23 | -(void) handleMessage:(unsigned)msgid;
|
---|
| 24 | @end
|
---|
| 25 |
|
---|
| 26 |
|
---|
| 27 | @implementation SUSpeaker
|
---|
| 28 |
|
---|
| 29 | /*"Returns the voice names in the same order as expected by setVoice:."*/
|
---|
| 30 | +(NSArray*) voiceNames
|
---|
| 31 | {
|
---|
| 32 | NSMutableArray *voices = [NSMutableArray arrayWithCapacity:0];
|
---|
| 33 | short voiceCount;
|
---|
| 34 | OSErr error = noErr;
|
---|
| 35 | int voiceIndex;
|
---|
| 36 |
|
---|
| 37 |
|
---|
| 38 |
|
---|
| 39 | error = CountVoices(&voiceCount);
|
---|
| 40 |
|
---|
| 41 | if(error != noErr) return voices;
|
---|
| 42 | //NSLog(@"hello : %d", voiceCount);
|
---|
| 43 | for(voiceIndex=0; voiceIndex<voiceCount; voiceIndex++)
|
---|
| 44 | {
|
---|
| 45 | VoiceSpec voiceSpec;
|
---|
| 46 | VoiceDescription voiceDescription;
|
---|
| 47 |
|
---|
| 48 | error = GetIndVoice(voiceIndex+1, &voiceSpec);
|
---|
| 49 | if(error != noErr) return voices;
|
---|
| 50 | error = GetVoiceDescription( &voiceSpec, &voiceDescription, sizeof(voiceDescription));
|
---|
| 51 | if(error == noErr)
|
---|
| 52 | {
|
---|
| 53 | NSString *voiceName = [[[NSString alloc] initWithCString:
|
---|
| 54 | &(voiceDescription.name[1]) length:voiceDescription.name[0]] autorelease];
|
---|
| 55 | //NSLog(voiceName);
|
---|
| 56 |
|
---|
| 57 | [voices addObject:voiceName];
|
---|
| 58 | }
|
---|
| 59 | else return voices;
|
---|
| 60 | }
|
---|
| 61 | return voices;
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 | + (NSString *)defaultVoice;
|
---|
| 65 | {
|
---|
| 66 | OSStatus err = noErr;
|
---|
| 67 | VoiceDescription voiceDescription;
|
---|
| 68 |
|
---|
| 69 | err = GetVoiceDescription(NULL, &voiceDescription, sizeof(voiceDescription));
|
---|
| 70 |
|
---|
| 71 | return [[[NSString alloc] initWithCString: &(voiceDescription.name[1]) length:voiceDescription.name[0]] autorelease];
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | -init
|
---|
| 75 | {
|
---|
| 76 | NSRunLoop *loop = [NSRunLoop currentRunLoop];
|
---|
| 77 | [super init];
|
---|
| 78 |
|
---|
| 79 | // we have 2 options here : we use a port or we don't.
|
---|
| 80 | // using a port means delegate message are invoked from the main
|
---|
| 81 | // thread (runloop in which this object is created), otherwise, those message
|
---|
| 82 | // are asynchronous.
|
---|
| 83 | if(loop != nil)
|
---|
| 84 | {
|
---|
| 85 | _port = [[NSPort port] retain];
|
---|
| 86 | // we use a port so that the speech manager callbacks can talk to the main thread.
|
---|
| 87 | // That way, we can safely access interface elements from the delegate methods
|
---|
| 88 |
|
---|
| 89 | [_port setDelegate:self];
|
---|
| 90 | [loop addPort:_port forMode:NSDefaultRunLoopMode];
|
---|
| 91 | _usePort = YES;
|
---|
| 92 | }
|
---|
| 93 | else _usePort = NO;
|
---|
| 94 |
|
---|
| 95 | NewSpeechChannel(NULL, &_speechChannel); // NULL voice is default voice
|
---|
| 96 | [self setCallbacks];
|
---|
| 97 | return self;
|
---|
| 98 | }
|
---|
| 99 |
|
---|
| 100 | -(void) dealloc
|
---|
| 101 | {
|
---|
| 102 |
|
---|
| 103 | [_port release];
|
---|
| 104 | if(_speechChannel != NULL)
|
---|
| 105 | {
|
---|
| 106 | DisposeSpeechChannel(_speechChannel);
|
---|
| 107 | }
|
---|
| 108 |
|
---|
| 109 | [super dealloc];
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 |
|
---|
| 113 | /*"Sets the pitch. Pitch is given in Hertz and should be comprised between 80 and 500, depending on the voice. Note that extreme value can make you app crash..."*/
|
---|
| 114 | -(void) setPitch:(float)pitch
|
---|
| 115 | {
|
---|
| 116 | int fixedPitch;
|
---|
| 117 |
|
---|
| 118 |
|
---|
| 119 | pitch = (pitch-90.0)/(300.0-90.0)*(65.0 - 30.0) + 30.0;
|
---|
| 120 | /* I don't know what Apple means with pitch between 30 and 65, so I convert that range to [90, 300]. I did not test frequencies correspond, though.*/
|
---|
| 121 |
|
---|
| 122 | fixedPitch = (int)pitch;
|
---|
| 123 |
|
---|
| 124 | fixedPitch = fixedPitch << 16; // fixed point
|
---|
| 125 |
|
---|
| 126 | if(_speechChannel != NULL)
|
---|
| 127 | {
|
---|
| 128 | SetSpeechPitch (_speechChannel, fixedPitch);
|
---|
| 129 | }
|
---|
| 130 | }
|
---|
| 131 | -(void) setVoice:(int)index
|
---|
| 132 | {
|
---|
| 133 | VoiceSpec voice;
|
---|
| 134 | OSErr error = noErr;
|
---|
| 135 |
|
---|
| 136 | if(_speechChannel != NULL)
|
---|
| 137 | {
|
---|
| 138 | DisposeSpeechChannel(_speechChannel);
|
---|
| 139 | _speechChannel = NULL;
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 | error = GetIndVoice(index, &voice);
|
---|
| 143 | if(error == noErr)
|
---|
| 144 | {
|
---|
| 145 | NewSpeechChannel(&voice, &_speechChannel);
|
---|
| 146 | [self setCallbacks];
|
---|
| 147 | }
|
---|
| 148 | }
|
---|
| 149 | -(void) speakText:(NSString*)text
|
---|
| 150 | {
|
---|
| 151 | //pid_t pid = getpid();
|
---|
| 152 | //pthread_t t = pthread_self();
|
---|
| 153 | //NSLog(@"pid : %d", t);
|
---|
| 154 |
|
---|
| 155 | if(_speechChannel != NULL && text != nil)
|
---|
| 156 | {
|
---|
| 157 | //finished = NO;
|
---|
| 158 | SpeakText(_speechChannel, [text cString], [text length]);
|
---|
| 159 | //while(!finished) ;
|
---|
| 160 | //sleep(2);
|
---|
| 161 | }
|
---|
| 162 | }
|
---|
| 163 | -(void) stopSpeaking
|
---|
| 164 | {
|
---|
| 165 | if(_speechChannel != NULL)
|
---|
| 166 | {
|
---|
| 167 | StopSpeech(_speechChannel);
|
---|
| 168 | if([_delegate respondsToSelector:@selector(didFinishSpeaking:)])
|
---|
| 169 | {
|
---|
| 170 | [_delegate didFinishSpeaking:self];
|
---|
| 171 | }
|
---|
| 172 | }
|
---|
| 173 | }
|
---|
| 174 |
|
---|
| 175 | -(void) setDelegate:(id)delegate
|
---|
| 176 | {
|
---|
| 177 | _delegate = delegate;
|
---|
| 178 | }
|
---|
| 179 | -(id) delegate
|
---|
| 180 | {
|
---|
| 181 | return _delegate;
|
---|
| 182 | }
|
---|
| 183 |
|
---|
| 184 | //--- Private ---
|
---|
| 185 |
|
---|
| 186 | -(void) setCallbacks
|
---|
| 187 | {
|
---|
| 188 | if(_speechChannel != NULL)
|
---|
| 189 | {
|
---|
| 190 | SetSpeechInfo(_speechChannel, soSpeechDoneCallBack, &MySpeechDoneCallback);
|
---|
| 191 | SetSpeechInfo(_speechChannel, soWordCallBack, &MySpeechWordCallback);
|
---|
| 192 | SetSpeechInfo(_speechChannel, soRefCon, (const void*)self);
|
---|
| 193 | }
|
---|
| 194 | }
|
---|
| 195 | -(void) setReserved1:(unsigned int)r
|
---|
| 196 | {
|
---|
| 197 | _reserved1 = r;
|
---|
| 198 | }
|
---|
| 199 | -(void) setReserved2:(unsigned int)r
|
---|
| 200 | {
|
---|
| 201 | _reserved2 = r;
|
---|
| 202 | }
|
---|
| 203 | -(NSPort*) port
|
---|
| 204 | {
|
---|
| 205 | return _port;
|
---|
| 206 | }
|
---|
| 207 | -(BOOL) usesPort
|
---|
| 208 | {
|
---|
| 209 | return _usePort;
|
---|
| 210 | }
|
---|
| 211 | -(void) handleMessage:(unsigned)msgid
|
---|
| 212 | {
|
---|
| 213 | if(msgid == 5)
|
---|
| 214 | {
|
---|
| 215 | if([_delegate respondsToSelector:@selector(willSpeakWord:at:length:)])
|
---|
| 216 | {
|
---|
| 217 | if(_reserved1 >= 0 && _reserved2 >= 0)
|
---|
| 218 | [_delegate willSpeakWord:self at:_reserved1 length:_reserved2];
|
---|
| 219 | else
|
---|
| 220 | [_delegate willSpeakWord:self at:0 length:0];
|
---|
| 221 | }
|
---|
| 222 | }
|
---|
| 223 | else if(msgid == 8)
|
---|
| 224 | {
|
---|
| 225 | if([_delegate respondsToSelector:@selector(didFinishSpeaking:)])
|
---|
| 226 | {
|
---|
| 227 | [_delegate didFinishSpeaking:self];
|
---|
| 228 | }
|
---|
| 229 | }
|
---|
| 230 | }
|
---|
| 231 | //--- NSPort delegate ---
|
---|
| 232 | - (void)handlePortMessage:(NSPortMessage *)portMessage
|
---|
| 233 | {
|
---|
| 234 | int msg = [portMessage msgid];
|
---|
| 235 |
|
---|
| 236 | [self handleMessage:msg];
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 | @end
|
---|
| 240 |
|
---|
| 241 | void MySpeechDoneCallback(SpeechChannel chan,SInt32 refCon)
|
---|
| 242 | {
|
---|
| 243 | SUSpeaker *speaker = (SUSpeaker*)refCon;
|
---|
| 244 | unsigned msg = 8;
|
---|
| 245 | //NSLog(@"Speech Done");
|
---|
| 246 |
|
---|
| 247 | if([speaker isKindOfClass:[SUSpeaker class]])
|
---|
| 248 | {
|
---|
| 249 | if([speaker usesPort])
|
---|
| 250 | {
|
---|
| 251 | NSPortMessage *message = [[NSPortMessage alloc] initWithSendPort:[speaker port]
|
---|
| 252 | receivePort:[speaker port] components:nil];
|
---|
| 253 |
|
---|
| 254 | [message setMsgid:msg];
|
---|
| 255 | [message sendBeforeDate:nil];
|
---|
| 256 | [message release];
|
---|
| 257 | }
|
---|
| 258 | else
|
---|
| 259 | {
|
---|
| 260 | // short-circuit port
|
---|
| 261 | [speaker handleMessage:msg];
|
---|
| 262 | }
|
---|
| 263 | }
|
---|
| 264 | }
|
---|
| 265 | void MySpeechWordCallback(SpeechChannel chan, SInt32 refCon, UInt32 wordPos,UInt16 wordLen)
|
---|
| 266 | {
|
---|
| 267 | SUSpeaker *speaker = (SUSpeaker*)refCon;
|
---|
| 268 | unsigned msg = 5;
|
---|
| 269 | //pid_t pid = getpid();
|
---|
| 270 | //pthread_t t = pthread_self();
|
---|
| 271 | //NSLog(@"pid : %d", t);
|
---|
| 272 |
|
---|
| 273 |
|
---|
| 274 | //NSLog(@"Word Done");
|
---|
| 275 | //while(1);
|
---|
| 276 | if([speaker isKindOfClass:[SUSpeaker class]])
|
---|
| 277 | {
|
---|
| 278 | [speaker setReserved1:wordPos];
|
---|
| 279 | [speaker setReserved2:wordLen];
|
---|
| 280 |
|
---|
| 281 | if([speaker usesPort])
|
---|
| 282 | {
|
---|
| 283 | NSPortMessage *message = [[NSPortMessage alloc] initWithSendPort:[speaker port]
|
---|
| 284 | receivePort:[speaker port] components:nil];
|
---|
| 285 |
|
---|
| 286 | [message setMsgid:msg];
|
---|
| 287 | [message sendBeforeDate:nil];
|
---|
| 288 | [message release];
|
---|
| 289 | }
|
---|
| 290 | else
|
---|
| 291 | {
|
---|
| 292 | // short-circuit port
|
---|
| 293 | [speaker handleMessage:msg];
|
---|
| 294 | }
|
---|
| 295 | }
|
---|
| 296 | }
|
---|