X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=play.c;h=e1fdb2ef9fb44f3e68554a80b79508456cf2cc07;hp=5d32df7cad66d05726c1cff1256171bf7e6b32c3;hb=773bdfd137b4d4bb350903a1cb6b25ef304b3e0a;hpb=6597f6cdccfd678eca2fecc0ed95dc35039413d9 diff --git a/play.c b/play.c index 5d32df7c..e1fdb2ef 100644 --- a/play.c +++ b/play.c @@ -31,38 +31,60 @@ #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 *); }; -#define NUM_WRITERS 1 -static struct writer writers[NUM_WRITERS]; -#define FOR_EACH_WRITER(i) for (i = 0; i < NUM_WRITERS, i++) -static struct writer_node *writer_nodes; +/* write.h */ +struct writer_node_group { + unsigned num_writers; + struct writer_node *writer_nodes; + int *written; + size_t max_chunk_bytes; + 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++) + +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; @@ -74,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 * @@ -93,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 * @@ -178,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 @@ -212,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; @@ -221,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; @@ -230,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 @@ -250,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 * @@ -265,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; @@ -281,6 +367,85 @@ 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; + size_t min_written = 0; + + while (need_more_writes) { + need_more_writes = 0; + FOR_EACH_WRITER_NODE(i, g) { + size_t w = g->written[i]; + unsigned char *p = buf + w; + int bytes_to_write; + struct writer_node *wn = &g->writer_nodes[i]; + if (!i) + min_written = w; + else + min_written = PARA_MIN(min_written, w); + if (w == *loaded) + continue; + if (!g->eof && (*loaded < wn->chunk_bytes + w)) + continue; + bytes_to_write = PARA_MIN(wn->chunk_bytes, + *loaded - w); + ret = wn->writer->write(p, bytes_to_write, wn); + if (ret < 0) + goto out; + if (ret != bytes_to_write) + PARA_WARNING_LOG("short write: %d/%d\n", ret, + bytes_to_write); + g->written[i] += ret; + need_more_writes = 1; + } + } + *loaded -= min_written; + ret = 0; + if (g->eof) + goto out; + if (*loaded) + memmove(buf, buf + min_written, *loaded); + FOR_EACH_WRITER_NODE(i, g) + g->written[i] -= min_written; + ret = 1; +out: + return ret; +} + +/* write_common.c */ +int wng_open(struct writer_node_group *g) +{ + int i, ret = 1; + + FOR_EACH_WRITER_NODE(i, g) { + struct writer_node *wn = &g->writer_nodes[i]; + ret = wn->writer->open(wn); + if (ret < 0) + goto out; + wn->chunk_bytes = ret; + g->max_chunk_bytes = PARA_MAX(g->max_chunk_bytes, ret); + } +out: + return ret; +} + +/* write_common.c */ +void wng_close(struct writer_node_group *g) +{ + int i; + + FOR_EACH_WRITER_NODE(i, g) { + struct writer_node *wn = &g->writer_nodes[i]; + wn->writer->close(wn); + } +} + +/* write.c */ /** * play raw pcm data * \param loaded number of bytes already loaded @@ -291,27 +456,21 @@ static int read_stdin(char *buf, size_t bytes_to_load, size_t *loaded) * * \return positive on success, negative on errors. */ -static int play_pcm(size_t loaded) +static int pcm_write(struct writer_node_group *wng, size_t loaded) { - size_t bufsize, min_written = 0, prebuf_size, bytes_to_load; + size_t bufsize, prebuf_size, bytes_to_load; struct timeval delay; - int i, max_chunk_bytes = 0, ret, not_yet_started = 1, need_more_writes; - struct writer_node *wn; - size_t *written = para_calloc(1 * sizeof(size_t)); + int ret, not_yet_started = 1; - for (i = 0; i < 1; i++) { - wn = &writer_nodes[i]; - ret = wn->writer->open(wn); - if (ret < 0) - goto out; - wn->chunk_bytes = ret; - max_chunk_bytes = PARA_MAX(max_chunk_bytes, ret); - } - PARA_INFO_LOG("max chunk_bytes: %d\n", max_chunk_bytes); - bufsize = (conf.bufsize_arg * 1024 / max_chunk_bytes) * max_chunk_bytes; + ret = wng_open(wng); + if (ret < 0) + goto out; + PARA_INFO_LOG("max chunk_bytes: %d\n", wng->max_chunk_bytes); + bufsize = (conf.bufsize_arg * 1024 / wng->max_chunk_bytes) + * wng->max_chunk_bytes; audiobuf = para_realloc(audiobuf, bufsize); prebuf_size = conf.prebuffer_arg * bufsize / 100; - bytes_to_load = PARA_MAX(prebuf_size, max_chunk_bytes); + bytes_to_load = PARA_MAX(prebuf_size, wng->max_chunk_bytes); ret = read_stdin(audiobuf, bytes_to_load, &loaded); if (ret <= 0 || loaded < bytes_to_load) { if (ret >= 0) @@ -322,64 +481,137 @@ static int play_pcm(size_t loaded) do_initial_delay(&delay); not_yet_started = 0; again: - need_more_writes = 1; - while (need_more_writes) { - need_more_writes = 0; - for (i = 0; i < 1; i++) { - unsigned char *p = audiobuf + written[i]; - wn = &writer_nodes[i]; - if (!i) - min_written = written[i]; - else - min_written = PARA_MIN(min_written, written[i]); - if (loaded < wn->chunk_bytes + written[i]) - continue; - ret = wn->writer->write(p, wn->chunk_bytes, wn); - if (ret < 0) - goto out; - if (ret != wn->chunk_bytes) - PARA_WARNING_LOG("short write: %d/%d", ret, - wn->chunk_bytes); - written[i] += ret; - need_more_writes = 1; + ret = wng_write(wng, audiobuf, &loaded); + if (ret <= 0) + goto out; + ret = -E_PLAY_OVERRUN; + if (loaded >= bufsize) + goto out; + bytes_to_load = PARA_MIN(wng->max_chunk_bytes, bufsize); + ret = read_stdin(audiobuf, bytes_to_load, &loaded); + if (ret < 0) + goto out; + if (!ret) + wng->eof = 1; + goto again; +out: + wng_close(wng); + 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)); + g->num_writers = num_writers; + g->writer_nodes = para_calloc(num_writers + * sizeof(struct writer_node)); + g->written = para_calloc(num_writers * sizeof(size_t)); + return g; +} + +/* writer_node.c */ +void wng_destroy(struct writer_node_group *g) +{ + if (!g) + return; + free(g->written); + free(g->writer_nodes); + 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); } - loaded -= min_written; - if (loaded > 0) { - if (loaded >= bufsize) { - ret = -E_PLAY_OVERRUN; + 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; - } - memmove(audiobuf, audiobuf + min_written, loaded); + tv.tv_sec = sec; + tv.tv_usec = usec; + start_time = &tv; } - for (i = 0; i < 1; i++) - written[i] -= min_written; - bytes_to_load = PARA_MIN(max_chunk_bytes, bufsize); - ret = read_stdin(audiobuf, bytes_to_load, &loaded); - if (ret < 0) + if (!conf.writer_given) { + wng = setup_default_wng(); + ret = 1; goto out; - if (ret) - goto again; - for (i = 0; i < 1; i++) { - unsigned char *p = audiobuf + written[i]; - wn = &writer_nodes[i]; - int bytes_to_write; - if (written[i] >= loaded) - continue; - bytes_to_write = PARA_MIN(wn->chunk_bytes, loaded - written[i]); - ret = wn->writer->write(p, bytes_to_write, wn); + } + 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; - written[i] += ret; + wng->writer_nodes[i].writer = &writers[ret]; } - ret = 0; + ret = 1; out: - free(written); - for (i = 0; i < 1; i++) { - wn = &writer_nodes[i]; - wn->writer->close(wn); - } - return ret; + if (ret > 0) + return wng; + free(wng); + return NULL; } /** @@ -398,34 +630,25 @@ 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]); - /* one for each given writer */ - writer_nodes = para_calloc(2 * sizeof(struct writer_node)); - writer_nodes[0].writer = &writers[0]; /* alsa */ - + init_supported_writers(); audiobuf = para_malloc(WAV_HEADER_LEN); ret = read_wav_header(); if (ret < 0) goto out; - ret = play_pcm(check_wave()); + ret = pcm_write(wng, check_wave()); out: + wng_destroy(wng); free(audiobuf); - free(writer_nodes); if (ret < 0) PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret)); return ret;