History

first commit

Alan W Black committed 9 years ago

Commit e7e4868No parent (first commit)510 files · +746275 −-5Browse files

Showing the changes to sapi/FliteTTSEngineObj/FliteTTSEngineObj.cpp (1 of 510 files). Show all

Unified · Split

sapi/FliteTTSEngineObj/FliteTTSEngineObj.cpp+719−0
@@ -0,0 +1,719 @@
1+/*************************************************************************/
2+/* */
3+/* Cepstral, LLC */
4+/* Copyright (c) 2001 */
5+/* All Rights Reserved. */
6+/* */
7+/* Permission is hereby granted, free of charge, to use and distribute */
8+/* this software and its documentation without restriction, including */
9+/* without limitation the rights to use, copy, modify, merge, publish, */
10+/* distribute, sublicense, and/or sell copies of this work, and to */
11+/* permit persons to whom this work is furnished to do so, subject to */
12+/* the following conditions: */
13+/* 1. The code must retain the above copyright notice, this list of */
14+/* conditions and the following disclaimer. */
15+/* 2. Any modifications must be clearly marked as such. */
16+/* 3. Original authors' names are not deleted. */
17+/* 4. The authors' names are not used to endorse or promote products */
18+/* derived from this software without specific prior written */
19+/* permission. */
20+/* */
21+/* CEPSTRAL, LLC AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL */
22+/* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED */
23+/* WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL */
24+/* CEPSTRAL, LLC NOR THE CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, */
25+/* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER */
26+/* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION */
27+/* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR */
28+/* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
29+/* */
30+/*************************************************************************/
31+/* Author: David Huggins-Daines (dhd@cepstral.com) */
32+/* Date: December 2001 */
33+/*************************************************************************/
34+/* */
35+/* FliteTTSEngineObj.cpp: implementation of SAPI interface to Flite */
36+/* */
37+/*************************************************************************/
38+/* Updated December 2015: Alok Parlikar <alok@cobaltspeech.com> */
39+/* to work with Flite-2.0.0. */
40+/*************************************************************************/
41+
42+#include "FliteTTSEngineObj.h"
43+
44+extern "C" {
45+#include "flite.h"
46+#include "cst_tokenstream.h"
47+};
48+
49+static cst_val *sapi_tokentowords(cst_item *i);
50+
51+STDMETHODIMP
52+CFliteTTSEngineObj::SetObjectToken(ISpObjectToken* pToken)
53+{
54+ USES_CONVERSION;
55+ CSpDynamicString voxdir;
56+ char *avoxdir;
57+ HRESULT hr;
58+ const cst_val *ttwv;
59+
60+ if (!SUCCEEDED(hr = SpGenericSetObjectToken(pToken, vox_token)))
61+ return hr;
62+ if (!SUCCEEDED(vox_token->GetStringValue(L"voxdir", &voxdir)))
63+ avoxdir = NULL; /* It isn't always necessary */
64+ else
65+ avoxdir = W2A(voxdir);
66+
67+ if (curr_vox)
68+ (*unregfunc)(curr_vox);
69+ if ((curr_vox = (*regfunc)(avoxdir)) == NULL)
70+ return E_INVALIDARG; /* or something */
71+
72+ if ((ttwv = feat_val(curr_vox->features, "tokentowords_func"))) {
73+ feat_set(curr_vox->features, "old_tokentowords_func", ttwv);
74+ feat_set(curr_vox->features, "tokentowords_func",
75+ itemfunc_val(sapi_tokentowords));
76+ }
77+
78+ return hr;
79+}
80+
81+STDMETHODIMP
82+CFliteTTSEngineObj::GetOutputFormat(const GUID * pTargetFormatId,
83+ const WAVEFORMATEX * pTargetWaveFormatEx,
84+ GUID * pDesiredFormatId,
85+ WAVEFORMATEX ** ppCoMemDesiredWaveFormatEx)
86+{
87+ WAVEFORMATEX *wfx;
88+
89+ if (curr_vox == NULL)
90+ return SpConvertStreamFormatEnum(SPSF_16kHz16BitMono,
91+ pDesiredFormatId,
92+ ppCoMemDesiredWaveFormatEx);
93+
94+ /* Just return the default format for the voice, since SAPI
95+ can do all the necessary conversion. (our resampling
96+ algorithm is a lot better than SAPI's, though...) */
97+ if ((wfx = (WAVEFORMATEX *)CoTaskMemAlloc(sizeof(*wfx))) == NULL)
98+ return E_OUTOFMEMORY;
99+ memset(wfx, 0, sizeof(*wfx));
100+ wfx->nChannels = 1;
101+ wfx->nSamplesPerSec = get_param_int(curr_vox->features, "sample_rate", 16000);
102+ wfx->wFormatTag = WAVE_FORMAT_PCM;
103+ wfx->wBitsPerSample = 16;
104+ wfx->nBlockAlign = wfx->nChannels*wfx->wBitsPerSample/8;
105+ wfx->nAvgBytesPerSec = wfx->nSamplesPerSec*wfx->nBlockAlign;
106+
107+ *pDesiredFormatId = SPDFID_WaveFormatEx;
108+ *ppCoMemDesiredWaveFormatEx = wfx;
109+
110+ return S_OK;
111+}
112+
113+int
114+CFliteTTSEngineObj::phoneme_len(cst_item *s)
115+{
116+ return (short) ((item_feat_float(s, "end")
117+ - ffeature_float(s, "p.end")) * 1000);
118+}
119+
120+int
121+CFliteTTSEngineObj::viseme_len(cst_item *s)
122+{
123+ int len, vis, v;
124+
125+ if (!visemefunc)
126+ return 0;
127+
128+ vis = (*visemefunc)(s);
129+ len = phoneme_len(s);
130+ for (s = item_next(s); s; s = item_next(s)) {
131+ v = (*visemefunc)(s);
132+ if (v != vis)
133+ break;
134+ len += phoneme_len(s);
135+ }
136+
137+ return len;
138+}
139+
140+int
141+CFliteTTSEngineObj::next_viseme(cst_item *s)
142+{
143+ int vis, v = 0;
144+
145+ if (!visemefunc)
146+ return 0;
147+
148+ vis = (*visemefunc)(s);
149+ for (s = item_next(s); s; s = item_next(s)) {
150+ v = (*visemefunc)(s);
151+ if (v != vis)
152+ break;
153+ }
154+
155+ return v;
156+}
157+
158+void
159+CFliteTTSEngineObj::send_item_events()
160+{
161+ USES_CONVERSION;
162+ cst_item *i;
163+ int sps;
164+
165+ sps = get_param_int(curr_vox->features, "sample_rate", 16000);
166+ for (i = relation_head(utt_relation(curr_utt, "Segment"));
167+ i; i = item_next(i))
168+ {
169+ int offset;
170+ SPEVENT evt;
171+ cst_item *token;
172+ const cst_val *bmark;
173+
174+ offset = (int) (ffeature_float(i, "p.end") * sps * sizeof(short));
175+ token = path_to_item(i, "R:SylStructure.parent.parent.R:Token.parent");
176+
177+ /* Word boundaries */
178+ if (token
179+ && item_parent(item_as(i, "SylStructure"))
180+ && item_prev(item_as(i, "SylStructure")) == NULL
181+ && item_prev(item_parent(item_as(i, "SylStructure"))) == NULL
182+ && (token !=
183+ path_to_item(i, "R:SylStructure.parent.parent.p."
184+ "R:Token.parent")))
185+ {
186+ cst_item *prev;
187+
188+ if ((prev = item_prev(token))
189+ && (bmark = item_feat(prev, "bookmark")))
190+ {
191+ SPEVENT fooevt; /* SpClearEvent will segfault... */
192+ fooevt.eEventId = SPEI_TTS_BOOKMARK;
193+ fooevt.elParamType = SPET_LPARAM_IS_STRING;
194+ fooevt.ullAudioStreamOffset = bcount + offset;
195+ fooevt.wParam = atol(val_string(bmark));
196+ fooevt.lParam = (LPARAM) A2W(val_string(bmark));
197+ site->AddEvents(&fooevt, 1);
198+ }
199+ SpClearEvent(&evt);
200+ evt.eEventId = SPEI_WORD_BOUNDARY;
201+ evt.elParamType = SPET_LPARAM_IS_UNDEFINED;
202+ evt.ullAudioStreamOffset = bcount + offset;
203+ evt.wParam = item_feat_int(token, "token_length");
204+ evt.lParam = item_feat_int(token, "token_pos");
205+ site->AddEvents(&evt, 1);
206+ }
207+
208+ /* Visemes */
209+ if (visemefunc
210+ && ((*visemefunc)(i)
211+ != (*visemefunc)(item_prev(i)))) {
212+ SpClearEvent(&evt);
213+ evt.eEventId = SPEI_VISEME;
214+ evt.elParamType = SPET_LPARAM_IS_UNDEFINED;
215+ evt.ullAudioStreamOffset = bcount + offset;
216+ evt.wParam = MAKEWPARAM(next_viseme(i), viseme_len(i));
217+ evt.lParam = MAKELPARAM((*visemefunc)(i), (*featurefunc)(i));
218+ site->AddEvents(&evt, 1);
219+ }
220+
221+ /* Bookmarks:
222+
223+ If the previous token is a bookmark and this is the
224+ first segment in the first word of the current
225+ token, then send an event. (we did that already)
226+
227+ If this is the last segment in the utterance
228+ (probably a pause) and the last token in the
229+ utterance is a bookmark, then send an event. */
230+
231+ if (item_next(i) == NULL
232+ && (bmark = item_feat(relation_tail(utt_relation(curr_utt, "Token")),
233+ "bookmark")))
234+ {
235+ SPEVENT fooevt; /* SpClearEvent will segfault... */
236+ fooevt.eEventId = SPEI_TTS_BOOKMARK;
237+ fooevt.elParamType = SPET_LPARAM_IS_STRING;
238+ fooevt.ullAudioStreamOffset = bcount + offset;
239+ fooevt.wParam = atol(val_string(bmark));
240+ fooevt.lParam = (LPARAM) A2W(val_string(bmark));
241+ site->AddEvents(&fooevt, 1);
242+ }
243+
244+ /* Phonemes */
245+ if (phonemefunc) {
246+ SpClearEvent(&evt);
247+ evt.eEventId = SPEI_PHONEME;
248+ evt.elParamType = SPET_LPARAM_IS_UNDEFINED;
249+ evt.ullAudioStreamOffset = bcount + offset;
250+ evt.wParam = MAKEWPARAM((*phonemefunc)(item_next(i)),
251+ phoneme_len(i));
252+ evt.lParam = MAKELPARAM((*phonemefunc)(i),
253+ (*featurefunc)(i));
254+ site->AddEvents(&evt, 1);
255+ }
256+ }
257+}
258+
259+void
260+CFliteTTSEngineObj::send_sentence_event(int fpos)
261+{
262+ SPEVENT evt;
263+
264+ SpClearEvent(&evt);
265+ evt.eEventId = SPEI_SENTENCE_BOUNDARY;
266+ evt.elParamType = SPET_LPARAM_IS_UNDEFINED;
267+ evt.ullAudioStreamOffset = bcount;
268+ evt.lParam = sentence_start;
269+ evt.wParam = fpos - sentence_start;
270+ site->AddEvents(&evt, 1);
271+
272+ sentence_start = fpos;
273+}
274+
275+void
276+CFliteTTSEngineObj::synth_one_utt()
277+{
278+ cst_wave *wav;
279+ ULONG b;
280+
281+ if (!(curr_utt && tok_rel && relation_head(tok_rel)))
282+ return;
283+
284+ /* Link the vox features into the utterance features so the voice */
285+ /* features will be searched too (after the utt ones) */
286+ feat_link_into(curr_vox->features, curr_utt->features);
287+ feat_link_into(curr_vox->ffunctions, curr_utt->ffunctions);
288+
289+ if (curr_vox->utt_init)
290+ curr_vox->utt_init(curr_utt, curr_vox);
291+ utt_synth_tokens(curr_utt);
292+ wav = utt_wave(curr_utt);
293+ send_item_events();
294+ if (volume != 100)
295+ cst_wave_rescale(wav, volume * 65536 / 100);
296+ site->Write(wav->samples,
297+ wav->num_samples * sizeof(*wav->samples),
298+ &b);
299+ bcount += wav->num_samples * sizeof(*wav->samples);
300+ delete_utterance(curr_utt);
301+ curr_utt = NULL;
302+ tok_rel = NULL;
303+}
304+
305+void
306+CFliteTTSEngineObj::start_new_utt()
307+{
308+ curr_utt = new_utterance();
309+ tok_rel = utt_relation_create(curr_utt, "Token");
310+}
311+
312+/* snarfed from flite.c */
313+static int
314+utt_break(cst_tokenstream *ts, const char *token, cst_relation *tokens)
315+{
316+ /* This is English (and some other latin based languages) */
317+ /* so it shouldn't be here */
318+ const char *postpunct;
319+ const char *ltoken;
320+ cst_item *tail = relation_tail(tokens);
321+
322+ if (!item_feat_present(tail, "punc"))
323+ return FALSE;
324+
325+ ltoken = item_name(tail);
326+ postpunct = item_feat_string(tail, "punc");
327+
328+ if (strchr(ts->whitespace,'\n') != cst_strrchr(ts->whitespace,'\n'))
329+ /* contains two new lines */
330+ return TRUE;
331+ else if (strchr(postpunct,':') ||
332+ strchr(postpunct,'?') ||
333+ strchr(postpunct,'!'))
334+ return TRUE;
335+ else if (strchr(postpunct,'.') &&
336+ (strlen(ts->whitespace) > 1) &&
337+ strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ",token[0]))
338+ return TRUE;
339+ else if (strchr(postpunct,'.') &&
340+ /* next word starts with a capital */
341+ strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ",token[0]) &&
342+ !strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ",ltoken[strlen(ltoken)-1]))
343+ return TRUE;
344+ else
345+ return FALSE;
346+}
347+
348+static cst_val *
349+sapi_tokentowords(cst_item *i)
350+{
351+ const cst_val *ttwv;
352+
353+ if (item_feat_present(i, "phones"))
354+ return default_tokentowords(i);
355+ if (item_feat_present(i, "bookmark"))
356+ return NULL;
357+
358+ ttwv = utt_feat_val(item_utt(i), "old_tokentowords_func");
359+ if (ttwv)
360+ return (*val_itemfunc(ttwv))(i);
361+ else
362+ return default_tokentowords(i);
363+}
364+
365+static cst_tokenstream *
366+my_ts_open_string(cst_utterance *utt, const char *text)
367+{
368+ cst_tokenstream *ts;
369+
370+ ts = ts_open_string(text,
371+ get_param_string(utt->features,"text_whitespace", cst_ts_default_whitespacesymbols),
372+ get_param_string(utt->features, "text_singlecharsymbols",cst_ts_default_singlecharsymbols),
373+ get_param_string(utt->features, "text_prepunctuation",cst_ts_default_prepunctuationsymbols),
374+ get_param_string(utt->features, "text_postpunctuation", cst_ts_default_postpunctuationsymbols)
375+ );
376+
377+
378+ return ts;
379+}
380+
381+static cst_item *
382+append_new_token(cst_tokenstream *ts, const char *token, cst_relation *rel)
383+{
384+ cst_item *t;
385+
386+ t = relation_append(rel, NULL);
387+ item_set_string(t, "name", token);
388+ if (ts) {
389+ item_set_string(t, "whitespace", ts->whitespace);
390+ item_set_string(t, "prepunctuation", ts->prepunctuation);
391+ item_set_string(t, "punc", ts->postpunctuation);
392+ }
393+ return t;
394+}
395+
396+/* Each step is prev * (10**(1/10)) */
397+static const double sapi_ratetab_foo[21] = {
398+ 3,
399+ 2.68787537952229,
400+ 2.40822468528069,
401+ 2.15766927997459,
402+ 1.93318204493176,
403+ 1.73205080756888,
404+ 1.55184557391536,
405+ 1.39038917031591,
406+ 1.24573093961552,
407+ 1.11612317403391,
408+ 1,
409+ 0.895958459840763,
410+ 0.802741561760231,
411+ 0.719223093324865,
412+ 0.644394014977255,
413+ 0.577350269189626,
414+ 0.517281857971787,
415+ 0.46346305677197,
416+ 0.415243646538506,
417+ 0.372041058011302,
418+ 0.333333333333333
419+};
420+static const double *sapi_ratetab = sapi_ratetab_foo + 10;
421+
422+/* Each step is prev * (2**(1/24)) */
423+static const double sapi_pitchtab_foo[21] = {
424+ 1.33333333333333,
425+ 1.29537592153814,
426+ 1.25849908357559,
427+ 1.22267205760623,
428+ 1.18786495752045,
429+ 1.15404874800819,
430+ 1.12119522033829,
431+ 1.0892769688274,
432+ 1.0582673679788,
433+ 1.02814055027196,
434+ 1.0,
435+ 0.970435455228124,
436+ 0.942809041582063,
437+ 0.915969098305392,
438+ 0.889893236113355,
439+ 0.864559703100672,
440+ 0.839947366596581,
441+ 0.816035695536436,
442+ 0.792804743335147,
443+ 0.770235131248181,
444+ 0.75
445+};
446+static const double *sapi_pitchtab = sapi_pitchtab_foo + 10;
447+
448+static double
449+convert_sapi_rate(int r)
450+{
451+ if (r < -10)
452+ r = -10;
453+ else if (r > 10)
454+ r = 10;
455+ return sapi_ratetab[r];
456+}
457+
458+static double
459+convert_sapi_pitch(int p)
460+{
461+ if (p == -24)
462+ return 0.5;
463+ else if (p == 24)
464+ return 2.0;
465+ else {
466+ if (p < -10)
467+ p = -10;
468+ else if (p > 10)
469+ p = 10;
470+ return sapi_pitchtab[p];
471+ }
472+}
473+
474+static void
475+set_local_prosody(cst_item *t, const SPVSTATE *st)
476+{
477+ int r, p;
478+
479+ if (st->EmphAdj) /* This doesn't really work, though */
480+ item_set_int(t, "EMPH", st->EmphAdj);
481+ if ((r = st->RateAdj))
482+ item_set_float(t, "local_duration_stretch",
483+ convert_sapi_rate(r));
484+ if ((p = st->PitchAdj.MiddleAdj))
485+ item_set_float(t, "local_f0_shift",
486+ convert_sapi_pitch(p));
487+ /* We could do something with RangeAdj, but SAPI apparently
488+ doesn't use it yet and the documentation doesn't say what
489+ the value passed in it would mean, anyway. */
490+}
491+
492+void
493+CFliteTTSEngineObj::speak_frag()
494+{
495+ cst_tokenstream *ts;
496+ const char *token;
497+ cst_item *t;
498+ char *text;
499+ size_t len;
500+
501+ // Sai Krishna + Tim 13 March 2017
502+
503+ //len = wcstombs(NULL, curr_frag->pTextStart, curr_frag->ulTextLen);
504+ //text = cst_alloc(char, len+1);
505+ //wcstombs(text, curr_frag->pTextStart, curr_frag->ulTextLen);
506+
507+ len = WideCharToMultiByte(CP_UTF8, 0, curr_frag->pTextStart, curr_frag->ulTextLen, NULL, 0, NULL, NULL);
508+ text = cst_alloc(char, len + 1);
509+ // WideCharToMultiByte(CP_UTF8, 0, curr_frag->pTextStart, curr_frag->ulTextLen, text, len, NULL, NULL);
510+ text[len] = 0x00;
511+
512+ /////////////////////////
513+
514+ ts = my_ts_open_string(curr_utt, text);
515+ cst_free(text);
516+
517+ while (!ts_eof(ts)) {
518+ // Sai Krishna 14 June 2017. This needs to be modified. Earlier change to ts_getc() didnt help.
519+ token = ts_get(ts);
520+ if (relation_head(tok_rel) && utt_break(ts, token, tok_rel)) {
521+ get_actions_and_do_them();
522+ if (aborted)
523+ goto pod_bay_doors;
524+ if (sentence_skip == 0) {
525+ send_sentence_event(ts->token_pos
526+ + curr_frag->ulTextSrcOffset);
527+ synth_one_utt();
528+ start_new_utt();
529+ } else {
530+ --sentence_skip;
531+ site->CompleteSkip(1);
532+ start_new_utt();
533+ }
534+ }
535+
536+ t = append_new_token(ts, token, tok_rel);
537+ item_set_int(t, "token_pos",
538+ ts->token_pos + curr_frag->ulTextSrcOffset);
539+ item_set_int(t, "token_length", strlen(token));
540+ set_local_prosody(t, &curr_frag->State);
541+ }
542+pod_bay_doors: /* Open the POD bay doors, KAL... */
543+ ts_close(ts);
544+}
545+
546+void
547+CFliteTTSEngineObj::spell_frag()
548+{
549+ int i;
550+
551+ for (i = 0; i < curr_frag->ulTextLen; ++i) {
552+ cst_item *t;
553+ char *c;
554+ int len;
555+
556+ if (!iswspace(curr_frag->pTextStart[i])) {
557+ len = wcstombs(NULL, curr_frag->pTextStart + i, 1);
558+ c = cst_alloc(char, len + 1);
559+ wcstombs(c, curr_frag->pTextStart + i, 1);
560+
561+ t = append_new_token(NULL, c, tok_rel);
562+ item_set_int(t, "token_pos", curr_frag->ulTextSrcOffset + i);
563+ item_set_int(t, "token_length", 1);
564+ set_local_prosody(t, &curr_frag->State);
565+ cst_free(c);
566+ }
567+ }
568+}
569+
570+void
571+CFliteTTSEngineObj::pronounce_frag()
572+{
573+ cst_val *phones;
574+ cst_item *t;
575+
576+ if (!pronouncefunc
577+ || (phones = (*pronouncefunc)(curr_frag->State.pPhoneIds)) == NULL)
578+ return;
579+
580+ t = append_new_token(NULL, "", tok_rel);
581+ item_set_int(t, "token_pos", curr_frag->ulTextSrcOffset);
582+ item_set_int(t, "token_length", curr_frag->ulTextLen);
583+ set_local_prosody(t, &curr_frag->State);
584+ item_set(t, "phones", phones);
585+}
586+
587+void
588+CFliteTTSEngineObj::silence_frag()
589+{
590+ int sps, silence;
591+ short *buf;
592+ ULONG b;
593+
594+ /* Force an utterance break */
595+ synth_one_utt();
596+ start_new_utt();
597+
598+ sps = get_param_int(curr_vox->features, "sample_rate", 16000);
599+ silence = sps * curr_frag->State.SilenceMSecs / 1000;
600+ buf = cst_alloc(short, 512);
601+ while (silence > 0) {
602+ b = ((silence < 512) ? silence : 512) * sizeof(*buf);
603+ site->Write(buf, b, &b);
604+ bcount += b;
605+ silence -= (b / sizeof(*buf));
606+ }
607+}
608+
609+void
610+CFliteTTSEngineObj::set_bookmark()
611+{
612+ size_t len;
613+ char *bookmark;
614+ cst_item *t;
615+
616+ len = wcstombs(NULL, curr_frag->pTextStart, curr_frag->ulTextLen);
617+ bookmark = cst_alloc(char, len+1);
618+ wcstombs(bookmark, curr_frag->pTextStart, curr_frag->ulTextLen);
619+
620+ t = relation_append(tok_rel, NULL);
621+ item_set_string(t, "name", "");
622+ item_set_string(t, "bookmark", bookmark);
623+ item_set_int(t, "token_pos", curr_frag->ulTextSrcOffset);
624+ item_set_int(t, "token_length", curr_frag->ulTextLen);
625+ cst_free(bookmark);
626+}
627+
628+void
629+CFliteTTSEngineObj::get_actions_and_do_them()
630+{
631+ DWORD actions;
632+
633+ actions = site->GetActions();
634+ if (actions & SPVES_ABORT) {
635+ aborted = 1;
636+ return;
637+ }
638+ if (actions & SPVES_SKIP) {
639+ SPVSKIPTYPE stype;
640+ long count;
641+
642+ site->GetSkipInfo(&stype, &count);
643+ if (stype == SPVST_SENTENCE) {
644+ sentence_skip += count;
645+ }
646+ }
647+ if (actions & SPVES_RATE) {
648+ long adj;
649+
650+ site->GetRate(&adj);
651+ utt_set_feat_float(curr_utt, "duration_stretch",
652+ convert_sapi_rate(adj));
653+ feat_set_float(curr_vox->features, "duration_stretch",
654+ convert_sapi_rate(adj));
655+ }
656+ if (actions & SPVES_VOLUME) {
657+ USHORT adj;
658+
659+ site->GetVolume(&adj);
660+ volume = adj;
661+ }
662+}
663+
664+STDMETHODIMP
665+CFliteTTSEngineObj::Speak(DWORD dwSpeakFlags,
666+ REFGUID rguidFormatId,
667+ const WAVEFORMATEX * pWaveFormatEx,
668+ const SPVTEXTFRAG* pTextFragList,
669+ ISpTTSEngineSite* pOutputSite)
670+{
671+ if (curr_vox == NULL)
672+ return E_POINTER;
673+
674+ start_new_utt();
675+ bcount = 0;
676+ sentence_start = 0;
677+ sentence_skip = 0;
678+ aborted = 0;
679+ volume = 100;
680+ site = pOutputSite;
681+
682+ for (curr_frag = pTextFragList; curr_frag && !aborted;
683+ curr_frag = curr_frag->pNext) {
684+ get_actions_and_do_them();
685+
686+ switch (curr_frag->State.eAction) {
687+ case SPVA_Speak:
688+ speak_frag();
689+ break;
690+ case SPVA_SpellOut:
691+ spell_frag();
692+ break;
693+ case SPVA_Pronounce:
694+ pronounce_frag();
695+ break;
696+ case SPVA_Silence:
697+ silence_frag();
698+ break;
699+ case SPVA_Bookmark:
700+ set_bookmark();
701+ break;
702+ default:
703+ break;
704+ }
705+ }
706+
707+ if (tok_rel && relation_tail(tok_rel))
708+ send_sentence_event(item_feat_int(relation_tail(tok_rel), "token_pos")
709+ + item_feat_int(relation_tail(tok_rel),
710+ "token_length"));
711+ synth_one_utt();
712+
713+ delete_utterance(curr_utt);
714+ curr_utt = NULL;
715+ tok_rel = NULL;
716+ site = NULL;
717+
718+ return S_OK;
719+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t