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 | }
|
---|