From: Andre Noll Date: Sun, 20 Aug 2006 16:52:34 +0000 (+0200) Subject: rename xxx_writer.c to xxx_write.c X-Git-Tag: v0.2.14~39 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=a5937a0eca5ada5cb2332dbce676b327aa9e37ee;ds=sidebyside rename xxx_writer.c to xxx_write.c Just to be consistent.. --- diff --git a/README b/README index a1e8adc1..02d647a9 100644 --- a/README +++ b/README @@ -69,9 +69,9 @@ read(2)/write(2)/select(2) etc. functions. para_write (obligatory) ---------------------- -A modular audio stream writer. It supports a simple file writer output -plugin and an optional wav/raw player for alsa. Debian package: -libasound2-dev +A modular audio stream writer. It supports a simple file writer output plugin +and optional wav/raw players for alsa (linux-only, Debian package: +libasound2-dev) and Mac OS. --------------------------------------- para_audiod (optional, but recommended) diff --git a/alsa_write.c b/alsa_write.c new file mode 100644 index 00000000..c104a5d5 --- /dev/null +++ b/alsa_write.c @@ -0,0 +1,239 @@ +/* + * 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. + */ + +/** \file alsa_write.c paraslash's alsa output plugin */ + +/* + * 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 "list.h" +#include "sched.h" +#include "write.h" + +#include + +#include "alsa_write.cmdline.h" +#include "error.h" + + +#define FORMAT SND_PCM_FORMAT_S16_LE + +/** data specific to the alsa writer */ +struct private_alsa_write_data { + /** the alsa handle */ + snd_pcm_t *handle; + /** determined and set by alsa_open() */ + size_t bytes_per_frame; + /** don't write anything until this time */ + struct timeval next_chunk; + /** the return value of snd_pcm_hw_params_get_buffer_time_max() */ + unsigned buffer_time; + unsigned samplerate; + unsigned channels; +}; + +/* + * 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; + int err; + snd_pcm_info_t *info; + snd_pcm_uframes_t period_size; + struct private_alsa_write_data *pad = para_calloc(sizeof(struct + private_alsa_write_data)); + struct alsa_write_args_info *conf = w->conf; + struct writer_node_group *wng = w->wng; + + if (!conf->samplerate_given && wng->samplerate) + pad->samplerate = *wng->samplerate; + else + pad->samplerate = conf->samplerate_arg; + if (!conf->channels_given && wng->channels) + pad->channels = *wng->channels; + else + pad->channels = conf->channels_arg; + PARA_INFO_LOG("%d channel(s), %dHz\n", pad->channels, pad->samplerate); + w->private_data = pad; + snd_pcm_info_alloca(&info); + err = snd_pcm_open(&pad->handle, conf->device_arg, + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + 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, + pad->channels) < 0) + return -E_CHANNEL_COUNT; + if (snd_pcm_hw_params_set_rate_near(pad->handle, hwparams, + &pad->samplerate, 0) < 0) + return -E_SET_RATE; + err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &pad->buffer_time, 0); + if (err < 0 || !pad->buffer_time) + return -E_GET_BUFFER_TIME; + PARA_INFO_LOG("buffer time: %d\n", pad->buffer_time); + if (snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams, + &pad->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_INFO_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) + * pad->channels / 8; + if (snd_pcm_nonblock(pad->handle, 1)) + PARA_ERROR_LOG("%s\n", "failed to set nonblock mode"); + return period_size * pad->bytes_per_frame; +} + +static int alsa_write_pre_select(struct sched *s, struct writer_node *wn) +{ + struct private_alsa_write_data *pad = wn->private_data; + struct writer_node_group *wng = wn->wng; + struct timeval diff; + + if (*wng->loaded < pad->bytes_per_frame) + return 1; + if (tv_diff(now, &pad->next_chunk, &diff) < 0) { + if (tv_diff(&s->timeout, &diff, NULL) > 0) + s->timeout = diff; + } else { + s->timeout.tv_sec = 0; + s->timeout.tv_usec = 1; + } + return 1; +// PARA_INFO_LOG("timeout: %lu\n", tv2ms(&s->timeout)); +} + +static int alsa_write_post_select(__a_unused struct sched *s, + struct writer_node *wn) +{ + struct private_alsa__write_data *pad = wn->private_data; + struct writer_node_group *wng = wn->wng; + size_t frames = (*wng->loaded - wn->written) / pad->bytes_per_frame; + snd_pcm_sframes_t ret; + unsigned char *data = (unsigned char*)wng->buf + wn->written; + struct timeval tv; + +// PARA_INFO_LOG("%zd frames\n", frames); + if (!frames) { + if (*wng->input_eof) + wn->written = *wng->loaded; + return 1; + } + if (tv_diff(now, &pad->next_chunk, NULL) < 0) + return 1; + ret = snd_pcm_writei(pad->handle, data, frames); + if (ret == -EPIPE) { + PARA_WARNING_LOG("%s", "EPIPE\n"); + snd_pcm_prepare(pad->handle); + return 1; + } + if (ret < 0) { + PARA_WARNING_LOG("%s", "ALSA ERROR\n"); + return -E_ALSA_WRITE; + } + ms2tv(pad->buffer_time / 4000, &tv); +// ms2tv(1, &tv); + tv_add(now, &tv, &pad->next_chunk); + wn->written += ret * pad->bytes_per_frame; + return 1; +} + +static void alsa_close(struct writer_node *wn) +{ + struct private_alsa_write_data *pad = wn->private_data; + PARA_INFO_LOG("closing writer node %p\n", wn); + snd_pcm_drain(pad->handle); + snd_pcm_close(pad->handle); + snd_config_update_free_global(); + free(pad); +} + +__malloc void *alsa_parse_config(char *options) +{ + struct alsa_write_args_info *conf + = para_calloc(sizeof(struct alsa_write_args_info)); + PARA_INFO_LOG("options: %s, %d\n", options, strcspn(options, " \t")); + int ret = alsa_cmdline_parser_string(options, conf, "alsa_write"); + if (ret) + goto err_out; + PARA_INFO_LOG("help given: %d\n", conf->help_given); + return conf; +err_out: + free(conf); + return NULL; +} + +/** the init function of the alsa writer */ +void alsa_write_init(struct writer *w) +{ + w->open = alsa_open; + w->close = alsa_close; + w->pre_select = alsa_write_pre_select; + w->post_select = alsa_write_post_select; + w->parse_config = alsa_parse_config; + w->shutdown = NULL; /* nothing to do */ +} diff --git a/alsa_writer.c b/alsa_writer.c deleted file mode 100644 index 7d7d36bf..00000000 --- a/alsa_writer.c +++ /dev/null @@ -1,238 +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. - */ - -/** \file alsa_writer.c paraslash's alsa output plugin */ - -/* - * 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 "list.h" -#include "sched.h" -#include "write.h" - -#include - -#include "alsa_write.cmdline.h" -#include "error.h" - - -#define FORMAT SND_PCM_FORMAT_S16_LE - -/** data specific to the alsa writer */ -struct private_alsa_data { - /** the alsa handle */ - snd_pcm_t *handle; - /** determined and set by alsa_open() */ - size_t bytes_per_frame; - /** don't write anything until this time */ - struct timeval next_chunk; - /** the return value of snd_pcm_hw_params_get_buffer_time_max() */ - unsigned buffer_time; - unsigned samplerate; - unsigned channels; -}; - -/* - * 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; - int err; - snd_pcm_info_t *info; - snd_pcm_uframes_t period_size; - struct private_alsa_data *pad = para_calloc(sizeof(struct private_alsa_data)); - struct alsa_write_args_info *conf = w->conf; - struct writer_node_group *wng = w->wng; - - if (!conf->samplerate_given && wng->samplerate) - pad->samplerate = *wng->samplerate; - else - pad->samplerate = conf->samplerate_arg; - if (!conf->channels_given && wng->channels) - pad->channels = *wng->channels; - else - pad->channels = conf->channels_arg; - PARA_INFO_LOG("%d channel(s), %dHz\n", pad->channels, pad->samplerate); - w->private_data = pad; - snd_pcm_info_alloca(&info); - err = snd_pcm_open(&pad->handle, conf->device_arg, - SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); - 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, - pad->channels) < 0) - return -E_CHANNEL_COUNT; - if (snd_pcm_hw_params_set_rate_near(pad->handle, hwparams, - &pad->samplerate, 0) < 0) - return -E_SET_RATE; - err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &pad->buffer_time, 0); - if (err < 0 || !pad->buffer_time) - return -E_GET_BUFFER_TIME; - PARA_INFO_LOG("buffer time: %d\n", pad->buffer_time); - if (snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams, - &pad->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_INFO_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) - * pad->channels / 8; - if (snd_pcm_nonblock(pad->handle, 1)) - PARA_ERROR_LOG("%s\n", "failed to set nonblock mode"); - return period_size * pad->bytes_per_frame; -} - -static int alsa_write_pre_select(struct sched *s, struct writer_node *wn) -{ - struct private_alsa_data *pad = wn->private_data; - struct writer_node_group *wng = wn->wng; - struct timeval diff; - - if (*wng->loaded < pad->bytes_per_frame) - return 1; - if (tv_diff(now, &pad->next_chunk, &diff) < 0) { - if (tv_diff(&s->timeout, &diff, NULL) > 0) - s->timeout = diff; - } else { - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 1; - } - return 1; -// PARA_INFO_LOG("timeout: %lu\n", tv2ms(&s->timeout)); -} - -static int alsa_write_post_select(__a_unused struct sched *s, - struct writer_node *wn) -{ - struct private_alsa_data *pad = wn->private_data; - struct writer_node_group *wng = wn->wng; - size_t frames = (*wng->loaded - wn->written) / pad->bytes_per_frame; - snd_pcm_sframes_t ret; - unsigned char *data = (unsigned char*)wng->buf + wn->written; - struct timeval tv; - -// PARA_INFO_LOG("%zd frames\n", frames); - if (!frames) { - if (*wng->input_eof) - wn->written = *wng->loaded; - return 1; - } - if (tv_diff(now, &pad->next_chunk, NULL) < 0) - return 1; - ret = snd_pcm_writei(pad->handle, data, frames); - if (ret == -EPIPE) { - PARA_WARNING_LOG("%s", "EPIPE\n"); - snd_pcm_prepare(pad->handle); - return 1; - } - if (ret < 0) { - PARA_WARNING_LOG("%s", "ALSA ERROR\n"); - return -E_ALSA_WRITE; - } - ms2tv(pad->buffer_time / 4000, &tv); -// ms2tv(1, &tv); - tv_add(now, &tv, &pad->next_chunk); - wn->written += ret * pad->bytes_per_frame; - return 1; -} - -static void alsa_close(struct writer_node *wn) -{ - struct private_alsa_data *pad = wn->private_data; - PARA_INFO_LOG("closing writer node %p\n", wn); - snd_pcm_drain(pad->handle); - snd_pcm_close(pad->handle); - snd_config_update_free_global(); - free(pad); -} - -__malloc void *alsa_parse_config(char *options) -{ - struct alsa_write_args_info *conf - = para_calloc(sizeof(struct alsa_write_args_info)); - PARA_INFO_LOG("options: %s, %d\n", options, strcspn(options, " \t")); - int ret = alsa_cmdline_parser_string(options, conf, "alsa_write"); - if (ret) - goto err_out; - PARA_INFO_LOG("help given: %d\n", conf->help_given); - return conf; -err_out: - free(conf); - return NULL; -} - -/** the init function of the alsa writer */ -void alsa_writer_init(struct writer *w) -{ - w->open = alsa_open; - w->close = alsa_close; - w->pre_select = alsa_write_pre_select; - w->post_select = alsa_write_post_select; - w->parse_config = alsa_parse_config; - w->shutdown = NULL; /* nothing to do */ -} diff --git a/configure.ac b/configure.ac index 721c5c0c..a83d39eb 100644 --- a/configure.ac +++ b/configure.ac @@ -71,7 +71,7 @@ audiod_cmdline_objs="audiod.cmdline grab_client.cmdline compress_filter.cmdline http_recv.cmdline dccp_recv.cmdline file_write.cmdline client.cmdline" audiod_errlist_objs="audiod signal string daemon stat net time grab_client filter_chain wav compress http_recv dccp dccp_recv - recv_common fd sched write_common file_writer audiod_command crypt + recv_common fd sched write_common file_write audiod_command crypt client_common" audiod_ldflags="" @@ -82,7 +82,7 @@ server_errlist_objs="server mp3_afh afs command net string signal random_selecto server_ldflags="" write_cmdline_objs="write.cmdline file_write.cmdline" -write_errlist_objs="write write_common file_writer time fd string sched stdin" +write_errlist_objs="write write_common file_write time fd string sched stdin" write_ldflags="" write_writers="file" @@ -169,11 +169,11 @@ if test ${have_core_audio} = yes; then f3="-framework AudioUnit" f4="-framework CoreServices" f="$f1 $f2 $f3 $f4" - audiod_errlist_objs="$audiod_errlist_objs osx_writer" + audiod_errlist_objs="$audiod_errlist_objs osx_write" audiod_cmdline_objs="$audiod_cmdline_objs osx_write.cmdline" audiod_ldflags="$audiod_ldflags $f" - write_errlist_objs="$write_errlist_objs osx_writer" + write_errlist_objs="$write_errlist_objs osx_write" write_cmdline_objs="$write_cmdline_objs osx_write.cmdline" write_ldflags="$write_ldflags $f" write_writers="$write_writers osx" @@ -448,9 +448,9 @@ 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)" +inits="$(for i in $write_writers; do printf 'extern void '$i'_write_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)" +array="$(for i in $write_writers; do printf '{.init = '$i'_write_init},'; done)" AC_DEFINE_UNQUOTED(WRITER_ARRAY, $array, array of supported writers) gui_cmdline_objs="gui.cmdline" diff --git a/error.h b/error.h index cbfb4083..e1af56a2 100644 --- a/error.h +++ b/error.h @@ -72,8 +72,8 @@ enum para_subsystem { SS_WRITE, SS_WRITE_COMMON, SS_ALSA_WRITER, - SS_FILE_WRITER, - SS_OSX_WRITER, + SS_FILE_WRITE, + SS_OSX_WRITE, NUM_SS }; @@ -94,7 +94,7 @@ enum para_subsystem { extern const char **para_errlist[]; /** \endcond */ -#define OSX_WRITER_ERRORS \ +#define OSX_WRITE_ERRORS \ PARA_ERROR(STREAM_FORMAT, "could not set stream format"), \ PARA_ERROR(ADD_CALLBACK, "can not add callback"), \ PARA_ERROR(READ_STDIN, "failed to read from stdin"), \ @@ -428,7 +428,7 @@ extern const char **para_errlist[]; PARA_ERROR(STOP_THRESHOLD, "snd_pcm_sw_params_set_stop_threshold() failed"), \ -#define FILE_WRITER_ERRORS \ +#define FILE_WRITE_ERRORS \ PARA_ERROR(FW_WRITE, "file writer write error"), \ PARA_ERROR(FW_OPEN, "file writer: can not open output file"), \ PARA_ERROR(FW_NO_FILE, "task started without open file"), \ @@ -565,8 +565,8 @@ SS_ENUM(FD); SS_ENUM(WRITE); SS_ENUM(WRITE_COMMON); SS_ENUM(ALSA_WRITER); -SS_ENUM(FILE_WRITER); -SS_ENUM(OSX_WRITER); +SS_ENUM(FILE_WRITE); +SS_ENUM(OSX_WRITE); SS_ENUM(RINGBUFFER); SS_ENUM(CLIENT); SS_ENUM(CLIENT_COMMON); diff --git a/file_write.c b/file_write.c new file mode 100644 index 00000000..2163b7a9 --- /dev/null +++ b/file_write.c @@ -0,0 +1,127 @@ +/* + * 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. + */ + +/** \file file_write.c simple output plugin for testing purposes */ + +#include "para.h" +#include "list.h" +#include "sched.h" +#include "write.h" +#include "string.h" +#include "fd.h" +#include "file_write.cmdline.h" +#include "error.h" + +/** data specific to the file writer */ +struct private_file_write_data { + /** the file descriptor of the output file */ + int fd; + /** non-zero if \a fd was added to the write fd set */ + int check_fd; +}; + +static int file_write_open(struct writer_node *wn) +{ + struct private_file_write_data *pfwd = para_calloc( + sizeof(struct private_file_write_data)); + struct file_write_args_info *conf = wn->conf; + char *filename; + if (conf->filename_given) + filename = conf->filename_arg; + else { + char *tmp = para_tmpname(), *home = para_homedir(); + filename = make_message("%s/.paraslash/%s", home, tmp); + free(home); + free(tmp); + } + wn->private_data = pfwd; + pfwd->fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (!conf->filename_given) + free(filename); + if (pfwd->fd >= 0) + return 8192; + free(pfwd); + return -E_FW_OPEN; +} + +static int file_write_pre_select(struct sched *s, struct writer_node *wn) +{ + struct private_file_write_data *pfwd = wn->private_data; + struct writer_node_group *wng = wn->wng; + + pfwd->check_fd = 0; + if (pfwd->fd <= 0) + return -E_FW_NO_FILE; + if (!*wng->loaded) + return 1; + para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno); + pfwd->check_fd = 1; + return 1; +} + +static int file_write_post_select(struct sched *s, struct writer_node *wn) +{ + struct private_file_write_data *pfwd = wn->private_data; + struct writer_node_group *wng = wn->wng; + int ret; + + if (!pfwd->check_fd) + return 1; + if (*wng->loaded <= wn->written) + return 1; + if (!FD_ISSET(pfwd->fd, &s->wfds)) + return 1; +// PARA_INFO_LOG("writing %zd\n", *wng->loaded); + ret = write(pfwd->fd, wng->buf + wn->written, + *wng->loaded - wn->written); + if (ret < 0) + return -E_FW_WRITE; + wn->written += ret; + return 1; +} + +static void file_write_close(struct writer_node *wn) +{ + struct private_file_write_data *pfwd = wn->private_data; + close(pfwd->fd); + free(pfwd); +} + +__malloc void *file_write_parse_config(char *options) +{ + PARA_INFO_LOG("options: %s\n", options); + struct file_write_args_info *conf + = para_calloc(sizeof(struct file_write_args_info)); + int ret = file_cmdline_parser_string(options, conf, "file_write"); + PARA_INFO_LOG("conf->filename_given: %d\n", conf->filename_given); + if (!ret) + return conf; + free(conf); + return NULL; +} + +/** the init function of the file writer */ +void file_write_init(struct writer *w) +{ + w->open = file_write_open; + w->pre_select = file_write_pre_select; + w->post_select = file_write_post_select; + w->parse_config = file_write_parse_config; + w->close = file_write_close; + w->shutdown = NULL; /* nothing to do */ +} diff --git a/file_writer.c b/file_writer.c deleted file mode 100644 index e90647d2..00000000 --- a/file_writer.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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. - */ - -/** \file file_writer.c simple output plugin for testing purposes */ - -#include "para.h" -#include "list.h" -#include "sched.h" -#include "write.h" -#include "string.h" -#include "fd.h" -#include "file_write.cmdline.h" -#include "error.h" - -/** data specific to the file writer */ -struct private_file_writer_data { - /** the file descriptor of the output file */ - int fd; - /** non-zero if \a fd was added to the write fd set */ - int check_fd; -}; - -static int file_writer_open(struct writer_node *wn) -{ - struct private_file_writer_data *pfwd = para_calloc( - sizeof(struct private_file_writer_data)); - struct file_write_args_info *conf = wn->conf; - char *filename; - if (conf->filename_given) - filename = conf->filename_arg; - else { - char *tmp = para_tmpname(), *home = para_homedir(); - filename = make_message("%s/.paraslash/%s", home, tmp); - free(home); - free(tmp); - } - wn->private_data = pfwd; - pfwd->fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); - if (!conf->filename_given) - free(filename); - if (pfwd->fd >= 0) - return 8192; - free(pfwd); - return -E_FW_OPEN; -} - -static int file_writer_pre_select(struct sched *s, struct writer_node *wn) -{ - struct private_file_writer_data *pfwd = wn->private_data; - struct writer_node_group *wng = wn->wng; - - pfwd->check_fd = 0; - if (pfwd->fd <= 0) - return -E_FW_NO_FILE; - if (!*wng->loaded) - return 1; - para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno); - pfwd->check_fd = 1; - return 1; -} - -static int file_writer_post_select(struct sched *s, struct writer_node *wn) -{ - struct private_file_writer_data *pfwd = wn->private_data; - struct writer_node_group *wng = wn->wng; - int ret; - - if (!pfwd->check_fd) - return 1; - if (*wng->loaded <= wn->written) - return 1; - if (!FD_ISSET(pfwd->fd, &s->wfds)) - return 1; -// PARA_INFO_LOG("writing %zd\n", *wng->loaded); - ret = write(pfwd->fd, wng->buf + wn->written, - *wng->loaded - wn->written); - if (ret < 0) - return -E_FW_WRITE; - wn->written += ret; - return 1; -} - -static void file_writer_close(struct writer_node *wn) -{ - struct private_file_writer_data *pfwd = wn->private_data; - close(pfwd->fd); - free(pfwd); -} - -__malloc void *file_writer_parse_config(char *options) -{ - PARA_INFO_LOG("options: %s\n", options); - struct file_write_args_info *conf - = para_calloc(sizeof(struct file_write_args_info)); - int ret = file_cmdline_parser_string(options, conf, "file_write"); - PARA_INFO_LOG("conf->filename_given: %d\n", conf->filename_given); - if (!ret) - return conf; - free(conf); - return NULL; -} - -/** the init function of the file writer */ -void file_writer_init(struct writer *w) -{ - w->open = file_writer_open; - w->pre_select = file_writer_pre_select; - w->post_select = file_writer_post_select; - w->parse_config = file_writer_parse_config; - w->close = file_writer_close; - w->shutdown = NULL; /* nothing to do */ -} diff --git a/osx_write.c b/osx_write.c new file mode 100644 index 00000000..7e825f71 --- /dev/null +++ b/osx_write.c @@ -0,0 +1,331 @@ +/* + * 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. + */ + +/** \file osx_write.c paraslash's output plugin for MacOs */ + +/* + * based on mosx-mpg123, by Guillaume Outters and Steven A. Kortze + * + */ + +#include +#include "para.h" +#include "fd.h" +#include "string.h" +#include "list.h" +#include "sched.h" +#include "write.h" +#include "osx_write.cmdline.h" +#include "error.h" + + +#include +#include +#include +struct osx_buffer { + float *buffer; + long size; + float *ptr; /* Where in the buffer are we? */ + long remaining; + struct osx_buffer *next; +}; +typedef struct osx_buffer osx_buffer; + +struct private_osx_write_data { + long size; + short *ptr; + AudioUnit output; + char play; + osx_buffer *from; /* Current buffers */ + osx_buffer *to; + unsigned samplerate; + unsigned channels; +}; + + +/* + * Tried with 3 buffers, but then any little window move is sufficient to + * stop the sound (OK, on a G3 400 with a Public Beta. Perhaps now we can + * go down to 2 buffers). With 16 buffers we have 1.5 seconds music + * buffered (or, if you're pessimistic, 1.5 seconds latency). Note 0 + * buffers don't work much further than the Bus error. + */ +#define NUMBER_BUFFERS 2 + +static void destroy_buffers(struct private_osx_write_data *powd) +{ + osx_buffer *ptr; + osx_buffer *ptr2; + ptr = powd->to->next; + powd->to->next = NULL; + while (ptr) { + ptr2 = ptr->next; + free(ptr->buffer); + free(ptr); + ptr = ptr2; + } +} + +static void init_buffers(struct private_osx_write_data *powd) +{ + int i; + + osx_buffer ** ptrptr; + ptrptr = &powd->to; + for (i = 0; i < NUMBER_BUFFERS; i++) { + *ptrptr = malloc(sizeof(osx_buffer)); + (*ptrptr)->size = 0; + (*ptrptr)->remaining = 0; + (*ptrptr)->buffer = NULL; + ptrptr = &(*ptrptr)->next; + /* This buffer is ready for filling (of course, it is empty!) */ + } + *ptrptr = powd->from = powd->to; +} + +static void fill_buffer(osx_buffer *b, short *source, long size) +{ + float *dest; + + if (b->remaining) /* Non empty buffer, must still be playing */ + return; + PARA_INFO_LOG("%ld\n", size); + if (b->size != size) { + /* + * Hey! What's that? Coudn't this buffer size be fixed + * once (well, perhaps we just didn't allocate it yet) + */ + if (b->buffer) + free(b->buffer); + b->buffer = malloc(size * sizeof(float)); + b->size = size; + } + dest = b->buffer; + while (size--) { + char *tmp = (char *)source; + char c = *tmp; + *tmp = *(tmp + 1); + *(tmp + 1) = c; + /* *dest++ = ((*source++) + 32768) / 65536.0; */ + *dest++ = (*source++) / 32768.0; + } + b->ptr = b->buffer; + b->remaining = b->size; +} + +static OSStatus osx_callback(void * inClientData, + __a_unused AudioUnitRenderActionFlags *inActionFlags, + __a_unused const AudioTimeStamp *inTimeStamp, + __a_unused UInt32 inBusNumber, + __a_unused UInt32 inNumFrames, + AudioBufferList *outOutputData) +{ + long m, n; + float *dest; + int i; + struct private_osx_write_data *powd = inClientData; + +// PARA_INFO_LOG("%p\n", powd); + for (i = 0; i < outOutputData->mNumberBuffers; ++i) { + /* what we have to fill */ + m = outOutputData->mBuffers[i].mDataByteSize / sizeof(float); + dest = outOutputData->mBuffers[i].mData; + while (m > 0) { + if ((n = powd->from->remaining) <= 0) { + PARA_INFO_LOG("%s", "buffer underrun\n"); + /* no more bytes in the current read buffer! */ + while ((n = powd->from->remaining) <= 0) + /* wait for the results */ + usleep(2000); + } + PARA_INFO_LOG("buf %p: n = %ld, m= %ld\n", powd->from->buffer, n, m); + /* + * we dump what we can. In fact, just the necessary + * should be sufficient + */ + if (n > m) + n = m; + memcpy(dest, powd->from->ptr, n * sizeof(float)); + dest += n; + /* remember all done work */ + m -= n; + powd->from->ptr += n; + if ((powd->from->remaining -= n) <= 0) + powd->from = powd->from->next; + } + } + return 0; +} + +static int osx_write_open(struct writer_node *wn) +{ + struct private_osx_write_data *powd = para_calloc( + sizeof(struct private_osx_write_data)); + ComponentDescription desc; + Component comp; + AURenderCallbackStruct inputCallback = {osx_callback, powd}; + AudioStreamBasicDescription format; + int ret; + struct writer_node_group *wng = wn->wng; + struct osx_write_args_info *conf = wn->conf; + + wn->private_data = powd; + /* where did that default audio output go? */ + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + /* NOTE: and if default output isn't Apple? */ + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + ret = -E_DEFAULT_COMP; + comp = FindNextComponent(NULL, &desc); + if (!comp) + goto e0; + ret = -E_OPEN_COMP; + if (OpenAComponent(comp, &powd->output)) + goto e0; + ret = -E_UNIT_INIT; + if (AudioUnitInitialize(powd->output)) + goto e1; + powd->size = 0; + powd->ptr = NULL; + powd->play = 0; + /* Hmmm, let's choose PCM format */ + /* We tell the Output Unit what format we're going to supply data to it. + * This is necessary if you're providing data through an input callback + * AND you want the DefaultOutputUnit to do any format conversions + * necessary from your format to the device's format. + */ + if (!conf->samplerate_given && wng->samplerate) + powd->samplerate = *wng->samplerate; + else + powd->samplerate = conf->samplerate_arg; + format.mSampleRate = powd->samplerate; + /* The specific encoding type of audio stream*/ + format.mFormatID = kAudioFormatLinearPCM; + /* flags specific to each format */ + format.mFormatFlags = kLinearPCMFormatFlagIsFloat + | kLinearPCMFormatFlagIsPacked + | kLinearPCMFormatFlagIsBigEndian; + if (!conf->channels_given && wng->channels) + powd->channels = *wng->channels; + else + powd->channels = conf->channels_arg; + format.mChannelsPerFrame = powd->channels; + format.mFramesPerPacket = 1; + format.mBytesPerPacket = format.mChannelsPerFrame * sizeof(float); + format.mBytesPerFrame = format.mFramesPerPacket * format.mBytesPerPacket; + /* one of the most constant constants of the whole computer history */ + format.mBitsPerChannel = sizeof(float) * 8; + ret = -E_STREAM_FORMAT; + if (AudioUnitSetProperty(powd->output, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, &format, + sizeof(AudioStreamBasicDescription))) + goto e2; + init_buffers(powd); + ret = -E_ADD_CALLBACK; + if (AudioUnitSetProperty(powd->output, kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, &inputCallback, + sizeof(inputCallback)) < 0) + goto e3; + return 1; +e3: + destroy_buffers(powd); +e2: + AudioUnitUninitialize(powd->output); +e1: + CloseComponent(powd->output); +e0: + return ret; +} + +__malloc void *osx_write_parse_config(char *options) +{ + struct osx_write_args_info *conf + = para_calloc(sizeof(struct osx_write_args_info)); + PARA_INFO_LOG("options: %s\n", options); + int ret = osx_cmdline_parser_string(options, conf, "osx_write"); + if (ret) + goto err_out; + return conf; +err_out: + free(conf); + return NULL; + +} + +static void osx_write_close(struct writer_node *wn) +{ + struct private_osx_write_data *powd = wn->private_data; + + PARA_INFO_LOG("closing writer node %p\n", wn); + AudioOutputUnitStop(powd->output); + AudioUnitUninitialize(powd->output); + CloseComponent(powd->output); + destroy_buffers(powd); + free(powd); +} + +static int need_new_buffer(struct writer_node *wn) +{ + struct writer_node_group *wng = wn->wng; + struct private_osx_write_data *powd = wn->private_data; + + if (*wng->loaded < sizeof(short)) + return 0; + if (powd->to->remaining) /* Non empty buffer, must still be playing */ + return 0; + return 1; +} + +static int osx_write_post_select(__a_unused struct sched *s, + struct writer_node *wn) +{ + struct private_osx_write_data *powd = wn->private_data; + struct writer_node_group *wng = wn->wng; + short *data = (short*)wng->buf; + + if (!need_new_buffer(wn)) + return 1; + fill_buffer(powd->to, data, *wng->loaded / sizeof(short)); + powd->to = powd->to->next; + wn->written = *wng->loaded; + if (!powd->play) { + if (AudioOutputUnitStart(powd->output)) + return -E_UNIT_START; + powd->play = 1; + } + return 1; +} + +static int osx_write_pre_select(struct sched *s, __a_unused struct writer_node *wn) +{ + s->timeout.tv_sec = 0; + s->timeout.tv_usec = 20; + return 1; +} + +void osx_write_init(struct writer *w) +{ + w->open = osx_write_open; + w->close = osx_write_close; + w->pre_select = osx_write_pre_select; + w->post_select = osx_write_post_select; + w->parse_config = osx_write_parse_config; + w->shutdown = NULL; /* nothing to do */ +} diff --git a/osx_writer.c b/osx_writer.c deleted file mode 100644 index fab60859..00000000 --- a/osx_writer.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * 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. - */ - -/** \file osx_writer.c paraslash's output plugin for MacOs */ - -/* - * based on mosx-mpg123, by Guillaume Outters and Steven A. Kortze - * - */ - -#include -#include "para.h" -#include "fd.h" -#include "string.h" -#include "list.h" -#include "sched.h" -#include "write.h" -#include "osx_write.cmdline.h" -#include "error.h" - - -#include -#include -#include -struct osx_buffer { - float *buffer; - long size; - float *ptr; /* Where in the buffer are we? */ - long remaining; - struct osx_buffer *next; -}; -typedef struct osx_buffer osx_buffer; - -struct private_osx_writer_data { - long size; - short *ptr; - AudioUnit output; - char play; - osx_buffer *from; /* Current buffers */ - osx_buffer *to; - unsigned samplerate; - unsigned channels; -}; - - -/* - * Tried with 3 buffers, but then any little window move is sufficient to - * stop the sound (OK, on a G3 400 with a Public Beta. Perhaps now we can - * go down to 2 buffers). With 16 buffers we have 1.5 seconds music - * buffered (or, if you're pessimistic, 1.5 seconds latency). Note 0 - * buffers don't work much further than the Bus error. - */ -#define NUMBER_BUFFERS 2 - -static void destroy_buffers(struct private_osx_writer_data *powd) -{ - osx_buffer *ptr; - osx_buffer *ptr2; - ptr = powd->to->next; - powd->to->next = NULL; - while (ptr) { - ptr2 = ptr->next; - free(ptr->buffer); - free(ptr); - ptr = ptr2; - } -} - -static void init_buffers(struct private_osx_writer_data *powd) -{ - int i; - - osx_buffer ** ptrptr; - ptrptr = &powd->to; - for (i = 0; i < NUMBER_BUFFERS; i++) { - *ptrptr = malloc(sizeof(osx_buffer)); - (*ptrptr)->size = 0; - (*ptrptr)->remaining = 0; - (*ptrptr)->buffer = NULL; - ptrptr = &(*ptrptr)->next; - /* This buffer is ready for filling (of course, it is empty!) */ - } - *ptrptr = powd->from = powd->to; -} - -static void fill_buffer(osx_buffer *b, short *source, long size) -{ - float *dest; - - if (b->remaining) /* Non empty buffer, must still be playing */ - return; - PARA_INFO_LOG("%ld\n", size); - if (b->size != size) { - /* - * Hey! What's that? Coudn't this buffer size be fixed - * once (well, perhaps we just didn't allocate it yet) - */ - if (b->buffer) - free(b->buffer); - b->buffer = malloc(size * sizeof(float)); - b->size = size; - } - dest = b->buffer; - while (size--) { - char *tmp = (char *)source; - char c = *tmp; - *tmp = *(tmp + 1); - *(tmp + 1) = c; - /* *dest++ = ((*source++) + 32768) / 65536.0; */ - *dest++ = (*source++) / 32768.0; - } - b->ptr = b->buffer; - b->remaining = b->size; -} - -static OSStatus osx_callback(void * inClientData, - __a_unused AudioUnitRenderActionFlags *inActionFlags, - __a_unused const AudioTimeStamp *inTimeStamp, - __a_unused UInt32 inBusNumber, - __a_unused UInt32 inNumFrames, - AudioBufferList *outOutputData) -{ - long m, n; - float *dest; - int i; - struct private_osx_writer_data *powd = inClientData; - -// PARA_INFO_LOG("%p\n", powd); - for (i = 0; i < outOutputData->mNumberBuffers; ++i) { - /* what we have to fill */ - m = outOutputData->mBuffers[i].mDataByteSize / sizeof(float); - dest = outOutputData->mBuffers[i].mData; - while (m > 0) { - if ((n = powd->from->remaining) <= 0) { - PARA_INFO_LOG("%s", "buffer underrun\n"); - /* no more bytes in the current read buffer! */ - while ((n = powd->from->remaining) <= 0) - /* wait for the results */ - usleep(2000); - } - PARA_INFO_LOG("buf %p: n = %ld, m= %ld\n", powd->from->buffer, n, m); - /* - * we dump what we can. In fact, just the necessary - * should be sufficient - */ - if (n > m) - n = m; - memcpy(dest, powd->from->ptr, n * sizeof(float)); - dest += n; - /* remember all done work */ - m -= n; - powd->from->ptr += n; - if ((powd->from->remaining -= n) <= 0) - powd->from = powd->from->next; - } - } - return 0; -} - -static int osx_writer_open(struct writer_node *wn) -{ - struct private_osx_writer_data *powd = para_calloc( - sizeof(struct private_osx_writer_data)); - ComponentDescription desc; - Component comp; - AURenderCallbackStruct inputCallback = {osx_callback, powd}; - AudioStreamBasicDescription format; - int ret; - struct writer_node_group *wng = wn->wng; - struct osx_write_args_info *conf = wn->conf; - - wn->private_data = powd; - /* where did that default audio output go? */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_DefaultOutput; - /* NOTE: and if default output isn't Apple? */ - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - ret = -E_DEFAULT_COMP; - comp = FindNextComponent(NULL, &desc); - if (!comp) - goto e0; - ret = -E_OPEN_COMP; - if (OpenAComponent(comp, &powd->output)) - goto e0; - ret = -E_UNIT_INIT; - if (AudioUnitInitialize(powd->output)) - goto e1; - powd->size = 0; - powd->ptr = NULL; - powd->play = 0; - /* Hmmm, let's choose PCM format */ - /* We tell the Output Unit what format we're going to supply data to it. - * This is necessary if you're providing data through an input callback - * AND you want the DefaultOutputUnit to do any format conversions - * necessary from your format to the device's format. - */ - if (!conf->samplerate_given && wng->samplerate) - powd->samplerate = *wng->samplerate; - else - powd->samplerate = conf->samplerate_arg; - format.mSampleRate = powd->samplerate; - /* The specific encoding type of audio stream*/ - format.mFormatID = kAudioFormatLinearPCM; - /* flags specific to each format */ - format.mFormatFlags = kLinearPCMFormatFlagIsFloat - | kLinearPCMFormatFlagIsPacked - | kLinearPCMFormatFlagIsBigEndian; - if (!conf->channels_given && wng->channels) - powd->channels = *wng->channels; - else - powd->channels = conf->channels_arg; - format.mChannelsPerFrame = powd->channels; - format.mFramesPerPacket = 1; - format.mBytesPerPacket = format.mChannelsPerFrame * sizeof(float); - format.mBytesPerFrame = format.mFramesPerPacket * format.mBytesPerPacket; - /* one of the most constant constants of the whole computer history */ - format.mBitsPerChannel = sizeof(float) * 8; - ret = -E_STREAM_FORMAT; - if (AudioUnitSetProperty(powd->output, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &format, - sizeof(AudioStreamBasicDescription))) - goto e2; - init_buffers(powd); - ret = -E_ADD_CALLBACK; - if (AudioUnitSetProperty(powd->output, kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, &inputCallback, - sizeof(inputCallback)) < 0) - goto e3; - return 1; -e3: - destroy_buffers(powd); -e2: - AudioUnitUninitialize(powd->output); -e1: - CloseComponent(powd->output); -e0: - return ret; -} - -__malloc void *osx_write_parse_config(char *options) -{ - struct osx_write_args_info *conf - = para_calloc(sizeof(struct osx_write_args_info)); - PARA_INFO_LOG("options: %s\n", options); - int ret = osx_cmdline_parser_string(options, conf, "osx_write"); - if (ret) - goto err_out; - return conf; -err_out: - free(conf); - return NULL; - -} - -static void osx_writer_close(struct writer_node *wn) -{ - struct private_osx_writer_data *powd = wn->private_data; - - PARA_INFO_LOG("closing writer node %p\n", wn); - AudioOutputUnitStop(powd->output); - AudioUnitUninitialize(powd->output); - CloseComponent(powd->output); - destroy_buffers(powd); - free(powd); -} - -static int need_new_buffer(struct writer_node *wn) -{ - struct writer_node_group *wng = wn->wng; - struct private_osx_writer_data *powd = wn->private_data; - - if (*wng->loaded < sizeof(short)) - return 0; - if (powd->to->remaining) /* Non empty buffer, must still be playing */ - return 0; - return 1; -} - -static int osx_write_post_select(__a_unused struct sched *s, - struct writer_node *wn) -{ - struct private_osx_writer_data *powd = wn->private_data; - struct writer_node_group *wng = wn->wng; - short *data = (short*)wng->buf; - - if (!need_new_buffer(wn)) - return 1; - fill_buffer(powd->to, data, *wng->loaded / sizeof(short)); - powd->to = powd->to->next; - wn->written = *wng->loaded; - if (!powd->play) { - if (AudioOutputUnitStart(powd->output)) - return -E_UNIT_START; - powd->play = 1; - } - return 1; -} - -static int osx_write_pre_select(struct sched *s, __a_unused struct writer_node *wn) -{ - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 20; - return 1; -} - -void osx_writer_init(struct writer *w) -{ - w->open = osx_writer_open; - w->close = osx_writer_close; - w->pre_select = osx_write_pre_select; - w->post_select = osx_write_post_select; - w->parse_config = osx_write_parse_config; - w->shutdown = NULL; /* nothing to do */ -}