Merge branch 't/bits_per_sample'
authorAndre Noll <maan@systemlinux.org>
Thu, 29 Jul 2010 18:52:06 +0000 (20:52 +0200)
committerAndre Noll <maan@systemlinux.org>
Thu, 29 Jul 2010 18:52:06 +0000 (20:52 +0200)
18 files changed:
aacdec_filter.c
alsa_write.c
filter.h
filter_common.c
ggo/alsa_write.m4
ggo/oss_write.ggo
ggo/osx_write.ggo
ggo/write.m4
mp3dec_filter.c
oggdec_filter.c
oss_write.c
osx_write.c
para.h
wav_filter.c
wmadec_filter.c
write.c
write_common.c
write_common.h

index 5b84d07e12c4757c4393e0d5da54a3bcbed499a9..1127fc1132b90d56169c34d732c9343e47e8fa43 100644 (file)
@@ -52,7 +52,7 @@ struct private_aacdec_data {
        /** The number of channels of the current stream. */
        unsigned int channels;
        /** Current sample rate in Hz. */
-       unsigned int samplerate;
+       unsigned int sample_rate;
 };
 
 static int aacdec_execute(struct btr_node *btrn, const char *cmd, char **result)
@@ -60,19 +60,7 @@ static int aacdec_execute(struct btr_node *btrn, const char *cmd, char **result)
        struct filter_node *fn = btr_context(btrn);
        struct private_aacdec_data *padd = fn->private_data;
 
-       if (!strcmp(cmd, "samplerate")) {
-               if (padd->samplerate == 0)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%u", padd->samplerate);
-               return 1;
-       }
-       if (!strcmp(cmd, "channels")) {
-               if (padd->channels == 0)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%u", padd->channels);
-               return 1;
-       }
-       return -ERRNO_TO_PARA_ERROR(ENOTSUP);
+       return decoder_execute(cmd, padd->sample_rate, padd->channels, result);
 }
 
 static void aacdec_open(struct filter_node *fn)
@@ -140,10 +128,10 @@ next_buffer:
                                        &channels) < 0)
                                goto out;
                }
