From: Andre Date: Tue, 18 Apr 2006 19:53:48 +0000 (+0200) Subject: split play.c and rename para_play to para_write X-Git-Tag: v0.2.12~68 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=780f86d5f849308b5100b087c6b223a6deef1dd7 split play.c and rename para_play to para_write This separates the code to write an audio stream into several independent pieces: write.h: definition of writer-related structures write.c: the para_write main program write_common.c: functions to be shared between para_write and para_audiod write_common.h: exported functions of write_common.c alsa_writer.c: writes the stream to an alsa sound device file_writer.c: writes the stream to a file Other writers (aka output plugins) can be added easily. --- diff --git a/Makefile.in b/Makefile.in index 50767941..68c60e19 100644 --- a/Makefile.in +++ b/Makefile.in @@ -56,7 +56,7 @@ CPPFLAGS += -DCODENAME='"$(codename)"' CPPFLAGS += -DCC_VERSION='"$(cc_version)"' CPPFLAGS += -Werror-implicit-function-declaration -BINARIES = para_server para_client para_gui para_audiod para_audioc para_recv para_filter $(extra_binaries) +BINARIES = para_server para_client para_gui para_audiod para_audioc para_recv para_filter para_write $(extra_binaries) FONTS := $(wildcard fonts/*.png) PICS := $(wildcard pics/paraslash/*.jpg) @@ -195,8 +195,8 @@ para_server: @server_objs@ para_sdl_gui: $(sdl_gui_objs) $(CC) -o $@ $(sdl_gui_objs) -lSDL_image -para_play: @play_objs@ - $(CC) -o $@ @play_objs@ @play_ldflags@ +para_write: @write_objs@ + $(CC) -o $@ @write_objs@ @write_ldflags@ para_compress: $(compress_objs) $(CC) -o $@ $(compress_objs) diff --git a/alsa_writer.c b/alsa_writer.c new file mode 100644 index 00000000..b0bc3b09 --- /dev/null +++ b/alsa_writer.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2005-2006 Andre Noll + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +/* + * Based in parts on aplay.c from the alsa-utils-1.0.8 package, + * Copyright (c) by Jaroslav Kysela , which is + * based on the vplay program by Michael Beck. + */ + +#include "para.h" +#include "fd.h" +#include "string.h" +#include "write.h" + +#include + +#include "write.cmdline.h" +#include "error.h" + +extern struct gengetopt_args_info conf; + +#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 + * + * Install PCM software and hardware configuration. Exit on errors. + */ +static int alsa_open(struct writer_node *w) +{ + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + snd_pcm_uframes_t buffer_size, xfer_align, start_threshold, + stop_threshold; + unsigned buffer_time = 0; + int err; + snd_pcm_info_t *info; + snd_output_t *log; + snd_pcm_uframes_t period_size; + struct private_alsa_data *pad = para_malloc(sizeof(struct private_alsa_data)); + w->private_data = pad; + + snd_pcm_info_alloca(&info); + if (snd_output_stdio_attach(&log, stderr, 0) < 0) + return -E_ALSA_LOG; + err = snd_pcm_open(&pad->handle, conf.device_arg, + SND_PCM_STREAM_PLAYBACK, 0); + if (err < 0) + return -E_PCM_OPEN; + if ((err = snd_pcm_info(pad->handle, info)) < 0) + return -E_SND_PCM_INFO; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + if (snd_pcm_hw_params_any(pad->handle, hwparams) < 0) + return -E_BROKEN_CONF; + if (snd_pcm_hw_params_set_access(pad->handle, hwparams, + SND_PCM_ACCESS_RW_INTERLEAVED) < 0) + return -E_ACCESS_TYPE; + if (snd_pcm_hw_params_set_format(pad->handle, hwparams, FORMAT) < 0) + return -E_SAMPLE_FORMAT; + if (snd_pcm_hw_params_set_channels(pad->handle, hwparams, + conf.channels_arg) < 0) + return -E_CHANNEL_COUNT; + if (snd_pcm_hw_params_set_rate_near(pad->handle, hwparams, + (unsigned int*) &conf.sample_rate_arg, 0) < 0) + return -E_SET_RATE; + err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0); + if (err < 0 || !buffer_time) + return -E_GET_BUFFER_TIME; + PARA_DEBUG_LOG("buffer time: %d\n", buffer_time); + if (snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams, + &buffer_time, 0) < 0) + return -E_SET_BUFFER_TIME; + if (snd_pcm_hw_params(pad->handle, hwparams) < 0) + return -E_HW_PARAMS; + snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0); + snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); + PARA_DEBUG_LOG("buffer size: %lu, period_size: %lu\n", buffer_size, + period_size); + if (period_size == buffer_size) + return -E_BAD_PERIOD; + snd_pcm_sw_params_current(pad->handle, swparams); + err = snd_pcm_sw_params_get_xfer_align(swparams, &xfer_align); + if (err < 0 || !xfer_align) + return -E_GET_XFER; + snd_pcm_sw_params_set_avail_min(pad->handle, swparams, period_size); + /* round to closest transfer boundary */ + start_threshold = (buffer_size / xfer_align) * xfer_align; + if (start_threshold < 1) + start_threshold = 1; + if (snd_pcm_sw_params_set_start_threshold(pad->handle, swparams, + start_threshold) < 0) + return -E_START_THRESHOLD; + stop_threshold = buffer_size; + if (snd_pcm_sw_params_set_stop_threshold(pad->handle, swparams, + stop_threshold) < 0) + return -E_STOP_THRESHOLD; + if (snd_pcm_sw_params_set_xfer_align(pad->handle, swparams, + xfer_align) < 0) + return -E_SET_XFER; + if (snd_pcm_sw_params(pad->handle, swparams) < 0) + return -E_SW_PARAMS; + pad->bytes_per_frame = snd_pcm_format_physical_width(FORMAT) + * conf.channels_arg / 8; + return period_size * pad->bytes_per_frame; +} + +/** + * push out pcm frames + * \param data pointer do data to be written + * \param nbytes number of bytes (not frames) + * + * \return Number of bytes written, -E_ALSA_WRITE on errors. + */ +static int alsa_write(char *data, size_t nbytes, struct writer_node *wn) +{ + struct private_alsa_data *pad = wn->private_data; + size_t frames = nbytes / pad->bytes_per_frame; + unsigned char *d = data; + snd_pcm_sframes_t r, result = 0; + + while (frames > 0) { + /* write interleaved frames */ + r = snd_pcm_writei(pad->handle, d, frames); + if (r < 0) + PARA_ERROR_LOG("write error: %s\n", snd_strerror(r)); + if (r == -EAGAIN || (r >= 0 && r < frames)) + snd_pcm_wait(pad->handle, 1); + else if (r == -EPIPE) + snd_pcm_prepare(pad->handle); + else if (r < 0) + return -E_ALSA_WRITE; + if (r > 0) { + result += r; + frames -= r; + d += r * pad->bytes_per_frame; + } + } + return result * pad->bytes_per_frame; +} + +static void alsa_close(struct writer_node *wn) +{ + struct private_alsa_data *pad = wn->private_data; + snd_pcm_drain(pad->handle); + snd_pcm_close(pad->handle); + snd_config_update_free_global(); + free(pad); +} + +void alsa_writer_init(struct writer *w) +{ + w->open = alsa_open; + w->write = alsa_write; + w->close = alsa_close; + w->shutdown = NULL; /* nothing to do */ +} diff --git a/configure.ac b/configure.ac index bfd659f1..69846789 100644 --- a/configure.ac +++ b/configure.ac @@ -76,9 +76,9 @@ server_errlist_objs="server mp3 afs command net string signal random_selector ipc dccp dccp_send fd" server_ldflags="" -play_cmdline_objs="play.cmdline" -play_errlist_objs="play time fd string" -play_ldflags="" +write_cmdline_objs="write.cmdline" +write_errlist_objs="write write_common file_writer time fd string" +write_ldflags="" write_writers="file" @@ -220,7 +220,7 @@ else fi ########################################################################### alsa have_alsa="yes" -msg="=> no para_play" +msg="=> no alsa support for para_write" AC_CHECK_HEADERS([alsa/asoundlib.h], [], [ AC_MSG_WARN([no alsa/asoundlib $msg]) have_alsa="no" @@ -230,8 +230,8 @@ AC_CHECK_LIB([asound], [snd_pcm_open], [], [ have_alsa="no" ]) if test "$have_alsa" = "yes"; then - extras="$extras para_play" - play_ldflags="$play_ldflags -lasound" + write_errlist_objs="$write_errlist_objs alsa_writer" + write_ldflags="$write_ldflags -lasound" write_writers="$write_writers alsa" fi ########################################################################### ortp @@ -296,7 +296,7 @@ recv_objs="$recv_cmdline_objs $recv_errlist_objs" filter_objs="$filter_cmdline_objs $filter_errlist_objs" audiod_objs="$audiod_cmdline_objs $audiod_errlist_objs" server_objs="$server_cmdline_objs $server_errlist_objs" -play_objs="$play_cmdline_objs $play_errlist_objs" +write_objs="$write_cmdline_objs $write_errlist_objs" AC_SUBST(recv_objs, add_dot_o($recv_objs)) AC_SUBST(recv_ldflags, $recv_ldflags) @@ -318,10 +318,10 @@ AC_SUBST(server_ldflags, $server_ldflags) AC_DEFINE_UNQUOTED(INIT_SERVER_ERRLISTS, objlist_to_errlist($server_errlist_objs), errors used by para_server) -AC_SUBST(play_objs, add_dot_o($play_objs)) -AC_SUBST(play_ldflags, $play_ldflags) -AC_DEFINE_UNQUOTED(INIT_PLAY_ERRLISTS, - objlist_to_errlist($play_errlist_objs), errors used by para_play) +AC_SUBST(write_objs, add_dot_o($write_objs)) +AC_SUBST(write_ldflags, $write_ldflags) +AC_DEFINE_UNQUOTED(INIT_WRITE_ERRLISTS, + objlist_to_errlist($write_errlist_objs), errors used by para_write) 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, @@ -352,5 +352,5 @@ ogg vorbis support: $have_ogg mp3dec support (libmad): $have_mad ortp support: $have_ortp unix socket credentials: $have_ucred -alsa support (para_play): $have_alsa +supported writers for para_write: $write_writers ]) diff --git a/error.h b/error.h index 81691667..237978eb 100644 --- a/error.h +++ b/error.h @@ -58,8 +58,10 @@ enum para_subsystem { SS_DCCP_SEND, SS_FD, SS_GUI, - SS_PLAY, - SS_ALSA, + SS_WRITE, + SS_WRITE_COMMON, + SS_ALSA_WRITER, + SS_FILE_WRITER, SS_RINGBUFFER}; #define NUM_SS (SS_RINGBUFFER + 1) @@ -297,13 +299,16 @@ extern const char **para_errlist[]; PARA_ERROR(F_SETFL, "failed to set fd flags"), \ -#define PLAY_ERRORS \ +#define WRITE_ERRORS \ PARA_ERROR(READ_HDR, "failed to read audio file header"), \ PARA_ERROR(READ_STDIN, "failed to read from stdin"), \ - PARA_ERROR(PLAY_SYNTAX, "syntax error"), \ - PARA_ERROR(PLAY_OVERRUN, "buffer overrun"), \ + PARA_ERROR(WRITE_SYNTAX, "para_write syntax error"), \ + PARA_ERROR(WRITE_OVERRUN, "buffer overrun"), \ PARA_ERROR(LIST_WRITERS_GIVEN, ""), \ PARA_ERROR(PREMATURE_END, "premature end of audio file"), \ + + +#define ALSA_WRITER_ERRORS \ PARA_ERROR(BROKEN_CONF, "Broken alsa configuration"), \ PARA_ERROR(ACCESS_TYPE, "alsa access type not available"), \ PARA_ERROR(SAMPLE_FORMAT, "sample format not available"), \ @@ -322,13 +327,17 @@ 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"), \ + + +#define FILE_WRITER_ERRORS \ PARA_ERROR(FW_WRITE, "file writer write error"), \ PARA_ERROR(FW_OPEN, "file writer: can not open output file"), \ +#define WRITE_COMMON_ERRORS \ + PARA_ERROR(WRITE_COMMON_SYNTAX, "syntax error in write option"), \ /* these do not need error handling (yet) */ -#define ALSA_ERRORS #define SERVER_ERRORS #define WAV_ERRORS #define COMPRESS_ERRORS @@ -448,8 +457,10 @@ SS_ENUM(DCCP_RECV); SS_ENUM(DCCP_SEND); SS_ENUM(FD); SS_ENUM(GUI); -SS_ENUM(PLAY); -SS_ENUM(ALSA); +SS_ENUM(WRITE); +SS_ENUM(WRITE_COMMON); +SS_ENUM(ALSA_WRITER); +SS_ENUM(FILE_WRITER); SS_ENUM(RINGBUFFER); /** \endcond */ #undef PARA_ERROR diff --git a/file_writer.c b/file_writer.c new file mode 100644 index 00000000..7878cce1 --- /dev/null +++ b/file_writer.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 Andre Noll + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include "para.h" +#include "write.h" +#include "string.h" +#include "error.h" + +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 */ +} diff --git a/play.c b/play.c deleted file mode 100644 index e1fdb2ef..00000000 --- a/play.c +++ /dev/null @@ -1,655 +0,0 @@ -/* - * Copyright (C) 2005-2006 Andre Noll - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - */ - -/* - * Based in parts on aplay.c from the alsa-utils-1.0.8 package, - * Copyright (c) by Jaroslav Kysela , which is - * based on the vplay program by Michael Beck. - */ - -#define WAV_HEADER_LEN 44 -#include /* gettimeofday */ -#include "para.h" -#include "fd.h" -#include "play.cmdline.h" -#include -#include "string.h" -#include "error.h" - -/* -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; - 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; - - if (ll < conf.loglevel_arg) - return; - va_start(argp, fmt); - vfprintf(stderr, fmt, argp); - va_end(argp); -} - -/* write.c */ -/** - * read WAV_HEADER_LEN bytes from stdin to audio buffer - * - * \return -E_READ_HDR on errors and on eof before WAV_HEADER_LEN could be - * read. A positive return value indicates success. - */ -static int read_wav_header(void) -{ - ssize_t ret, count = 0; - - while (count < WAV_HEADER_LEN) { - ret = read(STDIN_FILENO, audiobuf + count, WAV_HEADER_LEN - count); - if (ret <= 0) - return -E_READ_HDR; - count += ret; - } - 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 - * - * Install PCM software and hardware configuration. Exit on errors. - */ -static int alsa_open(struct writer_node *w) -{ - snd_pcm_hw_params_t *hwparams; - snd_pcm_sw_params_t *swparams; - snd_pcm_uframes_t buffer_size, xfer_align, start_threshold, - stop_threshold; - unsigned buffer_time = 0; - int err; - snd_pcm_info_t *info; - snd_output_t *log; - snd_pcm_uframes_t period_size; - struct private_alsa_data *pad = para_malloc(sizeof(struct private_alsa_data)); - w->private_data = pad; - - snd_pcm_info_alloca(&info); - if (snd_output_stdio_attach(&log, stderr, 0) < 0) - return -E_ALSA_LOG; - err = snd_pcm_open(&pad->handle, conf.device_arg, - SND_PCM_STREAM_PLAYBACK, 0); - if (err < 0) - return -E_PCM_OPEN; - if ((err = snd_pcm_info(pad->handle, info)) < 0) - return -E_SND_PCM_INFO; - - snd_pcm_hw_params_alloca(&hwparams); - snd_pcm_sw_params_alloca(&swparams); - if (snd_pcm_hw_params_any(pad->handle, hwparams) < 0) - return -E_BROKEN_CONF; - if (snd_pcm_hw_params_set_access(pad->handle, hwparams, - SND_PCM_ACCESS_RW_INTERLEAVED) < 0) - return -E_ACCESS_TYPE; - if (snd_pcm_hw_params_set_format(pad->handle, hwparams, FORMAT) < 0) - return -E_SAMPLE_FORMAT; - if (snd_pcm_hw_params_set_channels(pad->handle, hwparams, - conf.channels_arg) < 0) - return -E_CHANNEL_COUNT; - if (snd_pcm_hw_params_set_rate_near(pad->handle, hwparams, - (unsigned int*) &conf.sample_rate_arg, 0) < 0) - return -E_SET_RATE; - err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0); - if (err < 0 || !buffer_time) - return -E_GET_BUFFER_TIME; - PARA_DEBUG_LOG("buffer time: %d\n", buffer_time); - if (snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams, - &buffer_time, 0) < 0) - return -E_SET_BUFFER_TIME; - if (snd_pcm_hw_params(pad->handle, hwparams) < 0) - return -E_HW_PARAMS; - snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0); - snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); - PARA_DEBUG_LOG("buffer size: %lu, period_size: %lu\n", buffer_size, - period_size); - if (period_size == buffer_size) - return -E_BAD_PERIOD; - snd_pcm_sw_params_current(pad->handle, swparams); - err = snd_pcm_sw_params_get_xfer_align(swparams, &xfer_align); - if (err < 0 || !xfer_align) - return -E_GET_XFER; - snd_pcm_sw_params_set_avail_min(pad->handle, swparams, period_size); - /* round to closest transfer boundary */ - start_threshold = (buffer_size / xfer_align) * xfer_align; - if (start_threshold < 1) - start_threshold = 1; - if (snd_pcm_sw_params_set_start_threshold(pad->handle, swparams, - start_threshold) < 0) - return -E_START_THRESHOLD; - stop_threshold = buffer_size; - if (snd_pcm_sw_params_set_stop_threshold(pad->handle, swparams, - stop_threshold) < 0) - return -E_STOP_THRESHOLD; - if (snd_pcm_sw_params_set_xfer_align(pad->handle, swparams, - xfer_align) < 0) - return -E_SET_XFER; - if (snd_pcm_sw_params(pad->handle, swparams) < 0) - return -E_SW_PARAMS; - pad->bytes_per_frame = snd_pcm_format_physical_width(FORMAT) - * conf.channels_arg / 8; - return period_size * pad->bytes_per_frame; -} - -/* alsa_writer.c */ -/** - * push out pcm frames - * \param data pointer do data to be written - * \param nbytes number of bytes (not frames) - * - * \return Number of bytes written, -E_ALSA_WRITE on errors. - */ -static int alsa_write(char *data, size_t nbytes, struct writer_node *wn) -{ - struct private_alsa_data *pad = wn->private_data; - size_t frames = nbytes / pad->bytes_per_frame; - unsigned char *d = data; - snd_pcm_sframes_t r, result = 0; - - while (frames > 0) { - /* write interleaved frames */ - r = snd_pcm_writei(pad->handle, d, frames); - if (r < 0) - PARA_ERROR_LOG("write error: %s\n", snd_strerror(r)); - if (r == -EAGAIN || (r >= 0 && r < frames)) - snd_pcm_wait(pad->handle, 1); - else if (r == -EPIPE) - snd_pcm_prepare(pad->handle); - else if (r < 0) - return -E_ALSA_WRITE; - if (r > 0) { - result += r; - frames -= r; - d += r * pad->bytes_per_frame; - } - } - 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; - snd_pcm_drain(pad->handle); - snd_pcm_close(pad->handle); - snd_config_update_free_global(); - free(pad); -} - -/* alsa_writer.c */ -void alsa_writer_init(struct writer *w) -{ - w->open = alsa_open; - w->write = alsa_write; - w->close = alsa_close; - w->shutdown = NULL; /* nothing to do */ -} - - - - -/* 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 - * - * If start_time was not given, or current time is later than given - * start_time, return 0. Otherwise, return 1 and write the time - * difference between current time and start_time to diff. diff may be - * NULL. - * - */ -static int start_time_in_future(struct timeval *diff) -{ - struct timeval now; - - if (!conf.start_time_given) - return 0; - gettimeofday(&now, NULL); - return tv_diff(start_time, &now, diff) > 0? 1 : 0; -} - -/* write.c */ -/** - * sleep until time given at command line - * - * This is called if the initial buffer is filled. It returns - * immediately if no start_time was given at the command line - * or if the given start time is in the past. - * - */ -static void do_initial_delay(struct timeval *delay) -{ - do - para_select(1, NULL, NULL, 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; - - while (*loaded < bytes_to_load) { - ret = read(STDIN_FILENO, buf + *loaded, bytes_to_load - *loaded); - if (ret <= 0) { - if (ret < 0) - ret = -E_READ_STDIN; - return ret; - } - *loaded += ret; - } - 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 - * - * If start_time was given, prebuffer data until buffer is full or - * start_time is reached. In any case, do not start playing before - * start_time. - * - * \return positive on success, negative on errors. - */ -static int pcm_write(struct writer_node_group *wng, size_t loaded) -{ - size_t bufsize, prebuf_size, bytes_to_load; - struct timeval delay; - int ret, not_yet_started = 1; - - 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, wng->max_chunk_bytes); - ret = read_stdin(audiobuf, bytes_to_load, &loaded); - if (ret <= 0 || loaded < bytes_to_load) { - if (ret >= 0) - ret = -E_PREMATURE_END; - goto out; - } - if (not_yet_started && start_time && start_time_in_future(&delay)) - do_initial_delay(&delay); - not_yet_started = 0; -again: - 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); - } - 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 - * - * \return If not, return 0, otherwise, store number of channels and sample rate - * in struct conf and return WAV_HEADER_LEN. - */ -static size_t check_wave(void) -{ - unsigned char *a = audiobuf; - if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' || a[3] != 'F') - return WAV_HEADER_LEN; - conf.channels_arg = (unsigned) a[22]; - conf.sample_rate_arg = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24); - return 0; -} - -/* write.c */ -int main(int argc, char *argv[]) -{ - int ret = -E_PLAY_SYNTAX; - struct writer_node_group *wng = NULL; - - cmdline_parser(argc, argv, &conf); - wng = check_args(); - if (!wng) - goto out; - init_supported_writers(); - audiobuf = para_malloc(WAV_HEADER_LEN); - ret = read_wav_header(); - if (ret < 0) - goto out; - ret = pcm_write(wng, check_wave()); -out: - wng_destroy(wng); - free(audiobuf); - if (ret < 0) - PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret)); - return ret; -} diff --git a/play.ggo b/play.ggo deleted file mode 100644 index b1c80839..00000000 --- a/play.ggo +++ /dev/null @@ -1,33 +0,0 @@ -section "general options" -option "start_time" t "start playback at given time which must be in a:b format where a denotes seconds and b denotes microseconds since the epoch" string typestr="timeval" optional -option "device" d "set PCM device" string typestr="device" default="plughw:0,0" optional -option "channels" c "number of channels (only neccessary for raw audio)" int typestr="num" default="2" optional -option "sample_rate" s "force given sample rate (only neccessary for raw audio)" int typestr="num" default="44100" optional -option "bufsize" b "input buffer size" int typestr="kilobytes" default="64" optional -option "prebuffer" p "delay playback until buffer is filled" int typestr="percent" default="100" optional -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ - -"set loglevel (0-6)" - - int typestr="level" - 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 diff --git a/write.c b/write.c new file mode 100644 index 00000000..5e8c3195 --- /dev/null +++ b/write.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005-2006 Andre Noll + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include "para.h" +#include "string.h" +#include "write.cmdline.h" +#include "write.h" +#include "write_common.h" +#include "fd.h" + +#include /* gettimeofday */ + +#include "error.h" + +#define WAV_HEADER_LEN 44 + +static unsigned char *audiobuf; +static struct timeval *start_time; +struct gengetopt_args_info conf; + +INIT_WRITE_ERRLISTS; + +void para_log(int ll, const char* fmt,...) +{ + va_list argp; + + if (ll < conf.loglevel_arg) + return; + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); +} + +/** + * read WAV_HEADER_LEN bytes from stdin to audio buffer + * + * \return -E_READ_HDR on errors and on eof before WAV_HEADER_LEN could be + * read. A positive return value indicates success. + */ +static int read_wav_header(void) +{ + ssize_t ret, count = 0; + + while (count < WAV_HEADER_LEN) { + ret = read(STDIN_FILENO, audiobuf + count, WAV_HEADER_LEN - count); + if (ret <= 0) + return -E_READ_HDR; + count += ret; + } + return 1; +} + +/** + * check if current time is later than start_time + * \param diff pointer to write remaining time to + * + * If start_time was not given, or current time is later than given + * start_time, return 0. Otherwise, return 1 and write the time + * difference between current time and start_time to diff. diff may be + * NULL. + * + */ +static int start_time_in_future(struct timeval *diff) +{ + struct timeval now; + + if (!conf.start_time_given) + return 0; + gettimeofday(&now, NULL); + return tv_diff(start_time, &now, diff) > 0? 1 : 0; +} + +/** + * sleep until time given at command line + * + * This is called if the initial buffer is filled. It returns + * immediately if no start_time was given at the command line + * or if the given start time is in the past. + * + */ +static void do_initial_delay(struct timeval *delay) +{ + do + para_select(1, NULL, NULL, delay); + while (start_time_in_future(delay)); +} + +static int read_stdin(char *buf, size_t bytes_to_load, size_t *loaded) +{ + ssize_t ret; + + while (*loaded < bytes_to_load) { + ret = read(STDIN_FILENO, buf + *loaded, bytes_to_load - *loaded); + if (ret <= 0) { + if (ret < 0) + ret = -E_READ_STDIN; + return ret; + } + *loaded += ret; + } + return 1; +} +/** + * play raw pcm data + * \param loaded number of bytes already loaded + * + * If start_time was given, prebuffer data until buffer is full or + * start_time is reached. In any case, do not start playing before + * start_time. + * + * \return positive on success, negative on errors. + */ +static int pcm_write(struct writer_node_group *wng, size_t loaded) +{ + size_t bufsize, prebuf_size, bytes_to_load; + struct timeval delay; + int ret, not_yet_started = 1; + + 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, wng->max_chunk_bytes); + ret = read_stdin(audiobuf, bytes_to_load, &loaded); + if (ret <= 0 || loaded < bytes_to_load) { + if (ret >= 0) + ret = -E_PREMATURE_END; + goto out; + } + if (not_yet_started && start_time && start_time_in_future(&delay)) + do_initial_delay(&delay); + not_yet_started = 0; +again: + ret = wng_write(wng, audiobuf, &loaded); + if (ret <= 0) + goto out; + ret = -E_WRITE_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; +} +/* write.c */ + +struct writer_node_group *check_args(void) +{ + int i, ret = -E_WRITE_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 + * + * \return If not, return 0, otherwise, store number of channels and sample rate + * in struct conf and return WAV_HEADER_LEN. + */ +static size_t check_wave(void) +{ + unsigned char *a = audiobuf; + if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' || a[3] != 'F') + return WAV_HEADER_LEN; + conf.channels_arg = (unsigned) a[22]; + conf.sample_rate_arg = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24); + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = -E_WRITE_SYNTAX; + struct writer_node_group *wng = NULL; + + cmdline_parser(argc, argv, &conf); + wng = check_args(); + if (!wng) + goto out; + init_supported_writers(); + audiobuf = para_malloc(WAV_HEADER_LEN); + ret = read_wav_header(); + if (ret < 0) + goto out; + ret = pcm_write(wng, check_wave()); +out: + wng_destroy(wng); + free(audiobuf); + if (ret < 0) + PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret)); + return ret; +} diff --git a/write.ggo b/write.ggo new file mode 100644 index 00000000..b1c80839 --- /dev/null +++ b/write.ggo @@ -0,0 +1,33 @@ +section "general options" +option "start_time" t "start playback at given time which must be in a:b format where a denotes seconds and b denotes microseconds since the epoch" string typestr="timeval" optional +option "device" d "set PCM device" string typestr="device" default="plughw:0,0" optional +option "channels" c "number of channels (only neccessary for raw audio)" int typestr="num" default="2" optional +option "sample_rate" s "force given sample rate (only neccessary for raw audio)" int typestr="num" default="44100" optional +option "bufsize" b "input buffer size" int typestr="kilobytes" default="64" optional +option "prebuffer" p "delay playback until buffer is filled" int typestr="percent" default="100" optional +option "loglevel" l +#~~~~~~~~~~~~~~~~~~ + +"set loglevel (0-6)" + + int typestr="level" + 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 diff --git a/write.h b/write.h new file mode 100644 index 00000000..68730bf1 --- /dev/null +++ b/write.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 Andre Noll + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +enum writer_enum {WRITER_ENUM}; + +struct writer_node { + struct writer *writer; + void *private_data; + int chunk_bytes; +}; + +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 *); +}; + +struct writer_node_group { + unsigned num_writers; + struct writer_node *writer_nodes; + int *written; + size_t max_chunk_bytes; + int eof; +}; + +#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; +const char *writer_names[NUM_SUPPORTED_WRITERS]; +struct writer writers[NUM_SUPPORTED_WRITERS]; diff --git a/write_common.c b/write_common.c new file mode 100644 index 00000000..a7896fcb --- /dev/null +++ b/write_common.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2006 Andre Noll + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include "para.h" +#include "string.h" +#include "write.h" +#include "error.h" + +const char *writer_names[] ={WRITER_NAMES}; +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; +} + +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; +} + +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); + } +} + +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; +} + +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_WRITE_COMMON_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; +} diff --git a/write_common.h b/write_common.h new file mode 100644 index 00000000..e7652e20 --- /dev/null +++ b/write_common.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006 Andre Noll + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +int wng_write(struct writer_node_group *g, char *buf, size_t *loaded); +int wng_open(struct writer_node_group *g); +void wng_close(struct writer_node_group *g); +struct writer_node_group *wng_new(unsigned num_writers); +void wng_destroy(struct writer_node_group *g); +void init_supported_writers(void); +int check_writer_arg(const char *arg); +struct writer_node_group *setup_default_wng(void);