class library: SynthDef - lazy implementation of removeUGen
[supercollider.git] / lang / LangPrimSource / SC_Speech.M
blob4a22654f4ff3f090d2d3866f79fdda1ec217b4b7
1 /*
2 * SC_Speech.h
3 * SC3lang
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"
28 #include "SCBase.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"
40 #include "PyrSched.h"
41 #include "GC.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
53 /////////////////////
54 const int kMaxSpeechChannels = 32;
55 PyrSymbol * s_speech;
56 PyrSymbol * s_speechwordAction;
57 PyrSymbol * s_speechdoneAction;
58 #ifdef useCarbonSpeech
59 SpeechChannel fCurSpeechChannel[kMaxSpeechChannels];
60 char *speechStrings[kMaxSpeechChannels];
61 #else
62 //NSSpeechSynthesizer* speechSynths[kMaxSpeechChannels];
63 NSArray * speechSynthsArray;
64 #endif
66 pascal void OurSpeechDoneCallBackProc ( SpeechChannel inSpeechChannel, long inRefCon );
67 pascal void OurSpeechDoneCallBackProc ( SpeechChannel inSpeechChannel, long inRefCon )
69 //call action here;
70 // post("text done");
71 #ifdef useCarbonSpeech
72 pthread_mutex_lock (&gLangMutex);
73 VMGlobals *g = gMainVMGlobals;
74 g->canCallOS = true;
75 ++g->sp; SetObject(g->sp, s_speech->u.classobj); // Set the class
76 //set arguments:
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;
83 g->canCallOS = false;
84 pthread_mutex_unlock (&gLangMutex);
85 #endif
88 pascal void OurWordCallBackProc ( SpeechChannel inSpeechChannel, long inRefCon, long inWordPos, short inWordLen);
89 pascal void OurWordCallBackProc ( SpeechChannel inSpeechChannel, long inRefCon, long inWordPos, short inWordLen) {
90 //post("word done");
91 #ifdef useCarbonSpeech
92 pthread_mutex_lock (&gLangMutex);
93 VMGlobals *g = gMainVMGlobals;
94 g->canCallOS = true;
95 ++g->sp; SetObject(g->sp, s_speech->u.classobj);
96 //set arguments:
97 ++g->sp; SetInt(g->sp, (int) inRefCon); //src
98 runInterpreter(g, s_speechwordAction, 2);
100 g->canCallOS = false;
101 pthread_mutex_unlock (&gLangMutex);
102 #endif
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;
110 PyrSlot *b = g->sp;
111 int chan;
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);
122 #else
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];
129 // }
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];
139 #endif
140 return errNone;
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;
154 int chan;
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);
164 return errNone;
165 } else {
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]));
175 #else
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);
190 #endif
191 return errNone;
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;
200 PyrSlot *c = g->sp;
201 double val;
202 int chan;
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);
208 #else
209 // NSSpeechSynthesizer * spsynth = speechSynths[chan];
210 // if(!spsynth) return errNone;
211 // [spsynth setRate: val];
212 #endif
213 return errNone;
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;
222 PyrSlot *c = g->sp;
223 double val;
224 int chan;
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);
231 #else
232 NSSpeechSynthesizer * spsynth = [speechSynthsArray objectAtIndex: chan];
233 NSError * err;
234 if(!spsynth) return errNone;
235 // 10.5 only ... ;-(
236 // [spsynth setObject: val forProperty: NSSpeechPitchBaseProperty error: &err];
237 #endif
238 return errNone;
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;
247 PyrSlot *c = g->sp;
248 double val;
249 int chan;
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);
256 #endif
257 return errNone;
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;
266 PyrSlot *c = g->sp;
267 double val;
268 int chan;
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);
276 #else
277 // 10.5 :-(
278 // NSSpeechSynthesizer * spsynth = speechSynths[chan];
279 // if(!spsynth) return errNone;
280 // [spsynth setVolume: val];
281 #endif
282 return errNone;
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;
293 PyrSlot *c = g->sp;
294 int val;
295 int chan;
296 slotIntVal(b, &chan);
297 slotIntVal(c, &val);
298 #ifdef useCarbonSpeech
299 if(val) {
300 theErr = ContinueSpeech(fCurSpeechChannel[chan] );
301 } else {
302 theErr = PauseSpeechAt(fCurSpeechChannel[chan], kImmediate);
304 #else
305 // NSSpeechSynthesizer * spsynth = speechSynths[chan];
306 // if(!spsynth) return errNone;
307 // [spsynth setRate: val];
308 #endif
309 return errNone;
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;
318 PyrSlot *c = g->sp;
319 int selector [3] = {kImmediate, kEndOfWord, kEndOfWord};
320 int val;
321 int chan;
322 slotIntVal(b, &chan);
323 slotIntVal(c, &val);
324 #ifdef useCarbonSpeech
325 StopSpeechAt(fCurSpeechChannel[chan], selector[val]);
326 if(speechStrings[chan] != NULL) {
327 free(speechStrings[chan]);
328 speechStrings[chan] = NULL;
330 #else
331 NSSpeechSynthesizer * spsynth = [speechSynthsArray objectAtIndex: chan];
332 if(!spsynth) return errNone;
333 [spsynth stopSpeaking];
334 #endif
336 return errNone;
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;
345 PyrSlot *c = g->sp;
346 int val;
347 int chan;
349 slotIntVal(b, &chan);
350 slotIntVal(c, &val);
351 #ifdef useCarbonSpeech
352 VoiceSpec theVoiceSpec;
354 theErr = GetIndVoice (val, &theVoiceSpec);
355 if (SetSpeechInfo (fCurSpeechChannel[chan], soCurrentVoice, &theVoiceSpec) == incompatibleVoice) return (!errNone);
356 #else
357 NSSpeechSynthesizer * spsynth = [speechSynthsArray objectAtIndex: chan];
358 if(!spsynth) return errNone;
360 [spsynth setVoice: [[NSSpeechSynthesizer availableVoices] objectAtIndex: val]];
362 #endif
363 return errNone;
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;
371 PyrSlot *b = g->sp;
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);
391 return errNone;
394 #endif
396 int prSpeechVoiceIsSpeaking(struct VMGlobals *g, int numArgsPushed);
397 int prSpeechVoiceIsSpeaking(struct VMGlobals *g, int numArgsPushed){
398 PyrSlot *out = g->sp-1;
399 PyrSlot *b = g->sp;
400 int chan;
401 slotIntVal(b, &chan);
402 #ifdef useCarbonSpeech
404 if(speechStrings[chan] != NULL) SetTrue(out);
405 else SetFalse(out);
406 #else
407 NSSpeechSynthesizer * spsynth = [speechSynthsArray objectAtIndex: chan];
408 if(!spsynth) return errNone;
409 if([spsynth isSpeaking]) SetTrue(out);
410 else SetFalse(out);
411 #endif
412 return errNone;
416 void initSpeechPrimitives ()
418 int base, index;
420 base = nextPrimitiveIndex();
421 index = 0;
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);
439 #endif
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;
447 #else
448 #if 0
449 if(speechSynthsArray){
450 // [speechSynthsArray enumerator]
451 [speechSynthsArray release];
452 speechSynthsArray = NULL;
454 #endif
455 #endif