#include #include #include #include #include #include #include #include #include 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->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; } 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 (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->flags |= XAS_VOX_ACTIVE; 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) { ssize_t readlen, readcount; size_t i; if (!(vox->flags & XAS_VOX_ACTIVE)) { memset(samples, '\0', count * vox->sample_size); return count; } if ((readlen = read(vox->stdout, samples, count * vox->sample_size)) < 0) { goto error_read; } if (readlen == 0) { vox_stop(vox); } readcount = readlen / vox->sample_size; for (i=readcount; itext2wave_path = text2wave_path; vox->sample_size = sample_size; vox->sample_rate = sample_rate; vox->buffer_size = buffer_size; vox->ctx = ctx; vox->flags = XAS_VOX_IDLE; vox->pid = -1; vox->stdin = -1; vox->stdout = -1; vox->in = NULL; return vox; 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_generate(xas_vox *vox) { xas_audio_stream *output; (void)fflush(vox->in); (void)fclose(vox->in); (void)close(vox->stdin); vox->stdin = -1; vox->in = NULL; if ((output = xas_riff_open_fd(vox->stdout)) == NULL) { goto error_riff_open_fd; } if (output->sample_size != vox->sample_size || output->sample_rate != vox->sample_rate) { errno = EINVAL; goto error_invalid_stream; } xas_audio_stream_destroy(output); return 0; error_invalid_stream: error_riff_open_fd: return -1; } int xas_vox_vsayf(xas_vox *vox, const char *format, va_list args) { if (vox_start(vox) < 0) { goto error_vox_start; } return vfprintf(vox->in, format, args); 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, "%s", message); } int xas_vox_active(xas_vox *vox) { return vox->flags & XAS_VOX_ACTIVE; } 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, vox, vox->sample_size, vox->sample_rate, XAS_AUDIO_STREAM_MONO, vox->buffer_size); }