add new writer to para_play: file
authorAndre <maan@p133.(none)>
Tue, 18 Apr 2006 18:42:15 +0000 (20:42 +0200)
committerAndre <maan@p133.(none)>
Tue, 18 Apr 2006 18:42:15 +0000 (20:42 +0200)
It simply writes the input to a random filename under ~/.paraslash.
Also, add some writer-related macros to config.h and two new options
for para_play: --writer and --list_writers.

configure.ac
error.h
play.c
play.ggo

index 61b1808..bfd659f 100644 (file)
@@ -79,6 +79,8 @@ server_ldflags=""
 play_cmdline_objs="play.cmdline"
 play_errlist_objs="play time fd string"
 play_ldflags=""
+write_writers="file"
+
 
 ########################################################################### ssl
 dnl @synopsis CHECK_SSL
@@ -230,6 +232,7 @@ AC_CHECK_LIB([asound], [snd_pcm_open], [], [
 if test "$have_alsa" = "yes"; then
        extras="$extras para_play"
        play_ldflags="$play_ldflags -lasound"
+       write_writers="$write_writers alsa"
 fi
 ########################################################################### ortp
 have_ortp="yes"
@@ -320,6 +323,16 @@ AC_SUBST(play_ldflags, $play_ldflags)
 AC_DEFINE_UNQUOTED(INIT_PLAY_ERRLISTS,
        objlist_to_errlist($play_errlist_objs), errors used by para_play)
 
+enum="$(for i in $write_writers; do printf "${i}_WRITE, " | tr '[a-z]' '[A-Z]'; done)"
+AC_DEFINE_UNQUOTED(WRITER_ENUM, $enum NUM_SUPPORTED_WRITERS,
+       enum of supported writers)
+names="$(for i in $write_writers; do printf '\"'$i'\", ' ; done)"
+AC_DEFINE_UNQUOTED(WRITER_NAMES, $names, supported writer names)
+inits="$(for i in $write_writers; do printf 'extern void '$i'_writer_init(struct writer *); '; done)"
+AC_DEFINE_UNQUOTED(DECLARE_WRITER_INITS, $inits, init functions of the supported writers)
+array="$(for i in $write_writers; do printf '{.init = '$i'_writer_init},'; done)"
+AC_DEFINE_UNQUOTED(WRITER_ARRAY, $array, array of supported writers)
+
 gui_cmdline_objs="gui.cmdline"
 gui_errlist_objs="exec close_on_fork signal string stat ringbuffer fd"
 gui_other_objs="gui gui_common gui_theme"
diff --git a/error.h b/error.h
index 880df55..8169166 100644 (file)
--- a/error.h
+++ b/error.h
@@ -302,6 +302,7 @@ extern const char **para_errlist[];
        PARA_ERROR(READ_STDIN, "failed to read from stdin"), \
        PARA_ERROR(PLAY_SYNTAX, "syntax error"), \
        PARA_ERROR(PLAY_OVERRUN, "buffer overrun"), \
+       PARA_ERROR(LIST_WRITERS_GIVEN, ""), \
        PARA_ERROR(PREMATURE_END, "premature end of audio file"), \
        PARA_ERROR(BROKEN_CONF, "Broken alsa configuration"), \
        PARA_ERROR(ACCESS_TYPE, "alsa access type not available"), \
@@ -321,6 +322,8 @@ extern const char **para_errlist[];
        PARA_ERROR(START_THRESHOLD, "snd_pcm_sw_params_set_start_threshold() failed"), \
        PARA_ERROR(STOP_THRESHOLD, "snd_pcm_sw_params_set_stop_threshold() failed"), \
        PARA_ERROR(ALSA_LOG, "snd_output_stdio_attach() failed"), \
+       PARA_ERROR(FW_WRITE, "file writer write error"), \
+       PARA_ERROR(FW_OPEN, "file writer: can not open output file"), \
 
 
 
diff --git a/play.c b/play.c
index f312626..e1fdb2e 100644 (file)
--- a/play.c
+++ b/play.c
 #include "string.h"
 #include "error.h"
 
-#define FORMAT SND_PCM_FORMAT_S16_LE
-
-struct private_alsa_data {
-       snd_pcm_t *handle;
-       size_t bytes_per_frame;
-};
-
+/*
+files:
+~~~~~~
+write.c
+write.h wr
+write_common.c
+write_common.h: decratation of the wng funcs
+alsa_writer.c
+*/
+
+/* write.h */
+enum writer_enum {WRITER_ENUM};
+
+/* write.h */
 struct writer_node {
        struct writer *writer;
        void *private_data;
        int chunk_bytes;
 };
 
+/* write.h */
 struct writer {
+       void (*init)(struct writer *w);
        int (*open)(struct writer_node *);
        int (*write)(char *data, size_t nbytes, struct writer_node *);
        void (*close)(struct writer_node *);
        void (*shutdown)(struct writer_node *);
 };
 
+/* write.h */
 struct writer_node_group {
        unsigned num_writers;
        struct writer_node *writer_nodes;
@@ -59,20 +69,22 @@ struct writer_node_group {
        int eof;
 };
 
-
+/* write.h */
 #define FOR_EACH_WRITER_NODE(i, wng) for (i = 0; i < (wng)->num_writers; i++)
+#define FOR_EACH_WRITER(i) for (i = 0; i < NUM_SUPPORTED_WRITERS; i++)
 
-#define NUM_WRITERS 1
-static struct writer writers[NUM_WRITERS];
-#define FOR_EACH_WRITER(i) for (i = 0; i < NUM_WRITERS, i++)
+DECLARE_WRITER_INITS;
 
 
+/* write.c */
 static unsigned char *audiobuf;
 static struct timeval *start_time;
 static struct gengetopt_args_info conf;
 
+/* write.c */
 INIT_PLAY_ERRLISTS;
 
+/* write.c */
 void para_log(int ll, const char* fmt,...)
 {
        va_list argp;
@@ -84,6 +96,7 @@ void para_log(int ll, const char* fmt,...)
        va_end(argp);
 }
 
+/* write.c */
 /**
  * read WAV_HEADER_LEN bytes from stdin to audio buffer
  *
@@ -103,6 +116,13 @@ static int read_wav_header(void)
        return 1;
 }
 
+/* alsa_writer.c */
+#define FORMAT SND_PCM_FORMAT_S16_LE
+struct private_alsa_data {
+       snd_pcm_t *handle;
+       size_t bytes_per_frame;
+};
+
 /*
  * open and prepare the PCM handle for writing
  *
@@ -188,6 +208,7 @@ static int alsa_open(struct writer_node *w)
        return period_size * pad->bytes_per_frame;
 }
 
+/* alsa_writer.c */
 /**
  * push out pcm frames
  * \param data pointer do data to be written
@@ -222,6 +243,7 @@ static int alsa_write(char *data, size_t nbytes, struct writer_node *wn)
        return result * pad->bytes_per_frame;
 }
 
+/* alsa_writer.c */
 static void alsa_close(struct writer_node *wn)
 {
        struct private_alsa_data *pad = wn->private_data;
@@ -231,6 +253,7 @@ static void alsa_close(struct writer_node *wn)
        free(pad);
 }
 
+/* alsa_writer.c */
 void alsa_writer_init(struct writer *w)
 {
        w->open = alsa_open;
@@ -240,6 +263,57 @@ void alsa_writer_init(struct writer *w)
 }
 
 
+
+
+/* file_writer.c */
+
+struct private_file_writer_data {
+       int fd;
+};
+static int file_writer_open(struct writer_node *w)
+{
+       struct private_file_writer_data *pfwd = para_calloc(
+               sizeof(struct private_file_writer_data));
+       char *tmp = para_tmpname(), *home = para_homedir(),
+               *filename = make_message("%s/.paraslash/%s", home, tmp);
+
+       free(home);
+       free(tmp);
+       w->private_data = pfwd;
+       pfwd->fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+       free(filename);
+       if (pfwd->fd >= 0)
+               return 8192;
+       free(pfwd);
+       return -E_FW_OPEN;
+}
+
+static int file_writer_write(char *data, size_t nbytes, struct writer_node *wn)
+{
+       struct private_file_writer_data *pfwd = wn->private_data;
+       int ret = write(pfwd->fd, data, nbytes);
+       if (ret < 0)
+               ret = -E_FW_WRITE;
+       return ret;
+}
+
+static void file_writer_close(struct writer_node *wn)
+{
+       struct private_file_writer_data *pfwd = wn->private_data;
+       close(pfwd->fd);
+       free(pfwd);
+}
+
+void file_writer_init(struct writer *w)
+{
+       w->open = file_writer_open;
+       w->write = file_writer_write;
+       w->close = file_writer_close;
+       w->shutdown = NULL; /* nothing to do */
+}
+
+
+/* write.c */
 /**
  * check if current time is later than start_time
  * \param diff pointer to write remaining time to
@@ -260,6 +334,7 @@ static int start_time_in_future(struct timeval *diff)
        return tv_diff(start_time, &now, diff) > 0? 1 : 0;
 }
 
+/* write.c */
 /**
  * sleep until time given at command line
  *
@@ -275,6 +350,7 @@ static void do_initial_delay(struct timeval *delay)
        while (start_time_in_future(delay));
 }
 
+/* write.c */
 static int read_stdin(char *buf, size_t bytes_to_load, size_t *loaded)
 {
        ssize_t ret;
@@ -291,6 +367,11 @@ static int read_stdin(char *buf, size_t bytes_to_load, size_t *loaded)
        return 1;
 }
 
+/* write_common.c */
+
+const char *writer_names[] ={WRITER_NAMES};
+static struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY};
+
 int wng_write(struct writer_node_group *g, char *buf, size_t *loaded)
 {
        int ret, i, need_more_writes = 1;
@@ -336,6 +417,7 @@ out:
        return ret;
 }
 
+/* write_common.c */
 int wng_open(struct writer_node_group *g)
 {
        int i, ret = 1;
@@ -352,6 +434,7 @@ out:
        return ret;
 }
 
+/* write_common.c */
 void wng_close(struct writer_node_group *g)
 {
        int i;
@@ -362,6 +445,7 @@ void wng_close(struct writer_node_group *g)
        }
 }
 
+/* write.c */
 /**
  * play raw pcm data
  * \param loaded number of bytes already loaded
@@ -415,6 +499,7 @@ out:
        return ret;
 }
 
+/* writer_node.c */
 struct writer_node_group *wng_new(unsigned num_writers)
 {
        struct writer_node_group *g = para_calloc(sizeof(struct writer_node_group));
@@ -425,6 +510,7 @@ struct writer_node_group *wng_new(unsigned num_writers)
        return g;
 }
 
+/* writer_node.c */
 void wng_destroy(struct writer_node_group *g)
 {
        if (!g)
@@ -434,6 +520,100 @@ void wng_destroy(struct writer_node_group *g)
        free(g);
 }
 
+void init_supported_writers(void)
+{
+       int i;
+
+       FOR_EACH_WRITER(i)
+               writers[i].init(&writers[i]);
+}
+
+int check_writer_arg(const char *arg)
+{
+       int i, ret = -E_PLAY_SYNTAX;
+       char *a = para_strdup(arg), *p = strchr(a, ':');
+       if (p)
+               *p = '\0';
+       p++;
+       FOR_EACH_WRITER(i) {
+               if (strcmp(writer_names[i], a))
+                       continue;
+               ret = i;
+               goto out;
+       }
+out:
+       free(a);
+       return ret;
+}
+
+struct writer_node_group *setup_default_wng(void)
+{
+       struct writer_node_group *wng = wng_new(1);
+       enum writer_enum default_writer;
+
+       if (NUM_SUPPORTED_WRITERS == 1)
+               default_writer = FILE_WRITE;
+       else
+               default_writer = 1;
+       wng->writer_nodes[0].writer = &writers[default_writer];
+       PARA_INFO_LOG("using default writer: %s\n",
+               writer_names[default_writer]);
+       return wng;
+}
+
+/* write.c */
+
+struct writer_node_group *check_args(void)
+{
+       int i, ret = -E_PLAY_SYNTAX;
+       static struct timeval tv;
+       struct writer_node_group *wng = NULL;
+
+       if (conf.list_writers_given) {
+               char *msg = NULL;
+               FOR_EACH_WRITER(i) {
+                       char *tmp = make_message("%s%s%s",
+                               i? msg : "",
+                               i? " " : "",
+                               writer_names[i]);
+                       free(msg);
+                       msg = tmp;
+               }
+               fprintf(stderr, "%s\n", msg);
+               free(msg);
+               exit(EXIT_SUCCESS);
+       }
+       if (conf.prebuffer_arg < 0 || conf.prebuffer_arg > 100)
+               goto out;
+       if (conf.start_time_given) {
+               long unsigned sec, usec;
+               if (sscanf(conf.start_time_arg, "%lu:%lu",
+                               &sec, &usec) != 2)
+                       goto out;
+               tv.tv_sec = sec;
+               tv.tv_usec = usec;
+               start_time = &tv;
+       }
+       if (!conf.writer_given) {
+               wng = setup_default_wng();
+               ret = 1;
+               goto out;
+       }
+       wng = wng_new(conf.writer_given);
+       for (i = 0; i < conf.writer_given; i++) {
+               ret = check_writer_arg(conf.writer_arg[i]);
+               if (ret < 0)
+                       goto out;
+               wng->writer_nodes[i].writer = &writers[ret];
+       }
+       ret = 1;
+out:
+       if (ret > 0)
+               return wng;
+       free(wng);
+       return NULL;
+}
+
 /**
  * test if audio buffer contains a valid wave header
  *
@@ -450,27 +630,17 @@ static size_t check_wave(void)
        return 0;
 }
 
+/* write.c */
 int main(int argc, char *argv[])
 {
-       struct timeval tv;
        int ret = -E_PLAY_SYNTAX;
        struct writer_node_group *wng = NULL;
 
        cmdline_parser(argc, argv, &conf);
-       if (conf.prebuffer_arg < 0 || conf.prebuffer_arg > 100)
+       wng = check_args();
+       if (!wng)
                goto out;
-       if (conf.start_time_given) {
-               if (sscanf(conf.start_time_arg, "%lu:%lu",
-                               &tv.tv_sec, &tv.tv_usec) != 2)
-                       goto out;
-               start_time = &tv;
-       }
-       /* call init for each supported writer */
-       alsa_writer_init(&writers[0]);
-
-       wng = wng_new(1);
-       wng->writer_nodes[0].writer = &writers[0]; /* alsa */
-
+       init_supported_writers();
        audiobuf = para_malloc(WAV_HEADER_LEN);
        ret = read_wav_header();
        if (ret < 0)
index 23cd64e..b1c8083 100644 (file)
--- a/play.ggo
+++ b/play.ggo
@@ -14,3 +14,20 @@ option "loglevel" l
        default="4"
        optional
 
+
+option "writer" w
+#~~~~~~~~~~~~~~~
+
+"select stream writer"
+
+       string typestr="name"
+       default="alsa (file if alsa is unsupported)"
+       optional
+       multiple
+
+option "list_writers" L
+#~~~~~~~~~~~~~~~~~~~~~~
+"print available writers and exit"
+
+       flag off
+       optional