1
2
3 /*
4 * kali.cpp - Speech Dispatcher backend for Kali
5 *
6 * Copyright (C)2016 Hypra
7 *
8 * This is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This software 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 GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 *
21 * $Id: kali.c,v 1.59 2008-06-09 10:38:02 hanke Exp $
22 */
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <kali/Kali/kali.h>
28 extern "C" {
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32 #include <semaphore.h>
33 #include "spd_audio.h"
34
35 #include <speechd_types.h>
36
37 #include "module_utils.h"
38 }
39 #define MODULE_NAME "kali"
40 #define MODULE_VERSION "0.0"
41 #define DEBUG_MODULE 1
42 DECLARE_DEBUG();
43
44 /* Thread and process control */
45 static int kali_speaking = 0;
46
47 static pthread_t kali_speak_thread;
48 static sem_t kali_semaphore;
49
50 static char **kali_message;
51 static SPDMessageType kali_message_type;
52
53 static int kali_position = 0;
54 static int kali_pause_requested = 0;
55
56 signed int kali_volume = 0;
57 SPDVoice **kali_voice_list = NULL;
58
59 /* Internal functions prototypes */
60 static void kali_set_rate(signed int rate);
61 static void kali_set_pitch(signed int pitch);
62 static void kali_set_volume(signed int volume);
63 static void kali_set_punctuation_mode(SPDPunctuation punct);
64 static void kali_set_voice(char *voice);
65
66 static SPDVoice **kali_get_voices();
67 static void *_kali_speak(void *);
68
69 int kali_stop = 0;
70
71 MOD_OPTION_1_INT(KaliMaxChunkLength);
72 MOD_OPTION_1_STR(KaliDelimiters);
73 MOD_OPTION_1_INT(KaliNormalRate);
74 MOD_OPTION_1_INT(KaliNormalVolume);
75 MOD_OPTION_1_INT(KaliNormalPitch);
76 MOD_OPTION_1_STR(KaliVoiceParameters);
77 MOD_OPTION_1_INT(KaliExpandAbbreviations);
78
79 /* Public functions */
80
module_load(void)81 int module_load(void)
82 {
83 INIT_SETTINGS_TABLES();
84
85 REGISTER_DEBUG();
86
87 MOD_OPTION_1_INT_REG(KaliMaxChunkLength, 4999);
88 MOD_OPTION_1_STR_REG(KaliDelimiters, ".");
89 MOD_OPTION_1_INT_REG(KaliNormalRate, 70);
90 MOD_OPTION_1_INT_REG(KaliNormalVolume, 10);
91 MOD_OPTION_1_INT_REG(KaliNormalPitch, 6);
92 MOD_OPTION_1_STR_REG(KaliVoiceParameters, "Patrick");
93 MOD_OPTION_1_INT_REG(KaliExpandAbbreviations, 1);
94
95 return 0;
96 }
97
98 #define ABORT(msg) g_string_append(info, msg); \
99 DBG("FATAL ERROR:", info->str); \
100 *status_info = info->str; \
101 g_string_free(info, 0); \
102 return -1;
103
module_init(char ** status_info)104 int module_init(char **status_info)
105 {
106 int ret;
107 GString *info;
108
109 DBG("Module init");
110 INIT_INDEX_MARKING();
111
112 *status_info = NULL;
113 info = g_string_new("");
114
115 /* Init kali and register a new voice */
116 initGlobal();
117 initParle();
118 initTrans();
119 initAnalyse();
120 initKali();
121 SetSortieSonMultiKaliStd(0, false); //sound output
122 SetSortieBufMultiKaliStd(0, true); //Buffer output
123 SetDebitKali(KaliNormalRate);
124 SetVolumeKali(KaliNormalVolume);
125 SetHauteurKali(KaliNormalPitch);
126 kali_voice_list = kali_get_voices();
127 kali_set_voice(KaliVoiceParameters);
128
129 DBG("KaliMaxChunkLength = %d\n", KaliMaxChunkLength);
130 DBG("KaliDelimiters = %s\n", KaliDelimiters);
131 DBG("KaliExpandAbbreviations = %d\n", KaliExpandAbbreviations);
132
133 kali_message = (char **)g_malloc(sizeof(char *));
134 *kali_message = NULL;
135
136 sem_init(&kali_semaphore, 0, 0);
137
138 DBG("Kali: creating new thread for kali_speak\n");
139 kali_speaking = 0;
140 ret = pthread_create(&kali_speak_thread, NULL, _kali_speak, NULL);
141 if (ret != 0) {
142 DBG("Kali: thread failed\n");
143 *status_info =
144 g_strdup("The module couldn't initialize threads "
145 "This could be either an internal problem or an "
146 "architecture problem. If you are sure your architecture "
147 "supports threads, please report a bug.");
148 return -1;
149 }
150
151 *status_info = g_strdup("Kali initialized successfully.");
152
153 return 0;
154 }
155
156 #undef ABORT
157
module_list_voices(void)158 SPDVoice **module_list_voices(void)
159 {
160 return kali_voice_list;
161 }
162
module_speak(gchar * data,size_t bytes,SPDMessageType msgtype)163 int module_speak(gchar * data, size_t bytes, SPDMessageType msgtype)
164 {
165 DBG("write()\n");
166
167 if (kali_speaking) {
168 DBG("Speaking when requested to write");
169 return 0;
170 }
171
172 DBG("Requested data: |%s|\n", data);
173
174 if (*kali_message != NULL) {
175 g_free(*kali_message);
176 *kali_message = NULL;
177 }
178 *kali_message = module_strip_ssml(data);
179 kali_message_type = SPD_MSGTYPE_TEXT;
180
181 /* Setting voice */
182 UPDATE_PARAMETER(rate, kali_set_rate);
183 UPDATE_PARAMETER(volume, kali_set_volume);
184 UPDATE_PARAMETER(pitch, kali_set_pitch);
185 UPDATE_PARAMETER(punctuation_mode, kali_set_punctuation_mode);
186 UPDATE_STRING_PARAMETER(voice.name, kali_set_voice);
187 kali_set_voice(msg_settings.voice.name);
188
189 /* Send semaphore signal to the speaking thread */
190 kali_speaking = 1;
191 sem_post(&kali_semaphore);
192
193 DBG("Kali: leaving write() normally\n\r");
194 return bytes;
195 }
196
module_stop(void)197 int module_stop(void)
198 {
199 int ret;
200 DBG("kali: stop()\n");
201
202 kali_stop = 1;
203 if (module_audio_id) {
204 DBG("Stopping audio");
205 ret = spd_audio_stop(module_audio_id);
206 if (ret != 0)
207 DBG("WARNING: Non 0 value from spd_audio_stop: %d",
208 ret);
209 }
210
211 return 0;
212 }
213
module_pause(void)214 size_t module_pause(void)
215 {
216 DBG("pause requested\n");
217 if (kali_speaking) {
218 DBG("Kali doesn't support pause, stopping\n");
219
220 module_stop();
221
222 return -1;
223 } else {
224 return 0;
225 }
226 }
227
module_close(void)228 int module_close(void)
229 {
230
231 DBG("kali: close()\n");
232
233 DBG("Stopping speech");
234 if (kali_speaking) {
235 module_stop();
236 }
237
238 quitteAnalyse();
239 quitteTrans();
240 quitteParle();
241 quitteGlobal();
242
243 DBG("Terminating threads");
244 if (module_terminate_thread(kali_speak_thread) != 0)
245 return -1;
246
247 sem_destroy(&kali_semaphore);
248
249 return 0;
250 }
251
252 /* Internal functions */
253
_kali_speak(void * nothing)254 void *_kali_speak(void *nothing)
255 {
256 AudioTrack track;
257 #if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
258 AudioFormat format = SPD_AUDIO_BE;
259 #else
260 AudioFormat format = SPD_AUDIO_LE;
261 #endif
262 const AudioTrackKali *wav;
263 unsigned int pos;
264 char *buf;
265 int bytes;
266 int ret;
267
268 DBG("kali: speaking thread starting.......\n");
269
270 set_speaking_thread_parameters();
271
272 while (1) {
273 sem_wait(&kali_semaphore);
274 DBG("Semaphore on\n");
275
276 kali_stop = 0;
277 kali_speaking = 1;
278
279 /* TODO: free(buf) */
280 buf = (char *)g_malloc((KaliMaxChunkLength + 1) * sizeof(char));
281 pos = 0;
282 module_report_event_begin();
283 while (1) {
284 if (kali_stop) {
285 DBG("Stop in child, terminating");
286 kali_speaking = 0;
287 module_report_event_stop();
288 break;
289 }
290 bytes =
291 module_get_message_part(*kali_message, buf, &pos,
292 KaliMaxChunkLength,
293 KaliDelimiters);
294
295 if (bytes < 0) {
296 DBG("End of message");
297 kali_speaking = 0;
298 module_report_event_end();
299 break;
300 }
301
302 buf[bytes] = 0;
303 DBG("Returned %d bytes from get_part\n", bytes);
304 DBG("Text to synthesize is '%s'\n", buf);
305
306 if (kali_pause_requested && (current_index_mark != -1)) {
307 DBG("Pause requested in parent, position %d\n",
308 current_index_mark);
309 kali_pause_requested = 0;
310 kali_position = current_index_mark;
311 break;
312 }
313
314 if (bytes > 0) {
315 DBG("Speaking in child...");
316
317 DBG("Trying to synthesize text");
318 MessageKali((unsigned char *)buf);
319 while (QueryIndexKali() > 0)
320 ;
321 wav =
322 (const AudioTrackKali *)
323 GetBufMultiKaliStd(0);
324
325 if (wav == NULL) {
326 DBG("Stop in child, terminating");
327 kali_speaking = 0;
328 module_report_event_stop();
329 break;
330 }
331
332 track.num_samples = wav->num_samples;
333 track.num_channels = wav->num_channels;
334 track.sample_rate = wav->sample_rate;
335 track.bits = wav->bits;
336 track.samples = (signed short *)wav->samples;
337
338 DBG("Got %d samples", track.num_samples);
339 if (track.samples != NULL) {
340 if (kali_stop) {
341 DBG("Stop in child, terminating");
342 kali_speaking = 0;
343 module_report_event_stop();
344 break;
345 }
346 DBG("Playing part of the message");
347 ret = module_tts_output(track, format);
348 if (ret < 0)
349 DBG("ERROR: failed to play the track");
350 if (kali_stop) {
351 DBG("Stop in child, terminating (s)");
352 kali_speaking = 0;
353 module_report_event_stop();
354 break;
355 }
356 }
357 } else if (bytes == -1) {
358 DBG("End of data in speaking thread");
359 kali_speaking = 0;
360 module_report_event_end();
361 break;
362 } else {
363 kali_speaking = 0;
364 module_report_event_end();
365 break;
366 }
367
368 if (kali_stop) {
369 DBG("Stop in child, terminating");
370 kali_speaking = 0;
371 module_report_event_stop();
372 break;
373 }
374 }
375 kali_stop = 0;
376 g_free(buf);
377 }
378
379 kali_speaking = 0;
380
381 DBG("kali: speaking thread ended.......\n");
382
383 pthread_exit(NULL);
384 }
385
kali_set_rate(signed int rate)386 static void kali_set_rate(signed int rate)
387 {
388 short speed;
389
390 assert(rate >= -100 && rate <= +100);
391 if (rate < 0)
392 speed = GetDebitDefautKaliStd() - rate * (GetDebitMinKaliStd() - GetDebitDefautKaliStd()) / 100;
393 else
394 speed = GetDebitDefautKaliStd() + rate * (GetDebitMaxKaliStd() - GetDebitDefautKaliStd()) / 100;
395 SetDebitKali(speed);
396 }
397
kali_set_volume(signed int volume)398 static void kali_set_volume(signed int volume)
399 {
400 short vol;
401
402 assert(volume >= -100 && volume <= +100);
403 if (volume < 0)
404 vol = GetVolumeDefautKaliStd() - volume * (GetVolumeMinKaliStd() - GetVolumeDefautKaliStd()) / 100;
405 else
406 vol = GetVolumeDefautKaliStd() + volume * (GetVolumeMaxKaliStd() - GetVolumeDefautKaliStd()) / 100;
407 SetVolumeKali(vol);
408 }
409
kali_set_pitch(signed int pitch)410 static void kali_set_pitch(signed int pitch)
411 {
412 short ptch;
413
414 assert(pitch >= -100 && pitch <= +100);
415 if (pitch < 0)
416 ptch = GetHauteurDefautKaliStd() - pitch * (GetHauteurMinKaliStd() - GetHauteurDefautKaliStd()) / 100;
417 else
418 ptch = GetHauteurDefautKaliStd() + pitch * (GetHauteurMaxKaliStd() - GetHauteurDefautKaliStd()) / 100;
419 SetHauteurKali(ptch);
420 }
421
kali_set_punctuation_mode(SPDPunctuation punct)422 void kali_set_punctuation_mode(SPDPunctuation punct)
423 {
424 switch (punct) {
425 case SPD_PUNCT_NONE:
426 if (KaliExpandAbbreviations)
427 SetModeLectureKali(0);
428 else
429 SetModeLectureKali(1);
430 break;
431 case SPD_PUNCT_SOME:
432 SetModeLectureKali(2);
433 break;
434 case SPD_PUNCT_MOST:
435 /* XXX approximation */
436 SetModeLectureKali(2);
437 break;
438 case SPD_PUNCT_ALL:
439 SetModeLectureKali(3);
440 break;
441 default:
442 break;
443 }
444 }
445
kali_set_voice(char * voice)446 static void kali_set_voice(char *voice)
447 {
448 short i, nlang;
449 char *v = voice;
450
451 if (v == NULL)
452 v = KaliVoiceParameters;
453
454 for (i = 0; kali_voice_list[i] != NULL; i++) {
455 if (strcasecmp(kali_voice_list[i]->name, v) == 0) {
456 nlang = GetNLangueVoixKaliStd(i + 1);
457 SetLangueKali(nlang);
458 SetVoixKali(i + 1);
459 break;
460 }
461 }
462 }
463
kali_get_voices()464 static SPDVoice **kali_get_voices()
465 {
466 short i;
467 SPDVoice **result = NULL;
468 short num_voices;
469 char *voice;
470 short nlang;
471 char *language;
472
473 num_voices = GetNbVoixKali();
474 DBG("Kali: %d voices total.", num_voices);
475 voice = (char *)g_malloc(12);
476 language = (char *)g_malloc(9);
477 result = g_new0(SPDVoice *, num_voices);
478
479 for (i = 0; i < num_voices; i++) {
480 result[i] = g_new0(SPDVoice, 1);
481 GetNomVoixKali(i + 1, voice);
482 result[i]->name = (char *)g_strdup(voice);
483 nlang = GetNLangueVoixKaliStd(i + 1);
484 GetNomLangueKali(nlang, language);
485 result[i]->language = (char *)g_strdup(language);
486 result[i]->variant = NULL;
487 }
488 result[i] = NULL;
489
490 g_free(voice);
491 g_free(language);
492
493 return result;
494 }
495