blob: a77a0eea83715bead453ab8eef6e6ced2b8fdc52 [file] [log] [blame]
/*
* dummy.c - Speech Dispatcher dummy output module
*
* A simplific output module that just tries to play an
* an error message in various ways.
*
* Copyright (C) 2008 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: dummy.c,v 1.3 2008-06-09 10:32:00 hanke Exp $
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <speechd_types.h>
#include "module_utils.h"
#define MODULE_NAME "dummy"
#define MODULE_VERSION "0.1"
DECLARE_DEBUG()
/* Thread and process control */
static int dummy_speaking = 0;
static pthread_t dummy_speak_thread;
static pid_t dummy_pid;
static sem_t *dummy_semaphore;
static gboolean initialized = FALSE;
/* Internal functions prototypes */
static void *_dummy_speak(void *);
static void _dummy_child();
/* Fill the module_info structure with pointers to this modules functions */
/* Public functions */
int module_load(void)
{
INIT_SETTINGS_TABLES();
REGISTER_DEBUG();
return 0;
}
int module_init(char **status_info)
{
int ret;
*status_info = NULL;
char name[64];
snprintf(name, sizeof(name), "/speechd-modules-dummy-%d", getpid());
dummy_semaphore = sem_open(name, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 0);
sem_unlink(name);
DBG("Dummy: creating new thread for dummy_speak\n");
dummy_speaking = 0;
ret = spd_pthread_create(&dummy_speak_thread, NULL, _dummy_speak, NULL);
if (ret != 0) {
DBG("Dummy: thread failed\n");
*status_info = g_strdup("The module couldn't initialize threads"
"This can be either an internal problem or an"
"architecture problem. If you are sure your architecture"
"supports threads, please report a bug.");
return -1;
}
initialized = TRUE;
*status_info = g_strdup("Everything ok so far.");
DBG("Ok, now debugging");
return 0;
}
SPDVoice **module_list_voices(void)
{
static SPDVoice voice = {
.name = "dummy",
.language = "en",
};
static SPDVoice *voices[] = { &voice, NULL };
return voices;
}
int module_speak(gchar * data, size_t bytes, SPDMessageType msgtype)
{
DBG("speak()\n");
if (dummy_speaking) {
DBG("Speaking when requested to write");
return 0;
}
DBG("Requested data: |%s|\n", data);
/* Send semaphore signal to the speaking thread */
dummy_speaking = 1;
sem_post(dummy_semaphore);
DBG("Dummy: leaving write() normally\n\r");
return 1;
}
int module_stop(void)
{
DBG("dummy: stop(), dummy_speaking=%d, dummy_pid=%d\n", dummy_speaking,
dummy_pid);
if (dummy_speaking && dummy_pid) {
DBG("dummy: stopping process group pid %d\n", dummy_pid);
kill(-dummy_pid, SIGKILL);
}
DBG("Already stopped, no action");
return 0;
}
size_t module_pause(void)
{
DBG("pause requested\n");
if (dummy_speaking) {
DBG("Dummy module can't pause\n");
return 0;
} else {
return -1;
}
}
char *module_is_speaking(void)
{
return NULL;
}
int module_close(void)
{
DBG("dummy: close()\n");
if (!initialized)
return 0;
if (dummy_speaking) {
module_stop();
}
if (module_terminate_thread(dummy_speak_thread) != 0)
return -1;
sem_close(dummy_semaphore);
initialized = FALSE;
return 0;
}
/* Internal functions */
void *_dummy_speak(void *nothing)
{
int status;
spd_pthread_setname("_dummy_speak");
DBG("dummy: speaking thread starting.......\n");
/* Make interruptible */
set_speaking_thread_parameters();
while (1) {
sem_wait(dummy_semaphore);
DBG("Semaphore on\n");
module_report_event_begin();
/* Create a new process so that we could send it signals */
dummy_pid = fork();
switch (dummy_pid) {
case -1:
DBG("Can't say the message. fork() failed!\n");
dummy_speaking = 0;
continue;
case 0:{
/* Set this process as a process group leader (so that SIGKILL
is also delivered to the child processes created by system()) */
if (setpgid(0, 0) == -1)
DBG("Can't set myself as project group leader!");
DBG("Starting child...\n");
_dummy_child();
}
break;
default:
/* This is the parent. Send data to the child. */
DBG("Waiting for child...");
waitpid(dummy_pid, &status, 0);
dummy_speaking = 0;
DBG("Child exited");
// Report CANCEL if the process was signal-terminated
// and END if it terminated normally
if (WIFSIGNALED(status))
module_report_event_stop();
else
module_report_event_end();
DBG("child terminated -: status:%d signal?:%d signal number:%d.\n", WIFEXITED(status), WIFSIGNALED(status), WTERMSIG(status));
}
}
dummy_speaking = 0;
DBG("dummy: speaking thread ended.......\n");
pthread_exit(NULL);
}
void _dummy_child()
{
sigset_t some_signals;
int ret;
char *try1, *try2, *try3;
sigfillset(&some_signals);
module_sigunblockusr(&some_signals);
DBG("Entering child loop\n");
/* Read the waiting data */
try1 =
g_strdup("play " DATADIR
"/dummy-message.wav > /dev/null 2> /dev/null");
try2 =
g_strdup("aplay " DATADIR
"/dummy-message.wav > /dev/null 2> /dev/null");
try3 =
g_strdup("paplay -n speech-dispatcher " DATADIR
"/dummy-message.wav > /dev/null 2> /dev/null");
DBG("child: synth commands = |%s|%s|%s|", try1, try2, try3);
DBG("Speaking in child...");
module_sigblockusr(&some_signals);
ret = system(try1);
DBG("Executed shell command '%s' returned with %d", try1, ret);
if ((ret != 0)) {
DBG("Execution failed, trying second command");
ret = system(try2);
DBG("Executed shell command '%s' returned with %d", try1, ret);
if ((ret != 0)) {
DBG("Execution failed, trying third command");
ret = system(try3);
DBG("Executed shell command '%s' returned with %d",
try1, ret);
if ((ret != 0) && (ret != 256)) {
DBG("Failed, giving up.");
}
}
}
module_sigunblockusr(&some_signals);
g_free(try1);
g_free(try2);
g_free(try3);
DBG("Done, exiting from child.");
exit(0);
}