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

Last change on this file since 34 was 34, checked in by Nicholas Riley, 18 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.