summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/xas/vox.h5
-rw-r--r--src/vox.c229
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,
diff --git a/src/vox.c b/src/vox.c
index 047200a..43684e3 100644
--- a/src/vox.c
+++ b/src/vox.c
@@ -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,