| |
| /* |
| * 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); |
| } |