source: trunk/Cocoa/Pester/Source/SUSpeaker.m@ 306

Last change on this file since 306 was 34, checked in by Nicholas Riley, 22 years ago

Changes for Pester 1.1d1.

File size: 7.3 KB
Line 
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
13void MySpeechDoneCallback(SpeechChannel chan,SInt32 refCon);
14void 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
241void 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}
265void 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}
Note: See TracBrowser for help on using the repository browser.