]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
write: Support audio formats != 16 bit little endian.
authorAndre Noll <maan@systemlinux.org>
Mon, 12 Jul 2010 15:28:58 +0000 (17:28 +0200)
committerAndre Noll <maan@systemlinux.org>
Tue, 13 Jul 2010 12:39:16 +0000 (14:39 +0200)
This adds the new --sample_format option to para_write and teaches
the check_wav_task to read the sample format from the wav header
rather than assuming 16 bit little endian.

The alsa, oss and osx writers all ask the upper buffer tree nodes
for the current audio format. For para_write the check_wav task
answers this query by using the value given at the command line,
looking at the wav header, or using 16 bit little endian as the
default answer.

For para_audiod, the current decoder answers this query. In this
case the audio format is either 16 bit little endian or 16 bit
big endian, depending on the endianness of the machine.

alsa_write.c
filter_common.c
ggo/write.m4
oss_write.c
osx_write.c
para.h
write.c
write_common.c
write_common.h

index 09cca93f4a34783e16b2bc601408184591a32362..dfa7a2e1a5ba068da9e457068e50eeccdf079ad3 100644 (file)
@@ -31,9 +31,6 @@
 #include "alsa_write.cmdline.h"
 #include "error.h"
 
 #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 */
 /** Data specific to the alsa writer. */
 struct private_alsa_write_data {
        /** The alsa handle */
@@ -49,6 +46,8 @@ struct private_alsa_write_data {
         * of the writer node group.
         */
        unsigned sample_rate;
         * of the writer node group.
         */
        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.
        /**
         * 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;
 };
 
        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)
 /* Install PCM software and hardware configuration. */
 static int alsa_init(struct private_alsa_write_data *pad,
                struct alsa_write_args_info *conf)
@@ -80,7 +92,8 @@ 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_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_SAMPLE_FORMAT;
        if (snd_pcm_hw_params_set_channels(pad->handle, hwparams,
                        pad->channels) < 0)
@@ -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");
                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;
                * pad->channels / 8;
        if (pad->bytes_per_frame <= 0)
                return -E_PHYSICAL_WIDTH;
@@ -229,12 +242,16 @@ again:
        }
        if (!pad->handle) {
                int32_t val;
        }
        if (!pad->handle) {
                int32_t val;
+
                if (bytes == 0) /* no data available */
                        return;
                get_btr_sample_rate(btrn, &val);
                pad->sample_rate = val;
                get_btr_channels(btrn, &val);
                pad->channels = val;
                if (bytes == 0) /* no data available */
                        return;
                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);
                PARA_INFO_LOG("%d channel(s), %dHz\n", pad->channels,
                        pad->sample_rate);
                ret = alsa_init(pad, wn->conf);
index 8e657f5a4e880ea3968991974a8c07e1e75d96db..cdd9f39855c8add2f5c4f4bdfdd88c65bbe72651 100644 (file)
@@ -137,12 +137,18 @@ void generic_filter_pre_select(struct sched *s, struct task *t)
                sched_min_delay(s);
 }
 
                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.
  *
 /**
  * Execute a btr command for a decoder.
  *
- * The buffer tree nodes of the writers ask the parent nodes about sample_rate
- * and the channels count. This function is called by all decoders to answer
- * these queries.
+ * 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 cmd The command to be executed by the child node.
  * \param sample_rate Known to the decoder.
@@ -164,5 +170,9 @@ int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                *result = make_message("%u", channels);
                return 1;
        }
                *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);
 }
        return -ERRNO_TO_PARA_ERROR(ENOTSUP);
 }
index 847bd2054bb060332961a68749c665acfd9aa2bf..524d72484a7757000a320934e56b2d1ff1ce49d9 100644 (file)
@@ -32,3 +32,12 @@ option "sample-rate" s
 int typestr = "num"
 default = "44100"
 optional
 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 ea31fc8124aa84d374e3b7e48a32758a052b5352..b82b3968baf214f3889cca805306438073e21ac6 100644 (file)
@@ -25,9 +25,6 @@
 #include "oss_write.cmdline.h"
 #include "error.h"
 
 #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. */
 /** 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;
 };
 
        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);
 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.
  */
  * 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 sample_rate, 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;
        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 sample_rate, unsigned chann
        if (ret < 0)
                goto err;
        /* set PCM format */
        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;
        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 */
                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) {
        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 sample_rate, unsigned chann
        ret = -E_BAD_CHANNEL_COUNT;
        if (ch != channels)
                goto err;
        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
 
        /*
         * Set sampling rate
@@ -157,10 +169,11 @@ static void oss_post_select(__a_unused struct sched *s,
        if (ret == 0)
                return;
        if (powd->fd < 0) {
        if (ret == 0)
                return;
        if (powd->fd < 0) {
-               int32_t rate, ch;
+               int32_t rate, ch, format;
                get_btr_sample_rate(btrn, &rate);
                get_btr_channels(btrn, &ch);
                get_btr_sample_rate(btrn, &rate);
                get_btr_channels(btrn, &ch);
-               ret = oss_init(wn, rate, ch);
+               get_btr_sample_format(btrn, &format);
+               ret = oss_init(wn, rate, ch, format);
                if (ret < 0)
                        goto out;
                return;
                if (ret < 0)
                        goto out;
                return;
index 65a5adc79db3164ded3ef5a79070eb0ee71a43b4..cfd02e7453b1ccac04eb951e5f42009801806b55 100644 (file)
@@ -58,6 +58,8 @@ struct private_osx_write_data {
        struct osx_buffer *to;
        /** sample rate of the current audio stream */
        unsigned sample_rate;
        struct osx_buffer *to;
        /** sample rate of the current audio stream */
        unsigned sample_rate;
+       /** Sample format of the current audio stream */
+       unsigned sample_format;
        /** number of channels of the current audio stream */
        unsigned channels;
 };
        /** 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;
 }
 
        *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;
        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;
        }
        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;
 }
        b->ptr = b->buffer;
        b->remaining = b->size;
 }
@@ -164,8 +189,16 @@ static OSStatus osx_callback(void * inClientData,
 
 static int osx_write_open(struct writer_node *wn)
 {
 
 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};
        ComponentDescription desc;
        Component comp;
        AURenderCallbackStruct inputCallback = {osx_callback, powd};
@@ -174,7 +207,6 @@ static int osx_write_open(struct writer_node *wn)
        struct btr_node *btrn = wn->btrn;
        int32_t val;
 
        struct btr_node *btrn = wn->btrn;
        int32_t val;
 
-       wn->private_data = powd;
        /* where did that default audio output go? */
        desc.componentType = kAudioUnitType_Output;
        desc.componentSubType = kAudioUnitSubType_DefaultOutput;
        /* where did that default audio output go? */
        desc.componentType = kAudioUnitType_Output;
        desc.componentSubType = kAudioUnitSubType_DefaultOutput;
