5 * Created by jan truetzschler v. falkenstein on Wed Apr 16 2003.
6 * Copyright (c) 2003 sampleAndHold.org. All rights reserved.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <Carbon/Carbon.h>
26 #include "InitAlloc.h"
29 #include "VMGlobals.h"
30 #include "PyrSymbolTable.h"
31 #include "PyrInterpreter.h"
32 #include "PyrKernel.h"
34 #include "PyrPrimitive.h"
35 #include "PyrObjectProto.h"
36 #include "PyrPrimitiveProto.h"
37 #include "PyrKernelProto.h"
38 #include "SC_InlineUnaryOp.h"
39 #include "SC_InlineBinaryOp.h"
43 #import <AppKit/NSSpeechSynthesizer.h>
44 #import <Foundation/NSString.h>
45 #import <Foundation/NSAutoreleasePool.h>
46 #import <Foundation/NSArray.h>
47 #import <Foundation/NSEnumerator.h>
48 #import <Foundation/NSDictionary.h>
50 //comment the following line to use cocoa speech
51 #define useCarbonSpeech
54 const int kMaxSpeechChannels
= 32;
56 PyrSymbol
* s_speechwordAction
;
57 PyrSymbol
* s_speechdoneAction
;
58 #ifdef useCarbonSpeech
59 SpeechChannel fCurSpeechChannel
[kMaxSpeechChannels
];
60 char *speechStrings
[kMaxSpeechChannels
];
62 //NSSpeechSynthesizer* speechSynths[kMaxSpeechChannels];
63 NSArray
* speechSynthsArray
;
66 pascal void OurSpeechDoneCallBackProc ( SpeechChannel inSpeechChannel
, long inRefCon
);
67 pascal void OurSpeechDoneCallBackProc ( SpeechChannel inSpeechChannel
, long inRefCon
)
71 #ifdef useCarbonSpeech
72 pthread_mutex_lock (&gLangMutex
);
73 VMGlobals
*g
= gMainVMGlobals
;
75 ++g
->sp
; SetObject(g
->sp
, s_speech
->u.classobj
); // Set the class
77 ++g
->sp
;SetInt(g
->sp
, (int) inRefCon
); //src
78 runInterpreter(g
, s_speechdoneAction
, 2);
79 if(speechStrings
[(int) inRefCon
] != NULL
){
80 free(speechStrings
[(int) inRefCon
]);
81 speechStrings
[(int) inRefCon
] = NULL
;
84 pthread_mutex_unlock (&gLangMutex
);
88 pascal void OurWordCallBackProc ( SpeechChannel inSpeechChannel
, long inRefCon
, long inWordPos
, short inWordLen
);
89 pascal void OurWordCallBackProc ( SpeechChannel inSpeechChannel
, long inRefCon
, long inWordPos
, short inWordLen
) {
91 #ifdef useCarbonSpeech
92 pthread_mutex_lock (&gLangMutex
);
93 VMGlobals
*g
= gMainVMGlobals
;
95 ++g
->sp
; SetObject(g
->sp
, s_speech
->u.classobj
);
97 ++g
->sp
; SetInt(g
->sp
, (int) inRefCon
); //src
98 runInterpreter(g
, s_speechwordAction
, 2);
100 g
->canCallOS
= false;
101 pthread_mutex_unlock (&gLangMutex
);
105 int prInitSpeech(struct VMGlobals
*g
, int numArgsPushed
);
106 int prInitSpeech(struct VMGlobals
*g
, int numArgsPushed
){
108 OSErr theErr
= noErr
;
109 //PyrSlot *a = g->sp-1;
112 slotIntVal(b
, &chan
);
113 if (chan
< 0 || chan
>= kMaxSpeechChannels
) return errIndexOutOfRange
;
114 #ifdef useCarbonSpeech
115 for (int i
=0; i
<chan
; ++i
) {
116 if(fCurSpeechChannel
[i
]) DisposeSpeechChannel(fCurSpeechChannel
[i
]);
117 NewSpeechChannel( NULL
, fCurSpeechChannel
+i
);
118 theErr
= SetSpeechInfo (fCurSpeechChannel
[i
], soSpeechDoneCallBack
, (const void*)OurSpeechDoneCallBackProc
);
119 theErr
= SetSpeechInfo (fCurSpeechChannel
[i
], soWordCallBack
, (const void*)OurWordCallBackProc
);
120 theErr
= SetSpeechInfo (fCurSpeechChannel
[i
], soRefCon
, (void*) i
);
123 if(chan
<= [speechSynthsArray count
]) return errNone
;
124 if(speechSynthsArray
){
125 // NSEnumerator * voiceEnumerator = [speechSynthsArray objectEnumerator];
126 // NSSpeechSynthesizer * aVoice;
127 // while(aVoice = [voiceEnumerator nextObject]) {
128 // [voiceEnumerator release];
131 [speechSynthsArray release
];
132 speechSynthsArray
= NULL
;
135 NSSpeechSynthesizer
* speechSynths
[chan
];
136 for (int i
=0; i
<chan
; ++i
) speechSynths
[i
] = [[NSSpeechSynthesizer alloc
] init
];
137 speechSynthsArray
= [[NSArray arrayWithObjects
: speechSynths count
: chan
] retain
];
143 //NewSpeechDoneUPP(SpeechDoneProcPtr userRoutine);
144 //theErr = SetSpeechInfo (fCurSpeechChannel, soSpeechDoneCallBack, OurSpeechDoneCallBackProc);
146 int prSpeakText(struct VMGlobals
*g
, int numArgsPushed
);
147 int prSpeakText(struct VMGlobals
*g
, int numArgsPushed
){
149 OSErr theErr
= noErr
;
150 PyrSlot
*obj
= g
->sp
-2;
151 PyrSlot
*a
= g
->sp
-1;
152 PyrSlot
*str
= g
->sp
;
157 slotIntVal(a
, &chan
);
158 chan
= sc_clip(chan
, 0, kMaxSpeechChannels
);
159 PyrString
* pyrString
= slotRawString(str
);
160 #ifdef useCarbonSpeech
161 chan
= sc_clip(chan
, 0, kMaxSpeechChannels
);
162 if(speechStrings
[chan
] != NULL
) {
163 post("voice %i already speaking\n", chan
);
166 // speechStrings[chan] = (char*)pyr_pool_compile->Alloc((a->uo->size + 1)* sizeof(char));
167 speechStrings
[chan
] = (char*) malloc((slotRawObject(str
)->size
+ 1)* sizeof(char));
169 MEMFAIL(speechStrings
[chan
]);
170 slotStrVal(str
, speechStrings
[chan
], slotRawObject(str
)->size
+1);
172 //if(!fCurSpeechChannel) theErr = NewSpeechChannel( NULL, &fCurSpeechChannel );
173 theErr
= SpeakText( fCurSpeechChannel
[chan
], speechStrings
[chan
], strlen(speechStrings
[chan
]));
176 // pthread_mutex_lock (&gLangMutex);
178 NSAutoreleasePool
*autoreleasepool
= [[NSAutoreleasePool alloc
] init
];
179 NSSpeechSynthesizer
* spsynth
= [speechSynthsArray objectAtIndex
: chan
];
180 char cstr
[str
->uo
->size
+1];
181 slotStrVal(str
, cstr
, str
->uo
->size
+1);
182 // if([spsynth isSpeaking]) [spsynth stopSpeaking];
183 // NSString * nsstring = [NSString stringWithCString: cstr encoding: NSASCIIStringEncoding];
184 [spsynth startSpeakingString
: [NSString stringWithCString
: cstr encoding
: NSASCIIStringEncoding
]];
185 // [nsstring release];
186 // [nsstring release];
187 [autoreleasepool release
];
188 // pthread_mutex_unlock (&gLangMutex);
194 int prSetSpeechRate(struct VMGlobals
*g
, int numArgsPushed
);
195 int prSetSpeechRate(struct VMGlobals
*g
, int numArgsPushed
){
197 OSErr theErr
= noErr
;
198 //PyrSlot *a = g->sp-2;
199 PyrSlot
*b
= g
->sp
-1;
203 slotIntVal(b
, &chan
);
204 slotDoubleVal(c
, &val
);
205 #ifdef useCarbonSpeech
206 Fixed newRate
= (Fixed
)(val
* 65536.0);
207 theErr
= SetSpeechInfo (fCurSpeechChannel
[chan
], soRate
, &newRate
);
209 // NSSpeechSynthesizer * spsynth = speechSynths[chan];
210 // if(!spsynth) return errNone;
211 // [spsynth setRate: val];
216 int prSetSpeechPitch(struct VMGlobals
*g
, int numArgsPushed
);
217 int prSetSpeechPitch(struct VMGlobals
*g
, int numArgsPushed
){
219 OSErr theErr
= noErr
;
220 //PyrSlot *a = g->sp-2;
221 PyrSlot
*b
= g
->sp
-1;
225 slotIntVal(b
, &chan
);
226 slotDoubleVal(c
, &val
);
227 #ifdef useCarbonSpeech
228 Fixed newVal
= (Fixed
)(val
* 65536.0);
229 //if(!fCurSpeechChannel) theErr = NewSpeechChannel( NULL, &fCurSpeechChannel );
230 theErr
= SetSpeechPitch (fCurSpeechChannel
[chan
], newVal
);
232 NSSpeechSynthesizer
* spsynth
= [speechSynthsArray objectAtIndex
: chan
];
234 if(!spsynth
) return errNone
;
236 // [spsynth setObject: val forProperty: NSSpeechPitchBaseProperty error: &err];
241 int prSetSpeechPitchMod(struct VMGlobals
*g
, int numArgsPushed
);
242 int prSetSpeechPitchMod(struct VMGlobals
*g
, int numArgsPushed
){
244 OSErr theErr
= noErr
;
245 //PyrSlot *a = g->sp-2;
246 PyrSlot
*b
= g
->sp
-1;
250 slotIntVal(b
, &chan
);
251 slotDoubleVal(c
, &val
);
252 #ifdef useCarbonSpeech
253 Fixed newVal
= (Fixed
)(val
* 65536.0);
254 // if(!fCurSpeechChannel) theErr = NewSpeechChannel( NULL, &fCurSpeechChannel );
255 theErr
= SetSpeechInfo (fCurSpeechChannel
[chan
], soPitchMod
, &newVal
);
260 int prSetSpeechVolume(struct VMGlobals
*g
, int numArgsPushed
);
261 int prSetSpeechVolume(struct VMGlobals
*g
, int numArgsPushed
) {
263 OSErr theErr
= noErr
;
264 //PyrSlot *a = g->sp-2;
265 PyrSlot
*b
= g
->sp
-1;
269 slotIntVal(b
, &chan
);
270 slotDoubleVal(c
, &val
);
271 #ifdef useCarbonSpeech
273 Fixed newVal
= (Fixed
)(val
* 65536.0);
274 // if(!fCurSpeechChannel) theErr = NewSpeechChannel( NULL, &fCurSpeechChannel );
275 theErr
= SetSpeechInfo (fCurSpeechChannel
[chan
], soVolume
, &newVal
);
278 // NSSpeechSynthesizer * spsynth = speechSynths[chan];
279 // if(!spsynth) return errNone;
280 // [spsynth setVolume: val];
285 // theErr = PauseSpeechAt (fCurSpeechChannel, kImmediate);
286 // theErr = ContinueSpeech (fCurSpeechChannel);
287 int prSetSpeechPause(struct VMGlobals
*g
, int numArgsPushed
);
288 int prSetSpeechPause(struct VMGlobals
*g
, int numArgsPushed
){
290 OSErr theErr
= noErr
;
291 //PyrSlot *a = g->sp-2;
292 PyrSlot
*b
= g
->sp
-1;
296 slotIntVal(b
, &chan
);
298 #ifdef useCarbonSpeech
300 theErr
= ContinueSpeech(fCurSpeechChannel
[chan
] );
302 theErr
= PauseSpeechAt(fCurSpeechChannel
[chan
], kImmediate
);
305 // NSSpeechSynthesizer * spsynth = speechSynths[chan];
306 // if(!spsynth) return errNone;
307 // [spsynth setRate: val];
312 int prSetSpeechStop(struct VMGlobals
*g
, int numArgsPushed
);
313 int prSetSpeechStop(struct VMGlobals
*g
, int numArgsPushed
){
315 OSErr theErr
= noErr
;
316 //PyrSlot *a = g->sp-2;
317 PyrSlot
*b
= g
->sp
-1;
319 int selector
[3] = {kImmediate
, kEndOfWord
, kEndOfWord
};
322 slotIntVal(b
, &chan
);
324 #ifdef useCarbonSpeech
325 StopSpeechAt(fCurSpeechChannel
[chan
], selector
[val
]);
326 if(speechStrings
[chan
] != NULL
) {
327 free(speechStrings
[chan
]);
328 speechStrings
[chan
] = NULL
;
331 NSSpeechSynthesizer
* spsynth
= [speechSynthsArray objectAtIndex
: chan
];
332 if(!spsynth
) return errNone
;
333 [spsynth stopSpeaking
];
339 int prSetSpeechVoice(struct VMGlobals
*g
, int numArgsPushed
);
340 int prSetSpeechVoice(struct VMGlobals
*g
, int numArgsPushed
){
342 OSErr theErr
= noErr
;
343 //PyrSlot *a = g->sp-2;
344 PyrSlot
*b
= g
->sp
-1;
349 slotIntVal(b
, &chan
);
351 #ifdef useCarbonSpeech
352 VoiceSpec theVoiceSpec
;
354 theErr
= GetIndVoice (val
, &theVoiceSpec
);
355 if (SetSpeechInfo (fCurSpeechChannel
[chan
], soCurrentVoice
, &theVoiceSpec
) == incompatibleVoice
) return (!errNone
);
357 NSSpeechSynthesizer
* spsynth
= [speechSynthsArray objectAtIndex
: chan
];
358 if(!spsynth
) return errNone
;
360 [spsynth setVoice
: [[NSSpeechSynthesizer availableVoices
] objectAtIndex
: val
]];
366 #ifndef useCarbonSpeech
367 int prGetSpeechVoiceNames(struct VMGlobals
*g
, int numArgsPushed
);
368 int prGetSpeechVoiceNames(struct VMGlobals
*g
, int numArgsPushed
){
370 PyrSlot
*a
= g
->sp
-1;
373 NSAutoreleasePool
*autoreleasepool
= [[NSAutoreleasePool alloc
] init
];
375 NSString
* aVoice
= NULL
;
376 NSEnumerator
* voiceEnumerator
= [[NSSpeechSynthesizer availableVoices
] objectEnumerator
];
377 PyrObject
* allVoices
= newPyrArray(g
->gc
, (int) [[NSSpeechSynthesizer availableVoices
] count
] * sizeof(PyrObject
), 0 , true);
379 while(aVoice
= [voiceEnumerator nextObject
]) {
380 NSDictionary
* dictionaryOfVoiceAttributes
= [NSSpeechSynthesizer attributesForVoice
:aVoice
];
381 NSString
* voiceDisplayName
= [dictionaryOfVoiceAttributes objectForKey
:NSVoiceName
];
383 PyrString
*namestring
= newPyrString(g
->gc
, [voiceDisplayName cString
], 0, true);
384 SetObject(allVoices
->slots
+allVoices
->size
++, namestring
);
385 g
->gc
->GCWrite(allVoices
, (PyrObject
*) namestring
);
388 [autoreleasepool release
];
390 SetObject(a
, allVoices
);
396 int prSpeechVoiceIsSpeaking(struct VMGlobals
*g
, int numArgsPushed
);
397 int prSpeechVoiceIsSpeaking(struct VMGlobals
*g
, int numArgsPushed
){
398 PyrSlot
*out = g
->sp
-1;
401 slotIntVal(b
, &chan
);
402 #ifdef useCarbonSpeech
404 if(speechStrings
[chan
] != NULL
) SetTrue(out);
407 NSSpeechSynthesizer
* spsynth
= [speechSynthsArray objectAtIndex
: chan
];
408 if(!spsynth
) return errNone
;
409 if([spsynth isSpeaking
]) SetTrue(out);
416 void initSpeechPrimitives ()
420 base
= nextPrimitiveIndex();
423 s_speechwordAction
= getsym("doWordAction");
424 s_speechdoneAction
= getsym("doSpeechDoneAction");
425 s_speech
= getsym("Speech");
427 definePrimitive(base
, index
++, "_SpeakText", prSpeakText
, 3, 0);
428 definePrimitive(base
, index
++, "_InitSpeech", prInitSpeech
, 2, 0);
429 definePrimitive(base
, index
++, "_SetSpeechRate", prSetSpeechRate
, 3, 0);
430 definePrimitive(base
, index
++, "_SetSpeechPitch", prSetSpeechPitch
, 3, 0);
431 definePrimitive(base
, index
++, "_SetSpeechPitchMod", prSetSpeechPitchMod
, 3, 0);
432 definePrimitive(base
, index
++, "_SetSpeechVoice", prSetSpeechVoice
, 3, 0);
433 definePrimitive(base
, index
++, "_SetSpeechVolume", prSetSpeechVolume
, 3, 0);
434 definePrimitive(base
, index
++, "_SetSpeechPause", prSetSpeechPause
, 3, 0); //0 pause, 1 continue
435 definePrimitive(base
, index
++, "_SetSpeechStopAt", prSetSpeechStop
, 3, 0); //0 kImmediate, 1 kEndOfWord, 2 kEndOfSentence
436 definePrimitive(base
, index
++, "_SpeechVoiceIsSpeaking", prSpeechVoiceIsSpeaking
, 2, 0);
437 #ifndef useCarbonSpeech
438 definePrimitive(base
, index
++, "_GetSpeechVoiceNames", prGetSpeechVoiceNames
, 2, 0);
441 #ifdef useCarbonSpeech
442 for(int i
=0; i
<kMaxSpeechChannels
; ++i
){
443 speechStrings
[i
] = NULL
;
444 if(fCurSpeechChannel
[i
]) DisposeSpeechChannel(fCurSpeechChannel
[i
]);
445 fCurSpeechChannel
[i
] = NULL
;
449 if(speechSynthsArray
){
450 // [speechSynthsArray enumerator]
451 [speechSynthsArray release
];
452 speechSynthsArray
= NULL
;