libs-gui/Tools/speech/FliteSpeechEngine.m

122 lines
2.5 KiB
Mathematica
Raw Normal View History

#import "GSSpeechEngine.h"
#include <flite/flite.h>
cst_voice *register_cmu_us_kal();
/**
* Implementation of a speech engine using flite. This should be the default
* for resource-constrained platforms.
*/
@interface FliteSpeechEngine : GSSpeechEngine {
/** The audio device used for sound output. */
cst_audiodev *ad;
/** The current voice. Only one supported at the moment. */
cst_voice *v;
/** Flag set to tell the playback thread to exit. */
volatile BOOL shouldEndSpeaking;
/** Flag indicating whether the engine is currently speaking. */
volatile BOOL isSpeaking;
}
@end
@implementation FliteSpeechEngine
+ (void)initialize
{
flite_init();
}
- (id)init
{
if (nil == (self = [super init])) { return nil; }
// Only one voice supported by flite unless others are compiled in.
v = register_cmu_us_kal();
if (NULL == v)
{
[self release];
return nil;
}
// Each wave should be the same format.
cst_wave *w = flite_text_to_wave("test", v);
ad = audio_open(w->sample_rate, w->num_channels, CST_AUDIO_LINEAR16);
delete_wave(w);
if (NULL == ad)
{
[self release];
return nil;
}
return self;
}
- (void)sayString: (NSArray*)args
{
id pool = [NSAutoreleasePool new];
NSString *aString = [args objectAtIndex: 0];
int i,n,r;
int num_shorts;
BOOL didFinish = YES;
cst_wave *w = flite_text_to_wave([aString UTF8String], v);
num_shorts = w->num_samples * w->num_channels;
for (i=0; i < num_shorts; i += r/2)
{
if (num_shorts > i+CST_AUDIOBUFFSIZE)
{
n = CST_AUDIOBUFFSIZE;
}
else
{
n = num_shorts-i;
}
r = audio_write(ad, &w->samples[i], n*2);
if (shouldEndSpeaking)
{
didFinish = NO;
break;
}
}
isSpeaking = NO;
NS_DURING
[[args objectAtIndex: 1] didFinishSpeaking: didFinish];
NS_HANDLER
NS_ENDHANDLER
[args release];
[pool release];
delete_wave(w);
}
- (void)startSpeaking: (NSString*)aString notifyWhenDone: (id)aDelegate
{
[[[aDelegate delegate] connectionForProxy] enableMultipleThreads];
NSArray *arg = [[NSArray alloc] initWithObjects: aString, aDelegate, nil];
shouldEndSpeaking = NO;
isSpeaking = YES;
[NSThread detachNewThreadSelector: @selector(sayString:)
toTarget: self
withObject: arg];
}
- (BOOL)isSpeaking
{
return isSpeaking;
}
- (void)stopSpeaking
{
shouldEndSpeaking = YES;
// Spin until the other thread has died.
while (isSpeaking) {}
}
- (void)dealloc
{
[self stopSpeaking];
audio_close(ad);
[super dealloc];
}
@end
@implementation GSSpeechEngine (Flite)
+ (GSSpeechEngine*)defaultSpeechEngine
{
return [[[FliteSpeechEngine alloc] init] autorelease];
}
@end