-               padd->samplerate = rate;
+               padd->sample_rate = rate;
                padd->channels = channels;
                PARA_INFO_LOG("rate: %u, channels: %d\n",
-                       padd->samplerate, padd->channels);
+                       padd->sample_rate, padd->channels);
                padd->initialized = 1;
        }
        if (padd->decoder_length > 0) {
index 559dab25da2a55fd3f26bfd015582af70e393701..7f8aae01c22a6b9fcc2a48712c490b6bde73b40e 100644 (file)
@@ -31,9 +31,6 @@
 #include "alsa_write.cmdline.h"
 #include "error.h"
 
-/** always use 16 bit little endian */
-#define FORMAT SND_PCM_FORMAT_S16_LE
-
 /** Data specific to the alsa writer. */
 struct private_alsa_write_data {
        /** The alsa handle */
@@ -45,10 +42,12 @@ struct private_alsa_write_data {
        /* Number of frames that fit into the buffer. */
        snd_pcm_uframes_t buffer_frames;
        /**
-        * The samplerate given by command line option or the decoder
+        * The sample rate given by command line option or the decoder
         * of the writer node group.
         */
-       unsigned samplerate;
+       unsigned sample_rate;
+
+       snd_pcm_format_t sample_format;
        /**
         * The number of channels, given by command line option or the
         * decoder of the writer node group.
@@ -57,6 +56,19 @@ struct private_alsa_write_data {
        struct timeval drain_barrier;
 };
 
+static snd_pcm_format_t get_alsa_pcm_format(enum sample_format sf)
+{
+       switch (sf) {
+       case SF_S8: return SND_PCM_FORMAT_S8;
+       case SF_U8: return SND_PCM_FORMAT_U8;
+       case SF_S16_LE: return SND_PCM_FORMAT_S16_LE;
+       case SF_S16_BE: return SND_PCM_FORMAT_S16_BE;
+       case SF_U16_LE: return SND_PCM_FORMAT_U16_LE;
+       case SF_U16_BE: return SND_PCM_FORMAT_U16_BE;
+       default: return SND_PCM_FORMAT_S16_LE;
+       }
+}
+
 /* Install PCM software and hardware configuration. */
 static int alsa_init(struct private_alsa_write_data *pad,
                struct alsa_write_args_info *conf)
@@ -80,13 +92,14 @@ static int alsa_init(struct private_alsa_write_data *pad,
        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)
+       if (snd_pcm_hw_params_set_format(pad->handle, hwparams,
+                       pad->sample_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, NULL) < 0)
+                       &pad->sample_rate, NULL) < 0)
                return -E_SET_RATE;
        err = snd_pcm_hw_params_get_buffer_time_max(hwparams,
                &pad->buffer_time, NULL);
@@ -110,7 +123,7 @@ static int alsa_init(struct private_alsa_write_data *pad,
                start_threshold = 1;
        else
                start_threshold = PARA_MIN(pad->buffer_frames,
-                       (snd_pcm_uframes_t)pad->samplerate);
+                       (snd_pcm_uframes_t)pad->sample_rate);
        if (snd_pcm_sw_params_set_start_threshold(pad->handle, swparams,
                        start_threshold) < 0)
                return -E_START_THRESHOLD;
@@ -120,7 +133,7 @@ static int alsa_init(struct private_alsa_write_data *pad,
                return -E_STOP_THRESHOLD;
        if (snd_pcm_sw_params(pad->handle, swparams) < 0)
                PARA_WARNING_LOG("unable to install sw params\n");
-       pad->bytes_per_frame = snd_pcm_format_physical_width(FORMAT)
+       pad->bytes_per_frame = snd_pcm_format_physical_width(pad->sample_format)
                * pad->channels / 8;
        if (pad->bytes_per_frame <= 0)
                return -E_PHYSICAL_WIDTH;
@@ -228,23 +241,19 @@ again:
                return;
        }
        if (!pad->handle) {
-               struct alsa_write_args_info *conf = wn->conf;
+               int32_t val;
+
                if (bytes == 0) /* no data available */
                        return;
-               /* defaults */
-               pad->samplerate = conf->samplerate_arg;
-               pad->channels = conf->channels_arg;
-               if (!conf->samplerate_given) { /* config option trumps btr_exec */
-                       int32_t rate;
-                       if (get_btr_samplerate(btrn, &rate) >= 0)
-                               pad->samplerate = rate;
-               }
-               if (!conf->channels_given) {
-                       int32_t ch;
-                       if (get_btr_channels(btrn, &ch) >= 0)
-                               pad->channels = ch;
-               }
-               PARA_INFO_LOG("%d channel(s), %dHz\n", pad->channels, pad->samplerate);
+               get_btr_sample_rate(btrn, &val);
+               pad->sample_rate = val;
+               get_btr_channels(btrn, &val);
+               pad->channels = val;
+               get_btr_sample_format(btrn, &val);
+               pad->sample_format = get_alsa_pcm_format(val);
+
+               PARA_INFO_LOG("%d channel(s), %dHz\n", pad->channels,
+                       pad->sample_rate);
                ret = alsa_init(pad, wn->conf);
                if (ret < 0)
                        goto err;
index c34acca8e96063804fd0d4cf788ac27658ca94b7..692020c40ca80fcd7dd008d59095d7df469d9399 100644 (file)
--- a/filter.h
+++ b/filter.h
@@ -120,6 +120,8 @@ void filter_init(void);
 int check_filter_arg(char *filter_arg, void **conf);
 void print_filter_helps(int detailed);
 void generic_filter_pre_select(struct sched *s, struct task *t);
+int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
+               char **result);
 
 static inline void write_int16_host_endian(char *buf, int val)
 {
index 06a1f52bf15012c556b7b3443e8a5e51b5ac4f16..cdd9f39855c8add2f5c4f4bdfdd88c65bbe72651 100644 (file)
@@ -136,3 +136,43 @@ void generic_filter_pre_select(struct sched *s, struct task *t)
        if (btr_node_status(fn->btrn, fn->min_iqs, BTR_NT_INTERNAL) != 0)
                sched_min_delay(s);
 }
+
+#ifdef WORDS_BIGENDIAN
+#define DECODER_SAMPLE_FORMAT SF_S16_BE
+#else
+#define DECODER_SAMPLE_FORMAT SF_S16_LE
+#endif
+
+/**
+ * Execute a btr command for a decoder.
+ *
+ * The buffer tree nodes of the writers ask the parent nodes about sample_rate,
+ * channels count and sample format. This function is called by all decoders to
+ * answer these queries.
+ *
+ * \param cmd The command to be executed by the child node.
+ * \param sample_rate Known to the decoder.
+ * \param channels Known to the decoder.
+ * \param result Ascii representation on the answer is stored here.
+ */
+int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
+               char **result)
+{
+       if (!strcmp(cmd, "sample_rate")) {
+               if (sample_rate == 0)
+                       return -E_BTR_NAVAIL;
+               *result = make_message("%u", sample_rate);
+               return 1;
+       }
+       if (!strcmp(cmd, "channels")) {
+               if (channels == 0)
+                       return -E_BTR_NAVAIL;
+               *result = make_message("%u", channels);
+               return 1;
+       }
+       if (!strcmp(cmd, "sample_format")) {
+               *result = make_message("%u", DECODER_SAMPLE_FORMAT);
+               return 1;
+       }
+       return -ERRNO_TO_PARA_ERROR(ENOTSUP);
+}
index afcefccfacbc3af3d6ccb077db9d81d2aa6151a5..d0cd4071cdcf44346c982976f324547cabcafe19 100644 (file)
@@ -11,28 +11,4 @@ details="
        On systems with dmix, a better choice than the default
        value might be to use \"plug:swmix\".
 "
-
-option "channels" c
-#~~~~~~~~~~~~~~~~~~
-"specify number of channels"
-int typestr="num"
-default="2"
-optional
-details="
-       This option is only necessary for playing raw audio with
-       para_write.  In all other cases (plaing wav files with
-       para_write or using this writer with para_audiod), the number
-       of channels will be obtained from other resources.
-"
-
-option "samplerate" s
-#~~~~~~~~~~~~~~~~~~~~~
-"force given sample rate"
-int typestr="num"
-default="44100"
-optional
-details="
-       Again, it is only necessary to specify this when playing raw
-       audio with para_write.
-"
 </qu>
index 6068218679fe74e3ca05f715e2e0ef602da9f148..351561cd00c5daf9f8a00722a42a793d5bf49b6e 100644 (file)
@@ -4,27 +4,4 @@ option "device" d
 string typestr="device"
 default="/dev/dsp"
 optional
