From: Andre Noll Date: Sun, 1 Aug 2010 11:25:59 +0000 (+0200) Subject: Merge branch 't/error2' X-Git-Tag: v0.4.4~6 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=75cf863ef7a7cf5fe346f098ffb07a472b8366ec;hp=5842e3e7f2aa17fe16cb806b4ba955ce1f25ce53 Merge branch 't/error2' --- diff --git a/NEWS b/NEWS index e1954b31..d4260273 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ 0.4.4 (to be announced) "persistent regularity" ----------------------------------------------- + - aacdec: Fix a bug which could make the AAC decoder segfault. + - Support for sample formats other than 16 bit little endian. + ---------------------------------------- 0.4.3 (2010-07-05) "imaginary radiation" ---------------------------------------- diff --git a/aacdec_filter.c b/aacdec_filter.c index f33f21eb..1127fc11 100644 --- a/aacdec_filter.c +++ b/aacdec_filter.c @@ -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) @@ -112,7 +100,7 @@ next_buffer: return; btr_merge(btrn, fn->min_iqs); len = btr_next_buffer(btrn, (char **)&inbuf); - len = PARA_MAX(len, (size_t)8192); + len = PARA_MIN(len, (size_t)8192); consumed = 0; iqs = btr_get_input_queue_size(btrn); if (!padd->initialized) { @@ -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) { @@ -186,7 +174,8 @@ next_buffer: padd->frame_info.bytesconsumed); PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage( padd->frame_info.error)); - consumed++; /* catch 21 */ + if (consumed < len) + consumed++; /* catch 21 */ goto success; } padd->error_count = 0; diff --git a/alsa_write.c b/alsa_write.c index 7a4b868d..7f8aae01 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -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,27 +241,24 @@ 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; wn->min_iqs = pad->bytes_per_frame; + goto again; } frames = bytes / pad->bytes_per_frame; frames = snd_pcm_writei(pad->handle, data, frames); diff --git a/buffer_tree.c b/buffer_tree.c index 3a59f8b5..d92d9863 100644 --- a/buffer_tree.c +++ b/buffer_tree.c @@ -634,7 +634,7 @@ void btr_consume(struct btr_node *btrn, size_t numbytes) numbytes -= br->btrb->size - br->consumed; btr_drop_buffer_reference(br); } - assert(true); + assert(false); } /* * We have a wrap buffer, consume from it. If in total, i.e. including diff --git a/filter.h b/filter.h index c34acca8..692020c4 100644 --- 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) { diff --git a/filter_common.c b/filter_common.c index 06a1f52b..cdd9f398 100644 --- a/filter_common.c +++ b/filter_common.c @@ -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); +} diff --git a/ggo/alsa_write.m4 b/ggo/alsa_write.m4 index afcefccf..d0cd4071 100644 --- a/ggo/alsa_write.m4 +++ b/ggo/alsa_write.m4 @@ -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. -" diff --git a/ggo/oss_write.ggo b/ggo/oss_write.ggo index 60682186..351561cd 100644 --- a/ggo/oss_write.ggo +++ b/ggo/oss_write.ggo @@ -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 = "" diff --git a/ggo/osx_write.ggo b/ggo/osx_write.ggo index 33c495ab..bee16d9e 100644 --- a/ggo/osx_write.ggo +++ b/ggo/osx_write.ggo @@ -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 = "" diff --git a/ggo/write.m4 b/ggo/write.m4 index d954316a..524d7248 100644 --- a/ggo/write.m4 +++ b/ggo/write.m4 @@ -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 + diff --git a/mp3dec_filter.c b/mp3dec_filter.c index 35448da7..c2c15a3a 100644 --- a/mp3dec_filter.c +++ b/mp3dec_filter.c @@ -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) diff --git a/oggdec_filter.c b/oggdec_filter.c index 541b0b23..68b4db16 100644 --- a/oggdec_filter.c +++ b/oggdec_filter.c @@ -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) { diff --git a/oss_write.c b/oss_write.c index 6525afa4..b82b3968 100644 --- a/oss_write.c +++ b/oss_write.c @@ -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; diff --git a/osx_write.c b/osx_write.c index bcff09f7..cfd02e74 100644 --- a/osx_write.c +++ b/osx_write.c @@ -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 ae3b5b2b..287e3c7a 100644 --- 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 diff --git a/send_common.c b/send_common.c index 005583cf..33c56e7a 100644 --- a/send_common.c +++ b/send_common.c @@ -261,6 +261,7 @@ void generic_com_off(struct sender_status *ss) * Accept a connection on the socket this server is listening on. * * \param ss The sender whose listening fd is ready for reading. + * \param rfds Passed to para_accept(), * * This must be called only if the socket fd of \a ss is ready for reading. It * calls para_accept() to accept the connection and performs the following diff --git a/wav_filter.c b/wav_filter.c index abd850b2..63c7b7eb 100644 --- a/wav_filter.c +++ b/wav_filter.c @@ -24,15 +24,15 @@ /** 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; diff --git a/wmadec_filter.c b/wmadec_filter.c index 45fda79e..aab7cf94 100644 --- a/wmadec_filter.c +++ b/wmadec_filter.c @@ -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 416408a1..84110030 100644 --- 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); diff --git a/write_common.c b/write_common.c index 1da78b51..6cd7ed22 100644 --- a/write_common.c +++ b/write_common.c @@ -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); } diff --git a/write_common.h b/write_common.h index b6f66fa6..d26d42c4 100644 --- a/write_common.h +++ b/write_common.h @@ -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); +