diff options
-rw-r--r-- | include/xas/vox.h | 5 | ||||
-rw-r--r-- | src/vox.c | 229 |
2 files changed, 223 insertions, 11 deletions
diff --git a/include/xas/vox.h b/include/xas/vox.h index 179d5d1..790cbca 100644 --- a/include/xas/vox.h +++ b/include/xas/vox.h @@ -1,6 +1,7 @@ #ifndef _XAS_VOX_H #define _XAS_VOX_H +#include <stdio.h> #include <stdarg.h> #include <sys/types.h> @@ -21,7 +22,11 @@ typedef struct _xas_vox { int flags; int pid, + stdin, stdout; + + FILE *in; + xas_audio_stream *output; } xas_vox; xas_vox *xas_vox_new(const char *text2wave_path, @@ -1,34 +1,175 @@ #include <stdlib.h> +#include <string.h> #include <unistd.h> +#include <fcntl.h> +#include <sysexits.h> #include <sys/wait.h> +#include <errno.h> +#include <xas/riff.h> #include <xas/vox.h> -static void vox_cleanup(xas_vox *vox, xas_audio_stream *stream) { - int status; +static int vox_stop(xas_vox *vox) { + if (vox->stdin >= 0) { + (void)close(vox->stdin); + + vox->stdin = -1; + } + + if (vox->stdout >= 0) { + (void)close(vox->stdout); + + vox->stdout = -1; + } + + if (vox->in) { + (void)fclose(vox->in); + + vox->in = NULL; + } + + if (vox->output) { + xas_audio_stream_destroy(vox->output); + + vox->output = NULL; + } + + if (vox->pid) { + int status; + + if (waitpid(vox->pid, &status, 0) < 0) { + goto error_waitpid; + } + + vox->pid = -1; + } + + vox->flags &= ~XAS_VOX_ACTIVE; + + return 0; + +error_waitpid: + return -1; +} - if (vox->flags & XAS_VOX_ACTIVE) { - if (vox->pid >= 0) { - (void)waitpid(vox->pid, &status, 0); +static int vox_start(xas_vox *vox) { + int pipe_stdin[2], + pipe_stdout[2]; + + int pid; + + char sample_rate[40]; + + char *args[] = { + (char *)vox->text2wave_path, + "-F", + sample_rate, + "-", + "-o", + "-", + NULL + }; + + snprintf(sample_rate, sizeof(sample_rate)-1, "%zu", vox->sample_rate); + + (void)vox_stop(vox); + + if (pipe(pipe_stdin) < 0) { + goto error_pipe_stdin; + } + + if (pipe(pipe_stdout) < 0) { + goto error_pipe_stdout; + } + + if ((pid = fork()) < 0) { + goto error_fork; + } + + if (pid == 0) { + int fd; + + if ((fd = open("/dev/null", O_WRONLY)) < 0) { + goto error_child; + } + + close(pipe_stdin[1]); + close(pipe_stdout[0]); + + if (dup2(pipe_stdin[0], 0) < 0) { + goto error_child; } - if (vox->stdout >= 0) { - (void)close(vox->stdout); + if (dup2(pipe_stdout[1], 1) < 0) { + goto error_child; } + + if (dup2(fd, 2) < 0) { + goto error_child; + } + + if (execv(vox->text2wave_path, args) < 0) { + goto error_child; + } + +error_child: + exit(EX_OSERR); } + + close(pipe_stdin[0]); + close(pipe_stdout[1]); + + vox->pid = pid; + vox->stdin = pipe_stdin[1]; + vox->stdout = pipe_stdout[0]; + vox->in = fdopen(pipe_stdin[1], "w"); + vox->output = NULL; + + return 0; + +error_fork: + (void)close(pipe_stdout[1]); + (void)close(pipe_stdout[0]); + +error_pipe_stdout: + (void)close(pipe_stdin[1]); + (void)close(pipe_stdin[0]); + +error_pipe_stdin: + return -1; +} + +static void vox_cleanup(xas_vox *vox, xas_audio_stream *stream) { + (void)vox_stop(vox); } static ssize_t vox_fill(xas_vox *vox, int16_t *samples, size_t count, xas_audio_stream *stream) { - size_t i; + ssize_t readlen; + int16_t *buf; - for (i=0; i<count; i++) { - samples[i] = 0; + if (!(vox->flags & XAS_VOX_ACTIVE)) { + memset(samples, '\0', count * vox->sample_size); + + return count; } - return i; + if ((readlen = xas_audio_stream_read(vox->output, + (void **)&buf, + count)) < 0) { + goto error_audio_stream_read; + } + + if (readlen == 0) { + vox_stop(vox); + } + + return readlen; + +error_audio_stream_read: + return -1; } xas_vox *xas_vox_new(const char *text2wave_path, @@ -50,7 +191,9 @@ xas_vox *xas_vox_new(const char *text2wave_path, vox->flags = XAS_VOX_IDLE; vox->pid = -1; + vox->stdin = -1; vox->stdout = -1; + vox->in = NULL; return vox; @@ -58,6 +201,70 @@ error_malloc_vox: return NULL; } +void xas_vox_destroy(xas_vox *vox) { + free(vox); +} + +int xas_vox_stop(xas_vox *vox) { + return vox_stop(vox); +} + +int xas_vox_vsayf(xas_vox *vox, const char *format, va_list args) { + int ret; + + if (vox_start(vox) < 0) { + goto error_vox_start; + } + + if ((ret = vfprintf(vox->in, format, args)) < 0) { + goto error_vfprintf; + } + + (void)close(vox->stdin); + vox->stdin = -1; + + (void)fclose(vox->in); + vox->in = NULL; + + if ((vox->output = xas_riff_open_fd(vox->stdout)) == NULL) { + goto error_riff_open_fd; + } + + if (vox->output->sample_size != vox->sample_size + || vox->output->sample_rate != vox->sample_rate) { + errno = EINVAL; + + goto error_invalid_stream; + } + + return ret; + +error_invalid_stream: + xas_audio_stream_destroy(vox->output); + +error_riff_open_fd: +error_vfprintf: + (void)vox_stop(vox); + +error_vox_start: + return -1; +} + +int xas_vox_sayf(xas_vox *vox, const char *format, ...) { + int ret; + va_list args; + + va_start(args, format); + ret = xas_vox_vsayf(vox, format, args); + va_end(args); + + return ret; +} + +int xas_vox_say(xas_vox *vox, const char *message) { + return xas_vox_sayf(vox, "%\n", message); +} + xas_audio_stream *xas_vox_stream_new(xas_vox *vox) { return xas_audio_stream_new_source((xas_audio_fill)vox_fill, (xas_audio_cleanup)vox_cleanup, |