rename xxx_writer.c to xxx_write.c
authorAndre Noll <maan@congo.fml.local>
Sun, 20 Aug 2006 16:52:34 +0000 (18:52 +0200)
committerAndre Noll <maan@congo.fml.local>
Sun, 20 Aug 2006 16:52:34 +0000 (18:52 +0200)
Just to be consistent..

README
alsa_write.c [new file with mode: 0644]
alsa_writer.c [deleted file]
configure.ac
error.h
file_write.c [new file with mode: 0644]
file_writer.c [deleted file]
osx_write.c [new file with mode: 0644]
osx_writer.c [deleted file]

diff --git a/README b/README
index a1e8adc..02d647a 100644 (file)
--- 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 (file)
index 0000000..c104a5d
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2005-2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     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 <perex@suse.cz>, 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 <alsa/asoundlib.h>
+
+#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 (file)
index 7d7d36b..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2005-2006 Andre Noll <maan@systemlinux.org>
- *
- *     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 <perex@suse.cz>, 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 <alsa/asoundlib.h>
-
-#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 */
-}
index 721c5c0..a83d39e 100644 (file)
@@ -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 cbfb408..e1af56a 100644 (file)
--- 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 (file)
index 0000000..2163b7a
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     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 (file)
index e90647d..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
- *
- *     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 (file)
index 0000000..7e825f7
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     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
+ * <skortze@sourceforge.net>
+ */
+
+#include <CoreAudio/CoreAudio.h>
+#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 <CoreAudio/CoreAudio.h>
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/DefaultAudioOutput.h>
+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 (file)
index fab6085..0000000
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
- *
- *     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
- * <skortze@sourceforge.net>
- */
-
-#include <CoreAudio/CoreAudio.h>
-#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 <CoreAudio/CoreAudio.h>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/DefaultAudioOutput.h>
-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 */
-}