blob: 44114d6911c32d5e3aecd4afcead7a5156e23f5d [file] [log] [blame]
/*
* kali.cpp - Speech Dispatcher backend for Kali
*
* Copyright (C)2016 Hypra
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* $Id: kali.c,v 1.59 2008-06-09 10:38:02 hanke Exp $
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <kali/Kali/kali.h>
#include <speechd_types.h>
#include "module_utils.h"
#define MODULE_NAME "kali"
#define MODULE_VERSION "0.0"
#define DEBUG_MODULE 1
DECLARE_DEBUG();
/* Thread and process control */
static int kali_speaking = 0;
static char *buf;
signed int kali_volume = 0;
SPDVoice **kali_voice_list = NULL;
/* Internal functions prototypes */
static void kali_set_rate(signed int rate);
static void kali_set_pitch(signed int pitch);
static void kali_set_volume(signed int volume);
static void kali_set_punctuation_mode(SPDPunctuation punct);
static void kali_set_voice(char *voice);
static SPDVoice **kali_get_voices();
static int kali_stop = 0;
MOD_OPTION_1_INT(KaliMaxChunkLength);
MOD_OPTION_1_STR(KaliDelimiters);
MOD_OPTION_1_INT(KaliNormalRate);
MOD_OPTION_1_INT(KaliNormalVolume);
MOD_OPTION_1_INT(KaliNormalPitch);
MOD_OPTION_1_STR(KaliVoiceParameters);
MOD_OPTION_1_INT(KaliExpandAbbreviations);
MOD_OPTION_1_INT(KaliMulticasesWords);
/* Public functions */
int module_load(void)
{
INIT_SETTINGS_TABLES();
REGISTER_DEBUG();
MOD_OPTION_1_INT_REG(KaliMaxChunkLength, 4999);
MOD_OPTION_1_STR_REG(KaliDelimiters, ".");
MOD_OPTION_1_INT_REG(KaliNormalRate, 70);
MOD_OPTION_1_INT_REG(KaliNormalVolume, 10);
MOD_OPTION_1_INT_REG(KaliNormalPitch, 6);
MOD_OPTION_1_STR_REG(KaliVoiceParameters, "Patrick");
MOD_OPTION_1_INT_REG(KaliExpandAbbreviations, 1);
MOD_OPTION_1_INT_REG(KaliMulticasesWords, 1);
return 0;
}
int module_init(char **status_info)
{
DBG("Module init");
module_audio_set_server();
/* Init kali and register a new voice */
initGlobal();
initParle();
initTrans();
initAnalyse();
initKali();
SetSortieSonMultiKaliStd(0, false); //sound output
SetSortieBufMultiKaliStd(0, true); //Buffer output
SetDebitKali(KaliNormalRate);
SetVolumeKali(KaliNormalVolume);
SetHauteurKali(KaliNormalPitch);
kali_voice_list = kali_get_voices();
if (!kali_voice_list) {
*status_info = g_strdup("Kali has no voice installed");
return -1;
}
kali_set_voice(KaliVoiceParameters);
DBG("KaliMaxChunkLength = %d\n", KaliMaxChunkLength);
DBG("KaliDelimiters = %s\n", KaliDelimiters);
DBG("KaliExpandAbbreviations = %d\n", KaliExpandAbbreviations);
DBG("KaliMulticasesWords = %d\n", KaliMulticasesWords);
kali_speaking = 0;
buf = (char *)g_malloc((KaliMaxChunkLength + 1) * sizeof(char));
*status_info = g_strdup("Kali initialized successfully.");
return 0;
}
SPDVoice **module_list_voices(void)
{
return kali_voice_list;
}
void module_speak_sync(const char * data, size_t len, SPDMessageType msgtype)
{
DBG("Requested data: |%s|\n", data);
if (kali_speaking) {
module_speak_error();
DBG("Speaking when requested to write");
return;
}
kali_speaking = 1;
kali_stop = 0;
AudioTrack track;
#if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
AudioFormat format = SPD_AUDIO_BE;
#else
AudioFormat format = SPD_AUDIO_LE;
#endif
const AudioTrackKali *wav;
char *kali_message;
kali_message = module_strip_ssml(data);
if (KaliMulticasesWords)
kali_message = module_multicases_string(kali_message);
module_speak_ok();
/* Setting voice */
UPDATE_PARAMETER(rate, kali_set_rate);
UPDATE_PARAMETER(volume, kali_set_volume);
UPDATE_PARAMETER(pitch, kali_set_pitch);
UPDATE_PARAMETER(punctuation_mode, kali_set_punctuation_mode);
UPDATE_STRING_PARAMETER(voice.name, kali_set_voice);
kali_set_voice(msg_settings.voice.name);
unsigned int pos;
int bytes;
pos = 0;
module_report_event_begin();
while (1) {
/* Process server events in case we were told to stop in between */
module_process(STDIN_FILENO, 0);
if (kali_stop) {
DBG("Stop in child, terminating");
module_report_event_stop();
break;
}
bytes =
module_get_message_part(kali_message, buf, &pos,
KaliMaxChunkLength,
KaliDelimiters);
if (bytes < 0) {
DBG("End of message");
module_report_event_end();
break;
}
if (bytes == 0) {
DBG("No data");
module_report_event_end();
break;
}
buf[bytes] = 0;
DBG("Returned %d bytes from get_part\n", bytes);
DBG("Text to synthesize is '%s'\n", buf);
DBG("Trying to synthesize text");
MessageKali((unsigned char *)buf);
DBG("Waiting for synthesis");
while (QueryIndexKali() > 0)
usleep(1000);
wav =
(const AudioTrackKali *)
GetBufMultiKaliStd(0);
DBG("Got buffer");
if (wav == NULL) {
DBG("Stop in child, terminating");
module_report_event_stop();
break;
}
track.num_samples = wav->num_samples;
track.num_channels = wav->num_channels;
track.sample_rate = wav->sample_rate;
track.bits = wav->bits;
track.samples = (signed short *)wav->samples;
DBG("Got %d samples", track.num_samples);
if (track.samples != NULL) {
DBG("Playing part of the message");
module_tts_output_server(&track, format);
}
}
g_free(kali_message);
kali_speaking = 0;
}
int module_stop(void)
{
DBG("kali: stop()\n");
kali_stop = 1;
return 0;
}
size_t module_pause(void)
{
DBG("pause requested\n");
if (kali_speaking) {
DBG("Kali doesn't support pause, stopping\n");
module_stop();
return -1;
} else {
return 0;
}
}
int module_close(void)
{
DBG("kali: close()\n");
DBG("Stopping speech");
if (kali_speaking) {
module_stop();
}
quitteAnalyse();
quitteTrans();
quitteParle();
quitteGlobal();
return 0;
}
/* Internal functions */
static void kali_set_rate(signed int rate)
{
short speed;
assert(rate >= -100 && rate <= +100);
if (rate < 0)
speed = GetDebitDefautKaliStd() - rate * (GetDebitMinKaliStd() - GetDebitDefautKaliStd()) / 100;
else
speed = GetDebitDefautKaliStd() + rate * (GetDebitMaxKaliStd() - GetDebitDefautKaliStd()) / 100;
SetDebitKali(speed);
}
static void kali_set_volume(signed int volume)
{
short vol;
assert(volume >= -100 && volume <= +100);
if (volume < 0)
vol = GetVolumeDefautKaliStd() - volume * (GetVolumeMinKaliStd() - GetVolumeDefautKaliStd()) / 100;
else
vol = GetVolumeDefautKaliStd() + volume * (GetVolumeMaxKaliStd() - GetVolumeDefautKaliStd()) / 100;
SetVolumeKali(vol);
}
static void kali_set_pitch(signed int pitch)
{
short ptch;
assert(pitch >= -100 && pitch <= +100);
if (pitch < 0)
ptch = GetHauteurDefautKaliStd() - pitch * (GetHauteurMinKaliStd() - GetHauteurDefautKaliStd()) / 100;
else
ptch = GetHauteurDefautKaliStd() + pitch * (GetHauteurMaxKaliStd() - GetHauteurDefautKaliStd()) / 100;
SetHauteurKali(ptch);
}
void kali_set_punctuation_mode(SPDPunctuation punct)
{
switch (punct) {
case SPD_PUNCT_NONE:
if (KaliExpandAbbreviations)
SetModeLectureKali(0);
else
SetModeLectureKali(1);
break;
case SPD_PUNCT_SOME:
SetModeLectureKali(2);
break;
case SPD_PUNCT_MOST:
/* XXX approximation */
SetModeLectureKali(2);
break;
case SPD_PUNCT_ALL:
SetModeLectureKali(3);
break;
default:
break;
}
}
static void kali_set_voice(char *voice)
{
short i, nlang;
char *v = voice;
if (v == NULL)
v = KaliVoiceParameters;
for (i = 0; kali_voice_list[i] != NULL; i++) {
if (strcasecmp(kali_voice_list[i]->name, v) == 0) {
nlang = GetNLangueVoixKaliStd(i + 1);
SetLangueKali(nlang);
SetVoixKali(i + 1);
break;
}
}
}
static SPDVoice **kali_get_voices()
{
short i;
SPDVoice **result = NULL;
short num_voices;
static char voice[128];
short nlang;
static char language[128];
num_voices = GetNbVoixKali();
if (num_voices == 0)
return NULL;
DBG("Kali: %d voices total.", num_voices);
result = g_new0(SPDVoice *, num_voices+1);
for (i = 0; i < num_voices; i++) {
result[i] = g_new0(SPDVoice, 1);
GetNomVoixKali(i + 1, voice);
if (!memchr(voice, '\0', sizeof(voice))) {
voice[sizeof(voice)-1] = '\0';
MSG(1, "Kali: voice name overflow! truncated to %s but possibly overflowed memory...", voice);
}
result[i]->name = (char *)g_strdup(voice);
nlang = GetNLangueVoixKaliStd(i + 1);
GetNomLangueKali(nlang, language);
if (!memchr(language, '\0', sizeof(language))) {
language[sizeof(language)-1] = '\0';
MSG(1, "Kali: language name overflow! truncated to %s but possibly overflowed memory...", language);
}
result[i]->language = (char *)g_strdup(language);
result[i]->variant = NULL;
}
result[i] = NULL;
return result;
}