blob: 3dc02bdcdda43887a1733954d83383aab04bb12e [file] [log] [blame]
/*
* speaking.c - Speech Dispatcher speech output functions
*
* Copyright (C) 2001,2002,2003, 2006, 2007 Brailcom, o.p.s
*
* 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: speaking.c,v 1.56 2008-10-15 18:06:48 hanke Exp $
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <poll.h>
#include <unistd.h>
#include <safe_io.h>
#include "speechd.h"
#include "server.h"
#include "index_marking.h"
#include "symbols.h"
#include "module.h"
#include "set.h"
#include "alloc.h"
#include "msg.h"
#include "output.h"
#include "speaking.h"
#include "sem_functions.h"
TSpeechDMessage *current_message = NULL;
static SPDPriority highest_priority = 0;
int SPEAKING = 0;
int poll_count;
OutputModule *speaking_module;
AudioID *module_audio_id;
int speaking_uid;
int speaking_gid;
/* Pause and resume handling */
int pause_requested;
int pause_requested_fd;
int pause_requested_uid;
int resume_requested;
/*
Speak() is responsible for getting right text from right
queue in right time and saying it loud through the corresponding
synthetiser. This runs in a separate thread.
*/
void *speak(void *data)
{
TSpeechDMessage *message = NULL;
int ret;
struct pollfd poll_fds[2]; /* Descriptors to poll */
int revents;
OutputModule *output;
spd_pthread_setname("speak");
/* Make interruptible */
set_speaking_thread_parameters();
/* main_pfd */
poll_fds[0].fd = speaking_pipe[0];
poll_fds[0].events = POLLIN;
poll_fds[0].revents = 0;
/* speak queue fd */
poll_fds[1].fd = -1;
poll_fds[1].events = POLLIN;
poll_fds[1].revents = 0;
poll_count = 1;
while (1) {
ret = poll(poll_fds, poll_count, -1);
MSG(5,
"Poll in speak() returned socket activity, main_pfd revents=%d, poll_pfd revents=%d",
poll_fds[0].revents, poll_fds[1].revents);
if ((revents = poll_fds[0].revents)) {
if (revents & POLLIN) {
char buf[1];
MSG(5,
"wait_for_poll: activity in Speech Dispatcher");
const ssize_t rd_bytes =
safe_read(poll_fds[0].fd, buf, 1);
if (rd_bytes != 1)
FATAL
("read from polled fd: could not read 1 byte");
}
}
if (poll_count > 1) {
if ((revents = poll_fds[1].revents)) {
if (revents & POLLHUP) {
// FIXME: We should handle this more gracefully
FATAL
("wait_for_poll: output_module disconnected");
} else if ((revents & POLLIN)
|| (revents & POLLPRI)) {
MSG(5,
"wait_for_poll: activity on output_module: %d",
poll_fds[1].revents);
/* Check if sb is speaking or they are all silent.
* If some synthesizer is speaking, we must wait. */
is_sb_speaking();
}
}
}
/* Handle pause requests */
if (pause_requested) {
MSG(4, "Trying to pause...");
if (pause_requested == 1)
speaking_pause_all(pause_requested_fd);
if (pause_requested == 2)
speaking_pause(pause_requested_fd,
pause_requested_uid);
MSG(4, "Paused...");
pause_requested = 0;
continue;
}
if (SPEAKING) {
MSG(5,
"Continuing because already speaking in speak()");
continue;
}
/* Handle resume requests */
if (resume_requested) {
GList *gl;
MSG(5, "Resume requested");
/* Is there any message after resume? */
if (g_list_length(MessagePausedList) != 0) {
while (1) {
pthread_mutex_lock(&element_free_mutex);
gl = g_list_find_custom
(MessagePausedList, (void *)NULL,
message_nto_speak);
MSG(5,
"Message inserted back to the queues!");
MessagePausedList =
g_list_remove_link
(MessagePausedList, gl);
pthread_mutex_unlock
(&element_free_mutex);
if ((gl != NULL) && (gl->data != NULL)) {
MSG(5, "Reloading message");
reload_message((TSpeechDMessage
*) gl->data);
/* If this resumed message is the same as current_message, then it gets
* another trip through the queue. However, some code later in this
* function will free current_message, even though it is now requeued!
* Hence use-after-free.
* current_message is pretty useless after the requeue, make it NULL. */
if (current_message == gl->data)
current_message = NULL;
} else
break;
}
}
MSG(5, "End of resume processing");
resume_requested = 0;
}
MSG(5, "Locking element_free_mutex in speak()");
pthread_mutex_lock(&element_free_mutex);
/* Handle postponed priority progress message */
check_locked(&element_free_mutex);
if ((g_list_length(last_p5_block) != 0)
&& (g_list_length(MessageQueue->p5) == 0)) {
/* Transfer messages from last_p5_block to priority 2 (message) queue */
while (g_list_length(last_p5_block) != 0) {
GList *item;
item = g_list_first(last_p5_block);
message = item->data;
check_locked(&element_free_mutex);
MessageQueue->p2 =
g_list_insert_sorted(MessageQueue->p2,
message, sortbyuid);
last_p5_block =
g_list_remove_link(last_p5_block, item);
g_list_free1(item);
}
assert(message != NULL);
highest_priority = SPD_MESSAGE;
stop_priority_older_than(SPD_TEXT, message->id);
stop_priority(SPD_NOTIFICATION);
stop_priority(SPD_PROGRESS);
check_locked(&element_free_mutex);
pthread_mutex_unlock(&element_free_mutex);
speaking_semaphore_post();
continue;
} else {
/* Extract the right message from priority queue */
message = get_message_from_queues();
if (message == NULL) {
pthread_mutex_unlock(&element_free_mutex);
MSG(5, "No message in the queue");
continue;
}
}
/* Isn't the parent client of this message paused?
* If it is, insert the message to the MessagePausedList. */
if (message_nto_speak(message, NULL)) {
MSG(4, "Inserting message to paused list...");
MessagePausedList =
g_list_append(MessagePausedList, message);
pthread_mutex_unlock(&element_free_mutex);
continue;
}
/* Choose the output module */
output = get_output_module(message);
if (output == NULL) {
MSG(3, "Output module doesn't work...");
output_check_module(output);
pthread_mutex_unlock(&element_free_mutex);
continue;
}
int punct_missing = 0;
if (strcmp(output->name, "flite") == 0 ||
strcmp(output->name, "dtk-generic") == 0 ||
strcmp(output->name, "epos-generic") == 0 ||
strcmp(output->name, "llia_phon-generic") == 0 ||
strcmp(output->name, "mary-generic") == 0 ||
strcmp(output->name, "swift-generic") == 0 ||
strcmp(output->name, "pico") == 0)
/* These don't support punctuation */
/* FIXME: rather make them express it */
punct_missing = 1;
if (message->settings.type == SPD_MSGTYPE_TEXT ||
message->settings.type == SPD_MSGTYPE_CHAR) {
gchar *normalized = g_utf8_normalize(message->buf, -1,
G_NORMALIZE_ALL_COMPOSE);
if (!normalized) {
MSG(2, "Error: Not UTF-8 valid");
pthread_mutex_unlock(&element_free_mutex);
continue;
}
if (strcmp(message->buf, normalized)) {
MSG(5, "text: Normalized '%s' to '%s'", message->buf, normalized);
}
g_free(message->buf);
message->buf = normalized;
insert_symbols(message, punct_missing);
}
/* Insert index marks into textual messages */
if (message->settings.type == SPD_MSGTYPE_TEXT) {
insert_index_marks(message,
message->settings.ssml_mode);
}
/* Write the message to the output layer. */
ret = output_speak(message, output);
MSG(4, "Message sent to output module");
if (ret == -1) {
MSG(2, "Error: Output module failed");
output_check_module(output);
pthread_mutex_unlock(&element_free_mutex);
continue;
}
if (ret != 0) {
MSG(2,
"ERROR: Can't say message. Module reported error in speaking: %d",
ret);
pthread_mutex_unlock(&element_free_mutex);
continue;
}
SPEAKING = 1;
if (speaking_module != NULL) {
poll_fds[1].fd = speaking_module->pipe_speak[0];
poll_count = 2;
}
/* Set the id of the client who is speaking. */
speaking_uid = message->settings.uid;
if (current_message != NULL) {
if (!current_message->settings.paused_while_speaking) {
/* Check if the client who emited this message is disconnected
by now and this was his last message. If so, delete it's settings
from fdset */
if (get_client_settings_by_uid
(current_message->settings.uid)->active ==
0) {
if (!client_has_messages
(current_message->settings.uid)
&& (current_message->settings.uid !=
message->settings.uid)) {
/* client_has_messages does not account for message,
which was just retrieved from the queue.
We also have to compare the uids of message
and current_message to be sure that there are
no outstanding messages. */
MSG(4,
"Removing client settings for uid %d",
current_message->
settings.uid);
remove_client_settings_by_uid
(current_message->
settings.uid);
}
}
mem_free_message(current_message);
}
}
current_message = message;
/* Check if the last priority 5 message wasn't said yet */
if (last_p5_block != NULL) {
GList *elem;
TSpeechDMessage *p5_message;
elem = g_list_last(last_p5_block);
if (elem != NULL) {
p5_message = (TSpeechDMessage *) elem->data;
if (p5_message->settings.reparted ==
message->settings.reparted) {
g_list_foreach(last_p5_block,
(GFunc) mem_free_message,
NULL);
g_list_free(last_p5_block);
last_p5_block = NULL;
}
}
}
pthread_mutex_unlock(&element_free_mutex);
}
}
int reload_message(TSpeechDMessage * msg)
{
TFDSetElement *client_settings;
int im;
char *pos;
char *newtext;
char *tptr;
if (msg == NULL) {
MSG(4, "Warning: msg == NULL in reload_message()");
return -1;
}
if (msg->settings.index_mark != NULL) {
MSG(5, "Recovering index mark %s", msg->settings.index_mark);
client_settings = get_client_settings_by_uid(msg->settings.uid);
/* Scroll back to provide context, if required */
/* WARNING: This relies on ordered SD_MARK_BODY index marks! */
MSG(5, "Recovering index mark (number)");
im = strtol(msg->settings.index_mark + SD_MARK_BODY_LEN, &tptr,
10);
MSG(5, "Recovering index mark (comparing tptr)");
if (msg->settings.index_mark + SD_MARK_BODY_LEN == tptr) {
MSG2(2, "index_marking",
"ERROR: Invalid index_mark '%s'. Message not reloaded.",
msg->settings.index_mark);
return -1;
}
MSG(5, "Recovered index mark number: %d", im);
im += client_settings->pause_context;
MSG2(5, "index_marking",
"Requested index mark (with context) is %d (%s+%d)", im,
msg->settings.index_mark, client_settings->pause_context);
if (im < 0) {
im = 0;
pos = msg->buf;
} else {
pos = find_index_mark(msg, im);
if (pos == NULL)
return -1;
}
newtext = strip_index_marks(pos, client_settings->ssml_mode);
g_free(msg->buf);
if (newtext == NULL)
return -1;
msg->buf = newtext;
msg->bytes = strlen(msg->buf);
if (queue_message
(msg, -msg->settings.uid, 0, SPD_MSGTYPE_TEXT, 0) == 0) {
if (SPEECHD_DEBUG)
FATAL("Can't queue message\n");
g_free(msg->buf);
g_free(msg);
return -1;
}
return 0;
} else {
MSG(5, "Index mark unknown, inserting the whole message.");
if (queue_message
(msg, -msg->settings.uid, 0, SPD_MSGTYPE_TEXT, 0) == 0) {
if (SPEECHD_DEBUG)
FATAL("Can't queue message\n");
g_free(msg->buf);
g_free(msg);
return -1;
}
return 0;
}
return 0;
}
void speaking_stop(int uid)
{
TSpeechDMessage *msg;
GList *gl;
GList *queue;
signed int gid = -1;
/* Only act if the currently speaking client is the specified one */
if (get_speaking_client_uid() == uid) {
output_stop();
/* Get the queue where the message being spoken came from */
queue = speaking_get_queue(highest_priority);
if (queue == NULL)
return;
/* Get group ID of the current message */
gl = g_list_last(queue);
if (gl == NULL)
return;
if (gl->data == NULL)
return;
msg = (TSpeechDMessage *) gl->data;
if ((msg->settings.reparted != 0) && (msg->settings.uid == uid)) {
gid = msg->settings.reparted;
} else {
return;
}
while (1) {
gl = g_list_last(queue);
if (gl == NULL) {
speaking_set_queue(highest_priority, queue);
return;
}
if (gl->data == NULL)
return;
msg = (TSpeechDMessage *) gl->data;
if ((msg->settings.reparted == gid)
&& (msg->settings.uid == uid)) {
queue = g_list_remove_link(queue, gl);
assert(gl->data != NULL);
mem_free_message(gl->data);
} else {
speaking_set_queue(highest_priority, queue);
return;
}
}
}
}
void speaking_stop_all()
{
TSpeechDMessage *msg;
GList *gl;
GList *queue;
output_stop();
queue = speaking_get_queue(highest_priority);
if (queue == NULL)
return;
gl = g_list_last(queue);
if (gl == NULL)
return;
assert(gl->data != NULL);
msg = (TSpeechDMessage *) gl->data;
if (msg->settings.reparted == 0) {
return;
}
while (1) {
gl = g_list_last(queue);
if (gl == NULL) {
speaking_set_queue(highest_priority, queue);
return;
}
if (SPEECHD_DEBUG)
assert(gl->data != NULL);
msg = (TSpeechDMessage *) gl->data;
if (msg->settings.reparted == 1) {
queue = g_list_remove_link(queue, gl);
assert(gl->data != NULL);
mem_free_message(gl->data);
} else {
speaking_set_queue(highest_priority, queue);
return;
}
}
}
void speaking_cancel(int uid)
{
pthread_mutex_lock(&element_free_mutex);
speaking_stop(uid);
stop_from_uid(uid);
pthread_mutex_unlock(&element_free_mutex);
}
void speaking_cancel_all()
{
output_stop();
pthread_mutex_lock(&element_free_mutex);
stop_priority(SPD_IMPORTANT);
stop_priority(SPD_MESSAGE);
stop_priority(SPD_TEXT);
stop_priority(SPD_NOTIFICATION);
stop_priority(SPD_PROGRESS);
pthread_mutex_unlock(&element_free_mutex);
}
int speaking_pause_all(int fd)
{
int err = 0;
int i;
int uid;
for (i = 1; i <= SpeechdStatus.max_fd; i++) {
uid = get_client_uid_by_fd(i);
if (uid == 0)
continue;
err += speaking_pause(i, uid);
}
if (err > 0)
return 1;
else
return 0;
}
int speaking_pause(int fd, int uid)
{
TFDSetElement *settings;
int ret;
MSG(4, "Pause");
/* Find settings for this particular client */
settings = get_client_settings_by_uid(uid);
if (settings == NULL) {
MSG(4,
"ERROR: Can't get settings of active client in speaking_pause()!");
return 1;
}
settings->paused = 1;
if (speaking_uid != uid) {
MSG(5, "given uid %d not speaking_uid %d", uid, speaking_uid);
return 0;
}
if (SPEAKING) {
if (current_message == NULL) {
MSG(5, "current_message is null");
return 0;
}
ret = output_pause();
if (ret < 0) {
MSG(5, "output_pause returned %d", ret);
return 0;
}
MSG(5,
"Including current message into the message paused list");
current_message->settings.paused = 2;
current_message->settings.paused_while_speaking = 1;
if (g_list_find(MessagePausedList, current_message) == NULL)
MessagePausedList =
g_list_append(MessagePausedList, current_message);
}
return 0;
}
int speaking_resume_all()
{
int err = 0;
int i;
int uid;
for (i = 1; i <= SpeechdStatus.max_fd; i++) {
uid = get_client_uid_by_fd(i);
if (uid == 0)
continue;
err += speaking_resume(uid);
}
if (err > 0)
return 1;
else
return 0;
}
int speaking_resume(int uid)
{
TFDSetElement *settings;
/* Find settings for this particular client */
settings = get_client_settings_by_uid(uid);
if (settings == NULL)
return 1;
/* Set it to speak again. */
settings->paused = 0;
resume_requested = 1;
speaking_semaphore_post();
return 0;
}
int socket_send_msg(int fd, const char *msg)
{
int ret;
assert(msg != NULL);
pthread_mutex_lock(&socket_com_mutex);
MSG2(5, "protocol", "%d:REPLY:|%s|", fd, msg);
ret = write(fd, msg, strlen(msg));
pthread_mutex_unlock(&socket_com_mutex);
if (ret < 0) {
MSG(1, "write() error: %s", strerror(errno));
return -1;
}
return 0;
}
int report_index_mark(TSpeechDMessage * msg, const char *index_mark)
{
char *cmd;
int ret;
cmd = g_strdup_printf(EVENT_INDEX_MARK_C "-%d\r\n"
EVENT_INDEX_MARK_C "-%d\r\n"
EVENT_INDEX_MARK_C "-%s\r\n"
EVENT_INDEX_MARK,
msg->id, msg->settings.uid, index_mark);
ret = socket_send_msg(msg->settings.fd, cmd);
g_free(cmd);
if (ret) {
MSG(1, "ERROR: Can't report index mark!");
return -1;
}
return 0;
}
#define REPORT_STATE(state, ssip_code, ssip_msg) \
int \
report_ ## state (TSpeechDMessage *msg) \
{ \
char *cmd; \
int ret; \
cmd = g_strdup_printf(ssip_code"-%d\r\n"ssip_code"-%d\r\n"ssip_msg, \
msg->id, msg->settings.uid); \
ret = socket_send_msg(msg->settings.fd, cmd); \
g_free(cmd); \
if (ret){ \
MSG(2, "ERROR: Can't report index mark!"); \
return -1; \
} \
return 0; \
}
REPORT_STATE(begin, EVENT_BEGIN_C, EVENT_BEGIN)
REPORT_STATE(end, EVENT_END_C, EVENT_END)
REPORT_STATE(pause, EVENT_PAUSED_C, EVENT_PAUSED)
REPORT_STATE(resume, EVENT_RESUMED_C, EVENT_RESUMED)
REPORT_STATE(cancel, EVENT_CANCELED_C, EVENT_CANCELED)
int is_sb_speaking(void)
{
char *index_mark;
TFDSetElement *settings;
MSG(5, "is_sb_speaking(), SPEAKING=%d", SPEAKING);
/* Determine if the current module is still speaking */
if (speaking_module != NULL) {
if (current_message == NULL) {
MSG(1,
"Error: Current message is NULL in is_sb_speaking()");
return -1;
}
settings = &(current_message->settings);
output_is_speaking(&index_mark);
if (index_mark == NULL) {
poll_count = 1;
return SPEAKING = 0;
}
if (!strcmp(index_mark, "no")) {
g_free(index_mark);
return SPEAKING;
}
MSG(5, "INDEX MARK: %s", index_mark);
if (!strcmp(index_mark, SD_MARK_BODY "begin")) {
SPEAKING = 1;
if (!settings->paused_while_speaking) {
if (settings->notification & SPD_BEGIN)
report_begin(current_message);
} else {
if (settings->notification & SPD_RESUME)
report_resume(current_message);
settings->paused_while_speaking = 0;
}
} else if (!strcmp(index_mark, SD_MARK_BODY "end")) {
SPEAKING = 0;
poll_count = 1;
if (settings->notification & SPD_END)
report_end(current_message);
speaking_semaphore_post();
} else if (!strcmp(index_mark, SD_MARK_BODY "paused")) {
SPEAKING = 0;
poll_count = 1;
if (settings->notification & SPD_PAUSE)
report_pause(current_message);
/* We don't want to free this message in speak() since we will
later copy it in resume() */
current_message = NULL;
} else if (!strcmp(index_mark, SD_MARK_BODY "stopped")) {
SPEAKING = 0;
poll_count = 1;
if (settings->notification & SPD_CANCEL)
report_cancel(current_message);
speaking_semaphore_post();
} else if (index_mark != NULL) {
if (strncmp(index_mark, SD_MARK_BODY, SD_MARK_BODY_LEN)) {
if (settings->notification & SPD_INDEX_MARKS)
report_index_mark(current_message,
index_mark);
} else {
MSG(5,
"Setting current index_mark for the message to %s",
index_mark);
if (current_message->settings.index_mark !=
NULL)
g_free(current_message->
settings.index_mark);
current_message->settings.index_mark =
g_strdup(index_mark);
}
}
g_free(index_mark);
} else {
MSG(5, "Speaking module is NULL, SPEAKING==%d", SPEAKING);
SPEAKING = 0;
}
if (SPEAKING == 0)
speaking_module = NULL;
return SPEAKING;
}
int get_speaking_client_uid(void)
{
int speaking = 0;
if (SPEAKING == 0) {
speaking_uid = 0;
return 0;
}
if (speaking_uid != 0) {
speaking = speaking_uid;
}
return speaking;
}
GList *queue_remove_message(GList * queue, GList * gl)
{
TSpeechDMessage *msg;
assert(gl != NULL);
assert(gl->data != NULL);
msg = (TSpeechDMessage *) gl->data;
MSG(5, "Removing message |%s| with priority %d from queue", msg->buf, msg->settings.priority);
if (msg->settings.notification & SPD_CANCEL)
report_cancel(msg);
mem_free_message(gl->data);
queue = g_list_delete_link(queue, gl);
return queue;
}
GList *empty_queue(GList * queue)
{
int num, i;
GList *gl;
num = g_list_length(queue);
for (i = 0; i <= num - 1; i++) {
gl = g_list_first(queue);
queue = queue_remove_message(queue, gl);
}
return queue;
}
GList *empty_queue_by_time(GList * queue, unsigned int uid)
{
int num, i;
GList *gl, *gln;
TSpeechDMessage *msg;
num = g_list_length(queue);
gl = g_list_first(queue);
for (i = 0; i <= num - 1; i++) {
gln = g_list_next(gl);
if (gl == NULL)
break;
assert(gl->data != NULL);
msg = gl->data;
if (msg->id < uid) {
queue = queue_remove_message(queue, gl);
}
gl = gln;
}
return queue;
}
int stop_priority(SPDPriority priority)
{
GList *queue;
queue = speaking_get_queue(priority);
if (highest_priority == priority) {
output_stop();
}
queue = empty_queue(queue);
speaking_set_queue(priority, queue);
return 0;
}
int stop_priority_older_than(SPDPriority priority, unsigned int uid)
{
GList *queue;
queue = speaking_get_queue(priority);
if (highest_priority == priority) {
output_stop();
}
queue = empty_queue_by_time(queue, uid);
speaking_set_queue(priority, queue);
return 0;
}
GList *stop_priority_from_uid(GList * queue, const int uid)
{
GList *ret = queue;
GList *gl;
while ((gl = g_list_find_custom(ret, &uid, p_msg_uid_lc)))
ret = queue_remove_message(ret, gl);
return ret;
}
void stop_from_uid(const int uid)
{
check_locked(&element_free_mutex);
MessageQueue->p1 = stop_priority_from_uid(MessageQueue->p1, uid);
MessageQueue->p2 = stop_priority_from_uid(MessageQueue->p2, uid);
MessageQueue->p3 = stop_priority_from_uid(MessageQueue->p3, uid);
MessageQueue->p4 = stop_priority_from_uid(MessageQueue->p4, uid);
MessageQueue->p5 = stop_priority_from_uid(MessageQueue->p5, uid);
}
/* Determines if this messages is to be spoken
* (returns 1) or its parent client is paused (returns 0).
* Note: If you are wondering why it's reversed (not to speak instead
* of to speak), it's because we also use this function for
* searching through the list. */
gint message_nto_speak(gconstpointer data, gconstpointer nothing)
{
TFDSetElement *global_settings;
TSpeechDMessage *message = (TSpeechDMessage *) data;
/* Is there something in the body of the message? */
if (message == NULL)
return 0;
/* Find global settings for this connection. */
global_settings = get_client_settings_by_fd(message->settings.fd);
if (global_settings == NULL)
return 0;
if (!global_settings->paused)
return 0;
else
return 1;
}
void stop_priority_except_first(SPDPriority priority)
{
GList *queue;
GList *gl;
TSpeechDMessage *msg;
GList *gl_next;
int gid;
queue = speaking_get_queue(priority);
gl = g_list_last(queue);
if (gl == NULL)
return;
if (gl->data == NULL)
return;
msg = (TSpeechDMessage *) gl->data;
if (msg->settings.reparted <= 0) {
queue = g_list_remove_link(queue, gl);
speaking_set_queue(priority, queue);
stop_priority(priority);
/* Fill the queue with the list containing only the first message */
speaking_set_queue(priority, gl);
} else {
gid = msg->settings.reparted;
if (highest_priority == priority && speaking_gid != gid) {
output_stop();
}
gl = g_list_first(queue);
while (gl) {
gl_next = g_list_next(gl);
if (gl->data != NULL) {
TSpeechDMessage *msgg = gl->data;
if (msgg->settings.reparted != gid) {
queue = g_list_remove_link(queue, gl);
mem_free_message(msgg);
}
}
gl = gl_next;
}
speaking_set_queue(priority, queue);
}
return;
}
void resolve_priorities(SPDPriority priority)
{
if (priority == SPD_IMPORTANT) {
if (SPEAKING && highest_priority != SPD_IMPORTANT)
output_stop();
stop_priority(SPD_NOTIFICATION);
stop_priority(SPD_PROGRESS);
}
if (priority == SPD_MESSAGE) {
if (SPEAKING && highest_priority != SPD_IMPORTANT
&& highest_priority != SPD_MESSAGE)
output_stop();
stop_priority(SPD_TEXT);
stop_priority(SPD_NOTIFICATION);
stop_priority(SPD_PROGRESS);
}
if (priority == SPD_TEXT) {
stop_priority_except_first(SPD_TEXT);
stop_priority(SPD_NOTIFICATION);
stop_priority(SPD_PROGRESS);
}
if (priority == SPD_NOTIFICATION) {
stop_priority_except_first(SPD_NOTIFICATION);
if (SPEAKING && highest_priority != SPD_NOTIFICATION)
stop_priority(SPD_NOTIFICATION);
}
if (priority == SPD_PROGRESS) {
stop_priority(SPD_NOTIFICATION);
if (SPEAKING) {
GList *gl;
check_locked(&element_free_mutex);
gl = g_list_last(MessageQueue->p5);
check_locked(&element_free_mutex);
MessageQueue->p5 =
g_list_remove_link(MessageQueue->p5, gl);
if (gl != NULL) {
check_locked(&element_free_mutex);
MessageQueue->p5 =
empty_queue(MessageQueue->p5);
if (gl->data != NULL) {
MessageQueue->p5 = gl;
}
}
}
}
}
TSpeechDMessage *get_message_from_queues()
{
GList *gl;
SPDPriority prio;
TSpeechDMessage *message;
/* We will descend through priorities to say more important
messages first. */
for (prio = SPD_IMPORTANT; prio <= SPD_PROGRESS; prio++) {
GList *current_queue = speaking_get_queue(prio);
check_locked(&element_free_mutex);
gl = g_list_first(current_queue);
while (gl != NULL) {
if (message_nto_speak
((TSpeechDMessage *) gl->data, NULL)) {
gl = g_list_next(gl);
continue;
}
speaking_set_queue(prio,
g_list_remove_link(current_queue,
gl));
highest_priority = prio;
message = gl->data;
g_list_free(gl);
return (TSpeechDMessage *) message;
}
}
return NULL;
}
gint message_has_uid(gconstpointer msg, gconstpointer uid)
{
if (((TSpeechDMessage *) msg)->settings.uid == *(int *)uid)
return 0;
else
return 1;
}
/* Return 1 if any message from this client is found
in any of the queues, otherwise return 0 */
int client_has_messages(int uid)
{
if (g_list_find_custom
(MessageQueue->p5, (gconstpointer) & uid, message_has_uid)
|| g_list_find_custom(MessageQueue->p4, (gconstpointer) & uid,
message_has_uid)
|| g_list_find_custom(MessageQueue->p3, (gconstpointer) & uid,
message_has_uid)
|| g_list_find_custom(MessageQueue->p2, (gconstpointer) & uid,
message_has_uid)
|| g_list_find_custom(MessageQueue->p1, (gconstpointer) & uid,
message_has_uid))
return 1;
else
return 0;
}
GList *speaking_get_queue(SPDPriority priority)
{
GList *queue = NULL;
assert(priority >= SPD_IMPORTANT && priority <= SPD_PROGRESS);
check_locked(&element_free_mutex);
switch (priority) {
case SPD_IMPORTANT:
queue = MessageQueue->p1;
break;
case SPD_MESSAGE:
queue = MessageQueue->p2;
break;
case SPD_TEXT:
queue = MessageQueue->p3;
break;
case SPD_NOTIFICATION:
queue = MessageQueue->p4;
break;
case SPD_PROGRESS:
queue = MessageQueue->p5;
break;
}
return queue;
}
void speaking_set_queue(SPDPriority priority, GList * queue)
{
assert(priority >= SPD_IMPORTANT && priority <= SPD_PROGRESS);
check_locked(&element_free_mutex);
switch (priority) {
case SPD_IMPORTANT:
MessageQueue->p1 = queue;
break;
case SPD_MESSAGE:
MessageQueue->p2 = queue;
break;
case SPD_TEXT:
MessageQueue->p3 = queue;
break;
case SPD_NOTIFICATION:
MessageQueue->p4 = queue;
break;
case SPD_PROGRESS:
MessageQueue->p5 = queue;
break;
}
}
gint sortbyuid(gconstpointer a, gconstpointer b)
{
const TSpeechDMessage *msg1 = a;
const TSpeechDMessage *msg2 = b;
if ((msg1 == NULL) && (msg2 != NULL))
return -1;
if ((msg1 != NULL) && (msg2 == NULL))
return +1;
if ((msg1 == NULL) && (msg2 == NULL))
return 0;
return msg1->id - msg2->id;
}