-
-option "channels" c
-#~~~~~~~~~~~~~~~~~~
-"specify number of channels"
-int typestr="num"
-default="2"
-optional
-details="
-       This option is only necessary for playing raw audio with
-       para_write.  In all other cases (plaing wav files with
-       para_write or using this writer with para_audiod), the number
-       of channels will be obtained from other resources.
-"
-
-option "samplerate" s
-#~~~~~~~~~~~~~~~~~~~~~
-"force given sample rate"
-int typestr="num"
-default="44100"
-optional
-details="
-       Again, it is only necessary to specify this when playing raw
-       audio with para_write.
-"
+details = ""
index 33c495abf90fc2777b4171f224e5b9725c81b32d..bee16d9e629c16acb8414407fefa8af2c1b7a992 100644 (file)
@@ -1,28 +1,6 @@
 section "osx options"
 #####################
 
-option "channels" c
-#~~~~~~~~~~~~~~~~~~
-"channel count"
-
-details = "
-       Specify the number of channels. This is only neccessary for raw audio.
-"
-
-       int typestr="num"
-       default="2"
-       optional
-
-option "samplerate" s
-#~~~~~~~~~~~~~~~~~~~~~
-
-"force given sample rate (only neccessary for
-raw audio)"
-
-       int typestr="num"
-       default="44100"
-       optional
-
 option "numbuffers" n
 #~~~~~~~~~~~~~~~~~~~~~
 
@@ -32,3 +10,4 @@ you get buffer underruns)"
        int typestr="num"
        default="20"
        optional
+       details = ""
index d954316a40d1fe62ad9846b1dd803a83fb4cf2c0..524d72484a7757000a320934e56b2d1ff1ce49d9 100644 (file)
@@ -12,3 +12,32 @@ details="
        May be give multiple times. The same writer may be specified
        more than once.
 "
+
+text "
+       The following options are only necessary for raw audio. When
+       playing wav files this information is obtained from the
+       wave header.
+"
+
+option "channels" c
+#~~~~~~~~~~~~~~~~~~
+"specify number of channels"
+int typestr = "num"
+default = "2"
+optional
+
+option "sample-rate" s
+#~~~~~~~~~~~~~~~~~~~~~
+"force given sample rate"
+int typestr = "num"
+default = "44100"
+optional
+
+option "sample-format" f
+#~~~~~~~~~~~~~~~~~~~~~~~
+"specify sample format"
+# This must match the enum sample_format of para.h
+values = "S8", "U8", "S16_LE", "S16_BE", "U16_LE", "U16_BE" enum
+default = "S16_LE"
+optional
+
index 35448da7e22f67c84e2fb08d8f1ba6b15de2d49f..c2c15a3aeacd1e2b9b6d613efacd99345f30eb61 100644 (file)
@@ -50,7 +50,7 @@ struct private_mp3dec_data {
        /** The number of channels of the current stream. */
        unsigned int channels;
        /** Current sample rate in Hz. */
-       unsigned int samplerate;
+       unsigned int sample_rate;
 };
 
 static int need_bad_data_delay(struct private_mp3dec_data *pmd,
@@ -165,7 +165,7 @@ next_frame:
                goto next_buffer;
        }
        fn->min_iqs = 0;
-       pmd->samplerate = pmd->frame.header.samplerate;
+       pmd->sample_rate = pmd->frame.header.samplerate;
        pmd->channels = MAD_NCHANNELS(&pmd->frame.header);
        ret = mad_frame_decode(&pmd->frame, &pmd->stream);
        if (ret != 0) {
@@ -236,19 +236,7 @@ static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result)
        struct filter_node *fn = btr_context(btrn);
        struct private_mp3dec_data *pmd = fn->private_data;
 
-       if (!strcmp(cmd, "samplerate")) {
-               if (pmd->samplerate == 0)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%u", pmd->samplerate);
-               return 1;
-       }
-       if (!strcmp(cmd, "channels")) {
-               if (pmd->channels == 0)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%u", pmd->channels);
-               return 1;
-       }
-       return -ERRNO_TO_PARA_ERROR(ENOTSUP);
+       return decoder_execute(cmd, pmd->sample_rate, pmd->channels, result);
 }
 
 static void mp3dec_free_config(void *conf)
index 541b0b236452f1c52fc53d0d0476c9207adf0cfa..68b4db160662b5952136b015b33b17d8419f4907 100644 (file)
@@ -35,8 +35,8 @@ struct private_oggdec_data {
        /** The number of channels of the current stream. */
        unsigned int channels;
        /** Current sample rate in Hz. */
-       unsigned int samplerate;
        struct timeval stream_start;
+       unsigned int sample_rate;
 };
 
 static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
@@ -121,19 +121,7 @@ static int oggdec_execute(struct btr_node *btrn, const char *cmd, char **result)
        struct filter_node *fn = btr_context(btrn);
        struct private_oggdec_data *pod = fn->private_data;
 
-       if (!strcmp(cmd, "samplerate")) {
-               if (pod->samplerate == 0)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%u", pod->samplerate);
-               return 1;
-       }
-       if (!strcmp(cmd, "channels")) {
-               if (pod->channels == 0)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%u", pod->channels);
-               return 1;
-       }
-       return -ERRNO_TO_PARA_ERROR(ENOTSUP);
+       return decoder_execute(cmd, pod->sample_rate, pod->channels, result);
 }
 
 static int ogg_init(struct filter_node *fn)
