ENS_OK = 0, | ENS_OK = 0, | ||||
ENS_COMPILE_ERROR = 0x100001FF, | ENS_COMPILE_ERROR = 0x100001FF, | ||||
ENS_VERSION_MISMATCH = 0x100002FF, | ENS_VERSION_MISMATCH = 0x100002FF, | ||||
ENS_FIFO_BUFFER_FULL = 0x100003FF, | |||||
} espeak_ng_STATUS; | } espeak_ng_STATUS; | ||||
typedef enum { | typedef enum { |
/* | /* | ||||
* Copyright (C) 2007, Gilles Casse <[email protected]> | * Copyright (C) 2007, Gilles Casse <[email protected]> | ||||
* Copyright (C) 2013-2015 Reece H. Dunn | |||||
* Copyright (C) 2013-2016 Reece H. Dunn | |||||
* | * | ||||
* This program is free software; you can redistribute it and/or modify | * This program is free software; you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
#include <sys/time.h> | #include <sys/time.h> | ||||
#include <time.h> | #include <time.h> | ||||
#include "espeak_ng.h" | |||||
#include "speech.h" | #include "speech.h" | ||||
#include "fifo.h" | #include "fifo.h" | ||||
#include "wave.h" | #include "wave.h" | ||||
static void *say_thread(void *); | static void *say_thread(void *); | ||||
static espeak_ERROR push(t_espeak_command *the_command); | |||||
static espeak_ng_STATUS push(t_espeak_command *the_command); | |||||
static t_espeak_command *pop(); | static t_espeak_command *pop(); | ||||
static void init(int process_parameters); | static void init(int process_parameters); | ||||
static int node_counter = 0; | static int node_counter = 0; | ||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | } | ||||
espeak_ERROR fifo_add_command(t_espeak_command *the_command) | |||||
espeak_ng_STATUS fifo_add_command(t_espeak_command *the_command) | |||||
{ | { | ||||
int a_status = pthread_mutex_lock(&my_mutex); | |||||
espeak_ERROR a_error = EE_OK; | |||||
espeak_ng_STATUS status; | |||||
if ((status = pthread_mutex_lock(&my_mutex)) != ENS_OK) | |||||
return status; | |||||
if (!a_status) { | |||||
a_error = push(the_command); | |||||
a_status = pthread_mutex_unlock(&my_mutex); | |||||
} | |||||
if ((status = push(the_command)) != ENS_OK) | |||||
return status; | |||||
if ((status = pthread_mutex_unlock(&my_mutex)) != ENS_OK) | |||||
return status; | |||||
if (!a_status && !my_command_is_running && (a_error == EE_OK)) { | |||||
if (!my_command_is_running) { | |||||
// quit when command is actually started | // quit when command is actually started | ||||
// (for possible forthcoming 'end of command' checks) | // (for possible forthcoming 'end of command' checks) | ||||
sem_post(&my_sem_start_is_required); | sem_post(&my_sem_start_is_required); | ||||
} | } | ||||
} | } | ||||
if (a_status != 0) | |||||
a_error = EE_INTERNAL_ERROR; | |||||
return a_error; | |||||
return ENS_OK; | |||||
} | } | ||||
espeak_ERROR fifo_add_commands(t_espeak_command *command1, t_espeak_command *command2) | |||||
espeak_ng_STATUS fifo_add_commands(t_espeak_command *command1, t_espeak_command *command2) | |||||
{ | { | ||||
int a_status = pthread_mutex_lock(&my_mutex); | |||||
espeak_ERROR a_error = EE_OK; | |||||
if (!a_status) { | |||||
if (node_counter+1 >= MAX_NODE_COUNTER) | |||||
a_error = EE_BUFFER_FULL; | |||||
else { | |||||
push(command1); | |||||
push(command2); | |||||
} | |||||
a_status = pthread_mutex_unlock(&my_mutex); | |||||
} | |||||
espeak_ng_STATUS status; | |||||
if ((status = pthread_mutex_lock(&my_mutex)) != ENS_OK) | |||||
return status; | |||||
if (!a_status && !my_command_is_running && (a_error == EE_OK)) { | |||||
if (node_counter+1 >= MAX_NODE_COUNTER) | |||||
return ENS_FIFO_BUFFER_FULL; | |||||
if ((status = push(command1)) != ENS_OK) | |||||
return status; | |||||
if ((status = push(command2)) != ENS_OK) | |||||
return status; | |||||
if ((status = pthread_mutex_unlock(&my_mutex)) != ENS_OK) | |||||
return status; | |||||
if (!my_command_is_running) { | |||||
// quit when one command is actually started | // quit when one command is actually started | ||||
// (for possible forthcoming 'end of command' checks) | // (for possible forthcoming 'end of command' checks) | ||||
sem_post(&my_sem_start_is_required); | sem_post(&my_sem_start_is_required); | ||||
} | } | ||||
} | } | ||||
if (a_status != 0) | |||||
a_error = EE_INTERNAL_ERROR; | |||||
return a_error; | |||||
return ENS_OK; | |||||
} | } | ||||
espeak_ERROR fifo_stop() | |||||
espeak_ng_STATUS fifo_stop() | |||||
{ | { | ||||
int a_command_is_running = 0; | |||||
int a_status = pthread_mutex_lock(&my_mutex); | |||||
if (a_status != 0) | |||||
return EE_INTERNAL_ERROR; | |||||
espeak_ng_STATUS status; | |||||
if ((status = pthread_mutex_lock(&my_mutex)) != ENS_OK) | |||||
return status; | |||||
int a_command_is_running = 0; | |||||
if (my_command_is_running) { | if (my_command_is_running) { | ||||
a_command_is_running = 1; | a_command_is_running = 1; | ||||
my_stop_is_required = 1; | my_stop_is_required = 1; | ||||
} | } | ||||
a_status = pthread_mutex_unlock(&my_mutex); | |||||
if (a_status != 0) | |||||
return EE_INTERNAL_ERROR; | |||||
if ((status = pthread_mutex_unlock(&my_mutex)) != ENS_OK) | |||||
return status; | |||||
if (a_command_is_running) { | if (a_command_is_running) { | ||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) | while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) | ||||
my_stop_is_required = 0; | my_stop_is_required = 0; | ||||
return EE_OK; | |||||
return ENS_OK; | |||||
} | } | ||||
int fifo_is_busy() | int fifo_is_busy() | ||||
static node *head = NULL; | static node *head = NULL; | ||||
static node *tail = NULL; | static node *tail = NULL; | ||||
static espeak_ERROR push(t_espeak_command *the_command) | |||||
static espeak_ng_STATUS push(t_espeak_command *the_command) | |||||
{ | { | ||||
assert((!head && !tail) || (head && tail)); | assert((!head && !tail) || (head && tail)); | ||||
if (the_command == NULL) | if (the_command == NULL) | ||||
return EE_INTERNAL_ERROR; | |||||
return EINVAL; | |||||
if (node_counter >= MAX_NODE_COUNTER) | if (node_counter >= MAX_NODE_COUNTER) | ||||
return EE_BUFFER_FULL; | |||||
return ENS_FIFO_BUFFER_FULL; | |||||
node *n = (node *)malloc(sizeof(node)); | node *n = (node *)malloc(sizeof(node)); | ||||
if (n == NULL) | if (n == NULL) | ||||
return EE_INTERNAL_ERROR; | |||||
return ENOMEM; | |||||
if (head == NULL) { | if (head == NULL) { | ||||
head = n; | head = n; | ||||
the_command->state = CS_PENDING; | the_command->state = CS_PENDING; | ||||
return EE_OK; | |||||
return ENS_OK; | |||||
} | } | ||||
static t_espeak_command *pop() | static t_espeak_command *pop() |
/* | /* | ||||
* Copyright (C) 2007, Gilles Casse <[email protected]> | * Copyright (C) 2007, Gilles Casse <[email protected]> | ||||
* Copyright (C) 2015 Reece H. Dunn | |||||
* Copyright (C) 2015-2016 Reece H. Dunn | |||||
* | * | ||||
* This program is free software; you can redistribute it and/or modify | * This program is free software; you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
// | // | ||||
// Note: this function fails if too many commands are already buffered. | // Note: this function fails if too many commands are already buffered. | ||||
// In such a case, the calling function could wait and then add again its command. | // In such a case, the calling function could wait and then add again its command. | ||||
// | |||||
// Return: EE_OK: operation achieved | |||||
// EE_BUFFER_FULL: the command can not be buffered; | |||||
// you may try after a while to call the function again. | |||||
// EE_INTERNAL_ERROR. | |||||
espeak_ERROR fifo_add_command(t_espeak_command *c); | |||||
espeak_ng_STATUS fifo_add_command(t_espeak_command *c); | |||||
// Add two espeak commands in a single transaction. | // Add two espeak commands in a single transaction. | ||||
// | // | ||||
// Note: this function fails if too many commands are already buffered. | // Note: this function fails if too many commands are already buffered. | ||||
// In such a case, the calling function could wait and then add again these commands. | // In such a case, the calling function could wait and then add again these commands. | ||||
// | |||||
// Return: EE_OK: operation achieved | |||||
// EE_BUFFER_FULL: at least one command can not be buffered; | |||||
// you may try after a while to call the function again. | |||||
// EE_INTERNAL_ERROR. | |||||
espeak_ERROR fifo_add_commands(t_espeak_command *c1, t_espeak_command *c2); | |||||
espeak_ng_STATUS fifo_add_commands(t_espeak_command *c1, t_espeak_command *c2); | |||||
// The current running command must be stopped and the awaiting commands are cleared. | // The current running command must be stopped and the awaiting commands are cleared. | ||||
// Return: EE_OK: operation achieved | |||||
// EE_INTERNAL_ERROR. | |||||
espeak_ERROR fifo_stop(); | |||||
espeak_ng_STATUS fifo_stop(); | |||||
// Is there a running command? | // Is there a running command? | ||||
// Returns 1 if yes; 0 otherwise. | // Returns 1 if yes; 0 otherwise. |
voice_samplerate = wvoice->samplerate; | voice_samplerate = wvoice->samplerate; | ||||
} | } | ||||
static espeak_ERROR status_to_espeak_error(espeak_ng_STATUS status) | |||||
{ | |||||
switch (status) | |||||
{ | |||||
case ENS_OK: return EE_OK; | |||||
case ENOENT: return EE_NOT_FOUND; | |||||
case ENS_FIFO_BUFFER_FULL: return EE_BUFFER_FULL; | |||||
default: return EE_INTERNAL_ERROR; | |||||
} | |||||
} | |||||
#ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
static int dispatch_audio(short *outbuf, int length, espeak_EVENT *event) | static int dispatch_audio(short *outbuf, int length, espeak_EVENT *event) | ||||
// Try to add these 2 commands (single transaction) | // Try to add these 2 commands (single transaction) | ||||
if (c1 && c2) { | if (c1 && c2) { | ||||
a_error = fifo_add_commands(c1, c2); | |||||
a_error = status_to_espeak_error(fifo_add_commands(c1, c2)); | |||||
if (a_error != EE_OK) { | if (a_error != EE_OK) { | ||||
delete_espeak_command(c1); | delete_espeak_command(c1); | ||||
delete_espeak_command(c2); | delete_espeak_command(c2); | ||||
// Try to add these 2 commands (single transaction) | // Try to add these 2 commands (single transaction) | ||||
if (c1 && c2) { | if (c1 && c2) { | ||||
a_error = fifo_add_commands(c1, c2); | |||||
a_error = status_to_espeak_error(fifo_add_commands(c1, c2)); | |||||
if (a_error != EE_OK) { | if (a_error != EE_OK) { | ||||
delete_espeak_command(c1); | delete_espeak_command(c1); | ||||
delete_espeak_command(c2); | delete_espeak_command(c2); | ||||
#ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
t_espeak_command *c = create_espeak_key(key, NULL); | t_espeak_command *c = create_espeak_key(key, NULL); | ||||
a_error = fifo_add_command(c); | |||||
a_error = status_to_espeak_error(fifo_add_command(c)); | |||||
if (a_error != EE_OK) | if (a_error != EE_OK) | ||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
#endif | #endif | ||||
} | } | ||||
t_espeak_command *c = create_espeak_char(character, NULL); | t_espeak_command *c = create_espeak_char(character, NULL); | ||||
a_error = fifo_add_command(c); | |||||
a_error = status_to_espeak_error(fifo_add_command(c)); | |||||
if (a_error != EE_OK) | if (a_error != EE_OK) | ||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
return a_error; | return a_error; | ||||
t_espeak_command *c = create_espeak_parameter(parameter, value, relative); | t_espeak_command *c = create_espeak_parameter(parameter, value, relative); | ||||
a_error = fifo_add_command(c); | |||||
a_error = status_to_espeak_error(fifo_add_command(c)); | |||||
if (a_error != EE_OK) | if (a_error != EE_OK) | ||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
return a_error; | return a_error; | ||||
} | } | ||||
t_espeak_command *c = create_espeak_punctuation_list(punctlist); | t_espeak_command *c = create_espeak_punctuation_list(punctlist); | ||||
a_error = fifo_add_command(c); | |||||
a_error = status_to_espeak_error(fifo_add_command(c)); | |||||
if (a_error != EE_OK) | if (a_error != EE_OK) | ||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
return a_error; | return a_error; |