@@ -197,6 +229,8 @@ static int osx_write_open(struct writer_node *wn)
        powd->sample_rate = val;
        get_btr_channels(btrn, &val);
        powd->channels = 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
        /*
         * 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
@@ -204,31 +238,36 @@ static int osx_write_open(struct writer_node *wn)
         * any format conversions necessary from your format to the device's
         * format.
         */
         * any format conversions necessary from your format to the device's
         * format.
         */
-       format.mSampleRate = powd->sample_rate;
-       /* The specific encoding type of audio stream */
        format.mFormatID = kAudioFormatLinearPCM;
        format.mFormatID = kAudioFormatLinearPCM;
+       format.mFramesPerPacket = 1;
+       format.mSampleRate = powd->sample_rate;
        /* flags specific to each format */
        format.mFormatFlags = kLinearPCMFormatFlagIsFloat
                | kLinearPCMFormatFlagIsPacked
                | ENDIAN_FLAGS;
        /* 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.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;
        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;
        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);
        return 1;
 e3:
        destroy_buffers(powd);
@@ -285,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;
                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);
                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;
                btr_consume(btrn, bytes);
                if (!powd->play) {
                        ret = -E_UNIT_START;
@@ -298,9 +342,10 @@ static void osx_write_post_select(__a_unused struct sched *s, struct task *t)
                }
                powd->to = powd->to->next;
        }
                }
                powd->to = powd->to->next;
        }
-       if (ret < 0)
+       if (ret < 0 && powd->from->remaining <= 0) {
                btr_remove_node(btrn);
                btr_remove_node(btrn);
-       t->error = ret;
+               t->error = ret;
+       }
 }
 
 static void osx_write_pre_select(struct sched *s, struct task *t)
 }
 
 static void osx_write_pre_select(struct sched *s, struct task *t)
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__}
 
 /** 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
diff --git a/write.c b/write.c
index d2169f26c9ed499c300c03ca01a81b0c9f1963db..8411003027e33ad3a9bcc43962efcf65547dea04 100644 (file)
--- a/write.c
+++ b/write.c
@@ -32,12 +32,13 @@ enum check_wav_state {
        CWS_NO_HEADER,
 };
 
        CWS_NO_HEADER,
 };
 
+/* Information extracted from the wav header. */
 struct check_wav_task {
        int state;
 struct check_wav_task {
        int state;
-       /** Number of channels specified in wav header given by \a buf. */
+       /** Number of channels. */
        unsigned channels;
        unsigned channels;
-       /** Sample rate specified in wav header given by \a buf. */
        unsigned sample_rate;
        unsigned sample_rate;
+       unsigned sample_format;
        /** The task structure used by the scheduler. */
        struct task task;
        struct btr_node *btrn;
        /** 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
 
 /** 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);
 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);
@@ -71,7 +66,7 @@ static void check_wav_pre_select(struct sched *s, struct task *t)
        if (!strcmp(cmd, #_cmd)) { \
                if (!conf._cmd ## _given && cwt->state == CWS_NEED_HEADER) \
                        return -E_BTR_NAVAIL; \
        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? \
+               *result = make_message("%d", cwt->state == CWS_NO_HEADER || conf._cmd ## _given? \
                        conf._cmd ## _arg : cwt->_cmd); \
                return 1; \
        } \
                        conf._cmd ## _arg : cwt->_cmd); \
                return 1; \
        } \
@@ -83,6 +78,7 @@ static int check_wav_exec(struct btr_node *btrn, const char *cmd, char **result)
 
        HANDLE_EXEC(sample_rate);
        HANDLE_EXEC(channels);
 
        HANDLE_EXEC(sample_rate);
        HANDLE_EXEC(channels);
+       HANDLE_EXEC(sample_format);
        return -ERRNO_TO_PARA_ERROR(ENOTSUP);
 }
 
        return -ERRNO_TO_PARA_ERROR(ENOTSUP);
 }
 
@@ -93,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;
        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);
 
        t->error = 0;
        ret = btr_node_status(btrn, cwt->min_iqs, BTR_NT_INTERNAL);
@@ -105,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;
        if (sz < cwt->min_iqs) /* file size less than WAV_HEADER_SIZE */
                goto pushdown;
        cwt->min_iqs = 0;
-       cwt->channels = 2;
-       cwt->sample_rate = 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");
                PARA_NOTICE_LOG("wav header not found\n");
                cwt->state = CWS_NO_HEADER;
                sprintf(t->status, "check wav: no header");
@@ -118,7 +120,23 @@ static void check_wav_post_select(__a_unused struct sched *s, struct task *t)
        sprintf(t->status, "check wav: have header");
        cwt->channels = (unsigned) a[22];
        cwt->sample_rate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
        sprintf(t->status, "check wav: have header");
        cwt->channels = (unsigned) a[22];
        cwt->sample_rate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
-       PARA_INFO_LOG("channels: %d, sample rate: %d\n", cwt->channels, cwt->sample_rate);
+       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);
        btr_consume(btrn, WAV_HEADER_LEN);
 pushdown:
        btr_pushdown(btrn);
index b73ba9a11a753244bf23dc6d5f4a2e05985197cb..6cd7ed22cabf94eafdfd5019b06d366262378c5e 100644 (file)
@@ -159,18 +159,37 @@ static void get_btr_value(struct btr_node *btrn, const char *cmd,
        free(buf);
 }
 
        free(buf);
 }
 
-/*
+/**
  * Ask parent btr nodes for the sample rate 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.
  */
 void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
 {
        get_btr_value(btrn, "sample_rate", result);
 }
 
  */
 void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
 {
        get_btr_value(btrn, "sample_rate", result);
 }
 
-/*
+/**
  * Ask parent btr nodes for the channel count of the current stream.
  * 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);
 }
  */
 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.
+ */
+void get_btr_sample_format(struct btr_node *btrn, int32_t *result)
+{
+       get_btr_value(btrn, "sample_format", result);
+}
index 74f8e1c455b0d8d84ff9bad6f92db7e643c97234..d26d42c4d8e6e4f775aa6e7df57300cd587daa6b 100644 (file)
@@ -14,3 +14,5 @@ int setup_writer_node(const char *arg, struct btr_node *parent,
                struct writer_node *wn);
 void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
 void get_btr_channels(struct btr_node *btrn, int32_t *result);
                struct writer_node *wn);
 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);
+