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"
----------------------------------------
/** 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)
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)
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) {
&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) {
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;
#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 */
/* 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.
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)
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);
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;
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;
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);
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
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)
{
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);
+}
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>
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 = ""
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
#~~~~~~~~~~~~~~~~~~~~~
int typestr="num"
default="20"
optional
+ 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
+
/** 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,
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) {
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)
/** 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)
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)
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) {
#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. */
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);
* 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;
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) {
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
* 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.
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;
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;
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;
/** 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;
};
*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;
}
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;
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
* 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);
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;
}
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)
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);
/** 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
* 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
/** 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);
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 */
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;
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)
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;
/** 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);
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);
}
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);
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");
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);
}
}
-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);
}
* 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);
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);
+