@@ -178,10 +166,10 @@ open:
        if (oret < 0)
                goto out;
        pod->channels = ov_info(pod->vf, 0)->channels;
-       pod->samplerate = ov_info(pod->vf, 0)->rate;
+       pod->sample_rate = ov_info(pod->vf, 0)->rate;
        tv_add(now, &(struct timeval)EMBRACE(0, 300 * 1000), &pod->stream_start);
        PARA_NOTICE_LOG("%d channels, %d Hz\n", pod->channels,
-               pod->samplerate);
+               pod->sample_rate);
        ret = 1;
 out:
        if (ret <= 0) {
index 6525afa48fca41a9f42d01f36d36315f054c3cf5..b82b3968baf214f3889cca805306438073e21ac6 100644 (file)
@@ -25,9 +25,6 @@
 #include "oss_write.cmdline.h"
 #include "error.h"
 
-/** Always use 16 bit little endian. */
-#define FORMAT AFMT_S16_LE
-
 /** Data specific to the oss writer. */
 struct private_oss_write_data {
        /** The file handle of the device. */
@@ -36,6 +33,19 @@ struct private_oss_write_data {
        int bytes_per_frame;
 };
 
+static int get_oss_format(enum sample_format sf)
+{
+       switch (sf) {
+       case SF_S8: return AFMT_S8;
+       case SF_U8: return AFMT_U8;
+       case SF_S16_LE: return AFMT_S16_LE;
+       case SF_S16_BE: return AFMT_S16_BE;
+       case SF_U16_LE: return AFMT_U16_LE;
+       case SF_U16_BE: return AFMT_U16_BE;
+       default: return AFMT_S16_LE;
+       }
+}
+
 static void oss_pre_select(struct sched *s, struct task *t)
 {
        struct writer_node *wn = container_of(t, struct writer_node, task);
@@ -69,9 +79,10 @@ static void oss_close(struct writer_node *wn)
  * incorrectly believe that the device is still in 44.1 kHz mode when actually
  * the speed is decreased to 22.05 kHz.
  */
-static int oss_init(struct writer_node *wn, unsigned samplerate, unsigned channels)
+static int oss_init(struct writer_node *wn, unsigned sample_rate,
+               unsigned channels, int sample_format)
 {
-       int ret, format = FORMAT;
+       int ret, format;
        unsigned ch, rate;
        struct oss_write_args_info *conf = wn->conf;
        struct private_oss_write_data *powd = wn->private_data;
@@ -85,18 +96,16 @@ static int oss_init(struct writer_node *wn, unsigned samplerate, unsigned channe
        if (ret < 0)
                goto err;
        /* set PCM format */
+       sample_format = format = get_oss_format(sample_format);
        ret = ioctl(powd->fd, SNDCTL_DSP_SETFMT, &format);
        if (ret < 0) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
                goto err;
        }
        ret = -E_BAD_SAMPLE_FORMAT;
-       if (format != FORMAT)
+       if (format != sample_format)
                goto err;
        /* set number of channels */
-       ret = -E_BAD_CHANNEL_COUNT;
-       if (channels == 0)
-               goto err;
        ch = channels;
        ret = ioctl(powd->fd, SNDCTL_DSP_CHANNELS, &ch);
        if (ret < 0) {
@@ -106,7 +115,10 @@ static int oss_init(struct writer_node *wn, unsigned samplerate, unsigned channe
        ret = -E_BAD_CHANNEL_COUNT;
        if (ch != channels)
                goto err;
-       powd->bytes_per_frame = ch * 2;
+       if (format == SF_U8 || format == SF_S8)
+               powd->bytes_per_frame = ch;
+       else
+               powd->bytes_per_frame = ch * 2;
 
        /*
         * Set sampling rate
@@ -115,15 +127,15 @@ static int oss_init(struct writer_node *wn, unsigned samplerate, unsigned channe
         * device, the the highest possible speed is automatically used. The
         * value actually used is returned as the new value of the argument.
         */
-       rate = samplerate;
+       rate = sample_rate;
        ret = ioctl(powd->fd, SNDCTL_DSP_SPEED, &rate);
        if (ret < 0) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
                goto err;
        }
-       if (rate != samplerate) {
-               unsigned min = PARA_MIN(rate, samplerate),
-                       max = PARA_MAX(rate, samplerate);
+       if (rate != sample_rate) {
+               unsigned min = PARA_MIN(rate, sample_rate),
+                       max = PARA_MAX(rate, sample_rate);
                /*
                 * Check whether the returned sample rate differs significantly
                 * from the requested one.
@@ -132,7 +144,7 @@ static int oss_init(struct writer_node *wn, unsigned samplerate, unsigned channe
                if (100 * max > 110 * min) /* more than 10% deviation */
                        goto err;
                PARA_NOTICE_LOG("using %dHz rather than %dHz\n", rate,
-                       samplerate);
+                       sample_rate);
        }
        wn->min_iqs = powd->bytes_per_frame;
        return 1;
@@ -146,7 +158,6 @@ static void oss_post_select(__a_unused struct sched *s,
                struct task *t)
 {
        struct writer_node *wn = container_of(t, struct writer_node, task);
-       struct oss_write_args_info *conf = wn->conf;
        struct private_oss_write_data *powd = wn->private_data;
        struct btr_node *btrn = wn->btrn;
        size_t frames, bytes;
@@ -158,18 +169,11 @@ static void oss_post_select(__a_unused struct sched *s,
        if (ret == 0)
                return;
        if (powd->fd < 0) {
-               int32_t rate, ch;
-               ret = -1;
-               if (!conf->samplerate_given) /* config option trumps btr_exec */
-                       ret = get_btr_samplerate(wn->btrn, &rate);
-               if (ret < 0)
-                       rate = conf->samplerate_arg;
-               ret = -1;
-               if (!conf->channels_given)
-                       ret = get_btr_channels(wn->btrn, &ch);
-               if (ret < 0)
-                       ch = conf->channels_arg;
-               ret = oss_init(wn, rate, ch);
+               int32_t rate, ch, format;
+               get_btr_sample_rate(btrn, &rate);
+               get_btr_channels(btrn, &ch);
+               get_btr_sample_format(btrn, &format);
+               ret = oss_init(wn, rate, ch, format);
                if (ret < 0)
                        goto out;
                return;
index bcff09f781bc3c1e703a4cc4bcce91f5c8ff5628..cfd02e7453b1ccac04eb951e5f42009801806b55 100644 (file)
@@ -57,7 +57,9 @@ struct private_osx_write_data {
        /** the post_select writes audio data here */
        struct osx_buffer *to;
        /** sample rate of the current audio stream */
-       unsigned samplerate;
+       unsigned sample_rate;
+       /** Sample format of the current audio stream */
+       unsigned sample_format;
        /** number of channels of the current audio stream */
        unsigned channels;
 };
@@ -94,18 +96,41 @@ static void init_buffers(struct writer_node *wn)
        *ptrptr = powd->from = powd->to;
 }
 
-static void fill_buffer(struct osx_buffer *b, short *source, long size)
+static void fill_buffer(struct private_osx_write_data *powd, char *data, long bytes)
 {
+       struct osx_buffer *b = powd->to;
        float *dest;
+       long samples;
+       enum sample_format sf = powd->sample_format;
 
-       assert(b->remaining == 0 || size > 0);
-       if (b->size != size) {
-               b->buffer = para_realloc(b->buffer, size * sizeof(float));
-               b->size = size;
+       samples = (sf == SF_S8 || sf == SF_U8)? bytes : bytes / 2;
+       assert(b->remaining == 0 || samples > 0);
+       if (b->size != samples) {
+               b->buffer = para_realloc(b->buffer, samples * sizeof(float));
+               b->size = samples;
        }
        dest = b->buffer;
-       while (size--)
-               *dest++ = (*source++) / 32768.0;
+       switch (powd->sample_format) {
+       case SF_U8: {
+               uint8_t *src = (uint8_t *)data;
+               while (samples--) {
+                       *dest++ = (*src++) / 256.0;
+               }
+               break;
+       }
+       case SF_S8: {
+               int8_t *src = (int8_t *)data;
+               while (samples--) {
+                       *dest++ = ((*src++) + 128) / 256.0;
+               }
+               break;
+       }
+       default: {
+               short *src = (short *)data;
+               while (samples--)
+                       *dest++ = (*src++) / 32768.0;
+       }
+       }
        b->ptr = b->buffer;
        b->remaining = b->size;
 }
@@ -164,17 +189,24 @@ static OSStatus osx_callback(void * inClientData,
 
 static int osx_write_open(struct writer_node *wn)
 {
-       struct private_osx_write_data *powd = para_calloc(
-               sizeof(struct private_osx_write_data));
+       struct private_osx_write_data *powd = para_calloc(sizeof(*powd));
+
+       wn->private_data = powd;
+       init_buffers(wn);
+       return 0;
+}
+
+static int core_audio_init(struct writer_node *wn)
+{
+       struct private_osx_write_data *powd = wn->private_data;
        ComponentDescription desc;
        Component comp;
        AURenderCallbackStruct inputCallback = {osx_callback, powd};
        AudioStreamBasicDescription format;
        int ret;
        struct btr_node *btrn = wn->btrn;
-       struct osx_write_args_info *conf = wn->conf;
+       int32_t val;
 
-       wn->private_data = powd;
        /* where did that default audio output go? */
        desc.componentType = kAudioUnitType_Output;
        desc.componentSubType = kAudioUnitSubType_DefaultOutput;
@@ -193,18 +225,12 @@ static int osx_write_open(struct writer_node *wn)
        if (AudioUnitInitialize(powd->audio_unit))
                goto e1;
        powd->play = 0;
-       powd->samplerate = conf->samplerate_arg;
-       powd->channels = conf->channels_arg;
-       if (!conf->samplerate_given) {
-               int32_t rate;
-               if (get_btr_samplerate(btrn, &rate) >= 0)
-                       powd->samplerate = rate;
-       }
-       if (!conf->channels_given) {
-               int32_t ch;
-               if (get_btr_channels(btrn, &ch) >= 0)
-                       powd->channels = ch;
-       }
+       get_btr_sample_rate(btrn, &val);
+       powd->sample_rate = val;
+       get_btr_channels(btrn, &val);
+       powd->channels = val;
+       get_btr_sample_format(btrn, &val);
+       powd->sample_format = val;
        /*
         * 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
@@ -212,31 +238,36 @@ static int osx_write_open(struct writer_node *wn)
         * any format conversions necessary from your format to the device's
         * format.
         */
-       format.mSampleRate = powd->samplerate;
-       /* The specific encoding type of audio stream */
        format.mFormatID = kAudioFormatLinearPCM;
+       format.mFramesPerPacket = 1;
+       format.mSampleRate = powd->sample_rate;
        /* flags specific to each format */
        format.mFormatFlags = kLinearPCMFormatFlagIsFloat
                | kLinearPCMFormatFlagIsPacked
                | ENDIAN_FLAGS;
+       switch (powd->sample_format) {
+       case SF_S8:
+       case SF_U8:
+               wn->min_iqs = powd->channels;
+               break;
+       default:
+               wn->min_iqs = powd->channels * 2;
+       }
+       format.mBitsPerChannel = 8 * sizeof(float);
+       format.mBytesPerPacket = powd->channels * sizeof(float);
+       format.mBytesPerFrame = format.mBytesPerPacket;
        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->audio_unit, kAudioUnitProperty_StreamFormat,
                        kAudioUnitScope_Input, 0, &format,
                        sizeof(AudioStreamBasicDescription)))
                goto e2;
-       init_buffers(wn);
        ret = -E_ADD_CALLBACK;
        if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_SetRenderCallback,
                        kAudioUnitScope_Input, 0, &inputCallback,
                        sizeof(inputCallback)) < 0)
                goto e3;
-       wn->min_iqs = powd->channels * 2;
        return 1;
 e3:
        destroy_buffers(powd);
@@ -293,10 +324,15 @@ static void osx_write_post_select(__a_unused struct sched *s, struct task *t)
                ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
                if (ret <= 0)
                        break;
+               if (powd->sample_rate == 0) {
+                       ret = core_audio_init(wn);
+                       if (ret < 0)
+                               break;
+               }
                btr_merge(btrn, 8192);
                bytes = btr_next_buffer(btrn, &data);
                //PARA_CRIT_LOG("have: %zu\n", bytes);
-               fill_buffer(powd->to, (short *)data, bytes / sizeof(short));
+               fill_buffer(powd, data, bytes);
                btr_consume(btrn, bytes);
                if (!powd->play) {
                        ret = -E_UNIT_START;
@@ -306,9 +342,10 @@ static void osx_write_post_select(__a_unused struct sched *s, struct task *t)
                }
                powd->to = powd->to->next;
        }
-       if (ret < 0)
+       if (ret < 0 && powd->from->remaining <= 0) {
                btr_remove_node(btrn);
-       t->error = ret;
+               t->error = ret;
+       }
 }
 
 static void osx_write_pre_select(struct sched *s, struct task *t)
@@ -324,7 +361,7 @@ static void osx_write_pre_select(struct sched *s, struct task *t)
                sched_min_delay(s);
        if (ret <= 0 || numbytes < wn->min_iqs)
                return;
-       divisor = powd->samplerate * wn->min_iqs / numbytes;
+       divisor = powd->sample_rate * wn->min_iqs / numbytes;
        if (divisor)
                tv_divide(divisor, &tmp, &delay);
        sched_request_timeout(&delay, s);
diff --git a/para.h b/para.h
index ae3b5b2b2b2a1faf3473c66ec7fd5c25a3193de4..287e3c7a905bddd73b0cfda92e2653292ff93ab8 100644 (file)
--- a/para.h
+++ b/para.h
@@ -259,3 +259,27 @@ _static_inline_ long int para_random(unsigned max)
 /** Used to avoid a shortcoming in vim's syntax highlighting. */
 #define EMBRACE(...) { __VA_ARGS__}
 
+/**
+ * The sample formats supported by paraslash.
+ *
+ * It may be determined by one of the following sources:
+ *
+ *     1. The decoding filter (para_audiod only). In this case, it is always
+ *     \t SF_S16_LE which is the canonical format used within decoders.
+ *
+ *     2. The wav header (para_write only).
+ *
+ *     3. The --format option of para_write.
+ */
+#define SAMPLE_FORMATS \
+       SAMPLE_FORMAT(SF_S8, "8 bit signed"), \
+       SAMPLE_FORMAT(SF_U8, "8 bit unsigned"), \
+       SAMPLE_FORMAT(SF_S16_LE, "16 bit signed, little endian"), \
+       SAMPLE_FORMAT(SF_S16_BE, "16 bit signed, big endian"), \
+       SAMPLE_FORMAT(SF_U16_LE, "16 bit unsigned, little endian"), \
+       SAMPLE_FORMAT(SF_U16_BE, "16 bit unsigned, big endian"), \
+
+#define SAMPLE_FORMAT(a, b) a
+enum sample_format {SAMPLE_FORMATS};
+#undef SAMPLE_FORMAT
+#define SAMPLE_FORMAT(a, b) b
index abd850b2499429ab5e992b771b4144d371a9d987..63c7b7eb6752ed50126c6b5a3d9900d9a1f8030e 100644 (file)
 /** Always write 16 bit header. */
 #define BITS 16
 
-static void make_wav_header(unsigned int channels, unsigned int samplerate,
+static void make_wav_header(unsigned int channels, unsigned int sample_rate,
                char *headbuf)
 {
 
        unsigned int size = 0x7fffffff;
-       int bytespersec = channels * samplerate * BITS / 8;
+       int bytespersec = channels * sample_rate * BITS / 8;
        int align = channels * BITS / 8;
 
-       PARA_DEBUG_LOG("writing wave header: %d channels, %d KHz\n", channels, samplerate);
+       PARA_DEBUG_LOG("writing wave header: %d channels, %d KHz\n", channels, sample_rate);
        memset(headbuf, 0, WAV_HEADER_LEN);
        memcpy(headbuf, "RIFF", 4);
        write_u32(headbuf + 4, size - 8);
@@ -41,7 +41,7 @@ static void make_wav_header(unsigned int channels, unsigned int samplerate,
        write_u32(headbuf + 16, 16); /* 16 + extra format bytes (zero) */
        write_u16(headbuf + 20, 1);     /* format (1 == PCM/uncompressed) */
        write_u16(headbuf + 22, channels);
-       write_u32(headbuf + 24, samplerate);
+       write_u32(headbuf + 24, sample_rate);
        write_u32(headbuf + 28, bytespersec);
        write_u16(headbuf + 32, align); /* number of bytes per sample slice */
        write_u16(headbuf + 34, BITS); /* significant bits per sample */
@@ -92,7 +92,7 @@ static void wav_post_select(__a_unused struct sched *s, struct task *t)
                        goto err;
                return;
        }
-       ret = btr_exec_up(btrn, "samplerate", &buf);
+       ret = btr_exec_up(btrn, "sample_rate", &buf);
        if (ret < 0) {
                ret = -E_WAV_BAD_FC;
                goto err;
index 45fda79e7e7359a0535bd0a932ec03ad944b23fe..aab7cf94f76adc0311aa6cd925da00ad5d8323a4 100644 (file)
@@ -1208,19 +1208,8 @@ static int wmadec_execute(struct btr_node *btrn, const char *cmd, char **result)
        struct filter_node *fn = btr_context(btrn);
        struct private_wmadec_data *pwd = fn->private_data;
 
-       if (!strcmp(cmd, "samplerate")) {
-               if (pwd->ahi.sample_rate == 0)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%u", pwd->ahi.sample_rate);
-               return 1;
-       }
-       if (!strcmp(cmd, "channels")) {
-               if (pwd->ahi.channels == 0)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%u", pwd->ahi.channels);
-               return 1;
-       }
-       return -ERRNO_TO_PARA_ERROR(ENOTSUP);
+       return decoder_execute(cmd, pwd->ahi.sample_rate, pwd->ahi.channels,
+               result);
 }
 
 #define WMA_OUTPUT_BUFFER_SIZE (128 * 1024)
diff --git a/write.c b/write.c
index 416408a1f4cfe606d858ea833f4531225c3d90db..8411003027e33ad3a9bcc43962efcf65547dea04 100644 (file)
--- a/write.c
+++ b/write.c
@@ -32,12 +32,13 @@ enum check_wav_state {
        CWS_NO_HEADER,
 };
 
+/* Information extracted from the wav header. */
 struct check_wav_task {
        int state;
-       /** Number of channels specified in wav header given by \a buf. */
+       /** Number of channels. */
        unsigned channels;
-       /** Sample rate specified in wav header given by \a buf. */
-       unsigned samplerate;
+       unsigned sample_rate;
+       unsigned sample_format;
        /** The task structure used by the scheduler. */
        struct task task;
        struct btr_node *btrn;
@@ -51,12 +52,6 @@ static struct stdin_task sit;
 /** Length of a standard wav header. */
 #define WAV_HEADER_LEN 44
 
-/**
- * Test if audio buffer contains a valid wave header.
- *
- * \return If not, return -E_NO_WAV_HEADER, otherwise, return zero. If
- * there is less than WAV_HEADER_LEN bytes available, return one.
- */
 static void check_wav_pre_select(struct sched *s, struct task *t)
 {
        struct check_wav_task *cwt = container_of(t, struct check_wav_task, task);
@@ -67,23 +62,23 @@ static void check_wav_pre_select(struct sched *s, struct task *t)
                sched_min_delay(s);
 }
 
+#define HANDLE_EXEC(_cmd) \
+       if (!strcmp(cmd, #_cmd)) { \
+               if (!conf._cmd ## _given && cwt->state == CWS_NEED_HEADER) \
+                       return -E_BTR_NAVAIL; \
+               *result = make_message("%d", cwt->state == CWS_NO_HEADER || conf._cmd ## _given? \
+                       conf._cmd ## _arg : cwt->_cmd); \
+               return 1; \
+       } \
+
+
 static int check_wav_exec(struct btr_node *btrn, const char *cmd, char **result)
 {
        struct check_wav_task *cwt = btr_context(btrn);
 
-
-       if (!strcmp(cmd, "samplerate")) {
-               if (cwt->state != CWS_HAVE_HEADER)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%d", cwt->samplerate);
-               return 1;
-       }
-       if (!strcmp(cmd, "channels")) {
-               if (cwt->state != CWS_HAVE_HEADER)
-                       return -E_BTR_NAVAIL;
-               *result = make_message("%d", cwt->channels);
-               return 1;
-       }
+       HANDLE_EXEC(sample_rate);
+       HANDLE_EXEC(channels);
+       HANDLE_EXEC(sample_format);
        return -ERRNO_TO_PARA_ERROR(ENOTSUP);
 }
 
@@ -94,6 +89,8 @@ static void check_wav_post_select(__a_unused struct sched *s, struct task *t)
        unsigned char *a;
        size_t sz;
        int ret;
+       uint16_t bps; /* bits per sample */
+       const char *sample_formats[] = {SAMPLE_FORMATS};
 
        t->error = 0;
        ret = btr_node_status(btrn, cwt->min_iqs, BTR_NT_INTERNAL);
@@ -106,9 +103,13 @@ static void check_wav_post_select(__a_unused struct sched *s, struct task *t)
        if (sz < cwt->min_iqs) /* file size less than WAV_HEADER_SIZE */
                goto pushdown;
        cwt->min_iqs = 0;
-       cwt->channels = 2;
-       cwt->samplerate = 44100;
-       if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' || a[3] != 'F') {
+       /*
+        * The default byte ordering assumed for WAVE data files is
+        * little-endian. Files written using the big-endian byte ordering
+        * scheme have the identifier RIFX instead of RIFF.
+        */
+       if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' ||
+                       (a[3] != 'F' && a[3] != 'X')) {
                PARA_NOTICE_LOG("wav header not found\n");
                cwt->state = CWS_NO_HEADER;
                sprintf(t->status, "check wav: no header");
@@ -118,8 +119,24 @@ static void check_wav_post_select(__a_unused struct sched *s, struct task *t)
        cwt->state = CWS_HAVE_HEADER;
        sprintf(t->status, "check wav: have header");
        cwt->channels = (unsigned) a[22];
-       cwt->samplerate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
-       PARA_INFO_LOG("channels: %d, sample rate: %d\n", cwt->channels, cwt->samplerate);
+       cwt->sample_rate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
+       bps = a[34] + ((unsigned)a[35] << 8);
+       if (bps != 8 && bps != 16) {
+               PARA_WARNING_LOG("%u bps not supported, assuming 16\n", bps);
+               bps = 16;
+       }
+       /*
+        * 8-bit samples are stored as unsigned bytes, ranging from 0 to 255.
+        * 16-bit samples are stored as 2's-complement signed integers, ranging
+        * from -32768 to 32767.
+        */
+       if (bps == 8)
+               cwt->sample_format = SF_U8;
+       else
+               cwt->sample_format = (a[3] == 'F')? SF_S16_LE : SF_S16_BE;
+       PARA_NOTICE_LOG("%dHz, %s, %s\n", cwt->sample_rate,
+               cwt->channels == 1? "mono" : "stereo",
+               sample_formats[cwt->sample_format]);
        btr_consume(btrn, WAV_HEADER_LEN);
 pushdown:
        btr_pushdown(btrn);
index 1da78b5137fda429c80490abc77e548a1a3f812b..6cd7ed22cabf94eafdfd5019b06d366262378c5e 100644 (file)
@@ -147,30 +147,49 @@ void print_writer_helps(int detailed)
        }
 }
 
-static int get_btr_value(struct btr_node *btrn, const char *key, int32_t *result)
+static void get_btr_value(struct btr_node *btrn, const char *cmd,
+               int32_t *result)
 {
        char *buf = NULL;
-       int ret = btr_exec_up(btrn, key, &buf);
+       int ret = btr_exec_up(btrn, cmd, &buf);
 
-       if (ret < 0)
-               return ret;
+       assert(ret >= 0);
        ret = para_atoi32(buf, result);
+       assert(ret >= 0);
        free(buf);
-       return ret;
 }
 
-/*
- * Ask parent btr nodes for the samplerate of the current stream.
+/**
+ * Ask parent btr nodes for the sample rate of the current stream.
+ *
+ * \param btrn Where to start the search.
+ * \param result. Filled in by this function.
+ *
+ * This function is assumed to succeed and terminates on errors.
  */
-int get_btr_samplerate(struct btr_node *btrn, int32_t *result)
+void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
 {
-       return get_btr_value(btrn, "samplerate", result);
+       get_btr_value(btrn, "sample_rate", result);
 }
 
-/*
+/**
  * Ask parent btr nodes for the channel count of the current stream.
+ *
+ * \param btrn See \ref get_btr_sample_rate.
+ * \param result See \ref get_btr_sample_rate.
+ */
+void get_btr_channels(struct btr_node *btrn, int32_t *result)
+{
+       get_btr_value(btrn, "channels", result);
+}
+
+/**
+ * Ask parent btr nodes for the number of bits per sample and the byte sex.
+ *
+ * \param btrn See \ref get_btr_sample_rate.
+ * \param result Contains the sample format as an enum sample_format type.
  */
-int get_btr_channels(struct btr_node *btrn, int32_t *result)
+void get_btr_sample_format(struct btr_node *btrn, int32_t *result)
 {
-       return get_btr_value(btrn, "channels", result);
+       get_btr_value(btrn, "sample_format", result);
 }
index b6f66fa6c27182f99d510c57363b05724ad168fb..d26d42c4d8e6e4f775aa6e7df57300cd587daa6b 100644 (file)
@@ -4,7 +4,7 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file write_common.h exported symbols from write_common.c */
+/** \file write_common.h Exported symbols from write_common.c. */
 
 void writer_init(void);
 void *check_writer_arg(const char *wa, int *writer_num);
@@ -12,5 +12,7 @@ void print_writer_helps(int detailed);
 void register_writer_node(struct writer_node *wn, struct btr_node *parent);
 int setup_writer_node(const char *arg, struct btr_node *parent,
                struct writer_node *wn);
-int get_btr_samplerate(struct btr_node *btrn, int32_t *result);
-int get_btr_channels(struct btr_node *btrn, int32_t *result);
+void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
+void get_btr_channels(struct btr_node *btrn, int32_t *result);
+void get_btr_sample_format(struct btr_node *btrn, int32_t *result);
+