blob: 56db48b0d92885ee87fdfb8c1cedd7502ce0e2d8 [file] [log] [blame]
/*
* ivona_client.c - Speech Dispatcher backend for Ivona (IVO Software)
*
* Copyright (C) Bohdan R. Rau 2008 <ethanak@polip.com>
*
* 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/>.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <glib.h>
#include <libdumbtts.h>
#include "module_utils.h"
#include "ivona_client.h"
static struct sockaddr_in sinadr;
static int ivona_new_sock(void)
{
int fd;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
return -1;
if (connect(fd, (struct sockaddr *)&sinadr, sizeof(sinadr)) < 0) {
close(fd);
return -1;
}
return fd;
}
int ivona_init_sock(const char *host, int port)
{
int fd;
if (!inet_aton(host, &sinadr.sin_addr)) {
struct hostent *h = gethostbyname(host);
if (!h)
return -1;
memcpy(&sinadr.sin_addr, h->h_addr, sizeof(struct in_addr));
endhostent();
}
sinadr.sin_family = AF_INET;
sinadr.sin_port = htons(port);
fd = ivona_new_sock();
if (fd < 0)
return -1;
close(fd);
return 0;
}
#define BASE_WAVE_SIZE 65536
#define STEP_WAVE_SIZE 32768
int ivona_send_string(const char *to_say)
{
int fd = ivona_new_sock();
if (fd < 0)
return -1;
write(fd, to_say, strlen(to_say));
write(fd, "\n", 1);
return fd;
}
char *ivona_get_wave_fd(int fd, int *nsamples, int *offset)
{
int got, i;
char *ivona_wave;
int wave_size;
int wave_length;
short *w;
wave_size = BASE_WAVE_SIZE;
wave_length = 0;
ivona_wave = g_malloc(wave_size);
for (;;) {
if (wave_size < wave_length + 8192) {
ivona_wave =
g_realloc(ivona_wave, wave_size + STEP_WAVE_SIZE);
wave_size += STEP_WAVE_SIZE;
}
DBG("Have place for %d bytes", wave_size - wave_length);
got =
read(fd, ivona_wave + wave_length, wave_size - wave_length);
DBG("Wave part at %d size %d", wave_length, got);
if (got <= 0)
break;
wave_length += got;
}
close(fd);
w = (short *)ivona_wave;
for (i = wave_length / 2 - 1; i >= 0; i--)
if (w[i])
break;
if (i < 100) {
g_free(ivona_wave);
return NULL;
}
DBG("Trimmed %d samples at end", wave_length / 2 - i - 1);
*nsamples = i + 1;
for (i = 0; i < *nsamples; i++)
if (w[i])
break;
DBG("Should trim %d bytes at start", i);
*offset = i;
(*nsamples) -= i;
return ivona_wave;
}
/*
static char *ivona_get_wave_from_cache(char *to_say,int *nsamples);
void ivona_store_wave_in_cache(char *to_say,char *wave,int nsamples);
*/
char *ivona_get_wave(const char *to_say, int *nsamples, int *offset)
{
int fd;
char *s;
s = ivona_get_wave_from_cache(to_say, nsamples);
if (s) {
*offset = 0;
return s;
}
fd = ivona_send_string(to_say);
if (fd < 0)
return NULL;
s = ivona_get_wave_fd(fd, nsamples, offset);
if (s)
ivona_store_wave_in_cache(to_say, s + 2 * (*offset), *nsamples);
return s;
}
/* Plays the specified audio file - from ibmtts/espeak module */
void play_icon(const char *path, const char *name)
{
char *buf = g_strdup_printf("%s/%s", path, name);
module_play_file(buf);
g_free(buf);
}
#define IVONA_CACHE_SIZE 256
#define IVONA_CACHE_MAX_SAMPLES 65536
static int ivona_cache_count;
static struct ivona_cache {
struct ivona_cache *succ, *pred;
int count;
char str[16];
int samples;
char *wave;
} ica_head, ica_tail, icas[IVONA_CACHE_SIZE];
void ivona_init_cache(void)
{
ica_head.pred = &ica_tail;
ica_tail.succ = &ica_head;
}
void ica_tohead(struct ivona_cache *ica)
{
if (ica->pred)
ica->pred->succ = ica->succ;
if (ica->succ)
ica->succ->pred = ica->pred;
ica->pred = ica_head.pred;
ica->pred->succ = ica;
ica->succ = &ica_head;
ica_head.pred = ica;
}
static struct ivona_cache *find_min_count(void)
{
int cnt = 0x7fffffff;
struct ivona_cache *ica, *found;
found = NULL;
int i;
for (ica = ica_tail.succ, i = 0;
i < IVONA_CACHE_SIZE / 2 && ica->samples; ica = ica->succ) {
if (ica->count < cnt) {
cnt = ica->count;
found = ica;
}
}
if (found) {
for (ica = ica_tail.succ; ica->samples; ica = ica->succ) {
if (ica->count > 1)
ica->count--;
}
}
return found;
}
void ivona_store_wave_in_cache(const char *str, const char *wave, int samples)
{
struct ivona_cache *ica;
if (strlen(str) > IVONA_CACHE_MAX_STRLEN)
return;
if (ivona_cache_count < IVONA_CACHE_SIZE) {
ica = &icas[ivona_cache_count++];
} else {
ica = find_min_count();
if (!ica)
return;
g_free(ica->wave);
}
ica->count = 1;
ica->wave = g_malloc(samples * 2);
memcpy(ica->wave, wave, samples * 2);
ica->samples = samples;
strcpy(ica->str, str);
ica_tohead(ica);
DBG("Stored cache %s", str);
}
char *ivona_get_wave_from_cache(const char *to_say, int *samples)
{
struct ivona_cache *ica;
if (strlen(to_say) > IVONA_CACHE_MAX_STRLEN)
return NULL;
for (ica = ica_tail.succ; ica && ica->samples; ica = ica->succ) {
DBG("Cache cmp '%s'='%s'", ica->str, to_say);
if (!strcmp(ica->str, to_say)) {
char *wave = g_malloc(ica->samples * 2);
memcpy(wave, ica->wave, ica->samples * 2);
*samples = ica->samples;
ica->count++;
ica_tohead(ica);
return wave;
}
}
return NULL;
}