int snd_pcm_file_descriptor(snd_pcm_t *handle);
int snd_pcm_nonblock(snd_pcm_t *handle, int nonblock);
int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t *info);
+int snd_pcm_params_info(snd_pcm_t *handle, snd_pcm_params_info_t *info);
int snd_pcm_params(snd_pcm_t *handle, snd_pcm_params_t *params);
int snd_pcm_setup(snd_pcm_t *handle, snd_pcm_setup_t *setup);
int snd_pcm_channel_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t *setup);
return handle->ops->info(handle->private, info);
}
+int snd_pcm_params_info(snd_pcm_t *handle, snd_pcm_params_info_t *info)
+{
+ assert(handle && info);
+ /* Here we pass private and not op_arg.
+ FIXME: find a better solution */
+ return handle->ops->params_info(handle->private, info);
+}
+
int snd_pcm_setup(snd_pcm_t *handle, snd_pcm_setup_t *setup)
{
int err;
SND_PCM_SFMT_U8
};
-int snd_pcm_plug_slave_params(snd_pcm_params_t *params,
- snd_pcm_info_t *slave_info,
- snd_pcm_params_t *slave_params)
+int snd_pcm_plug_slave_format(int format, snd_pcm_params_info_t *slave_info)
{
- *slave_params = *params;
- if ((slave_info->formats & (1 << params->format.format)) == 0) {
- int format = params->format.format;
- if ((snd_pcm_plug_formats(slave_info->formats) & (1 << format)) == 0)
- return -EINVAL;
- if (snd_pcm_format_linear(format)) {
- int width = snd_pcm_format_width(format);
- int unsignd = snd_pcm_format_unsigned(format);
- int big = snd_pcm_format_big_endian(format);
- int format1;
- int wid, width1=width;
- int dwidth1 = 8;
- for (wid = 0; wid < 4; ++wid) {
- int end, big1 = big;
- for (end = 0; end < 2; ++end) {
- int sgn, unsignd1 = unsignd;
- for (sgn = 0; sgn < 2; ++sgn) {
- format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
- if (format1 >= 0 &&
- slave_info->formats & (1 << format1))
- goto _found;
- unsignd1 = !unsignd1;
- }
- big1 = !big1;
- }
- if (width1 == 32) {
- dwidth1 = -dwidth1;
- width1 = width;
+ if ((snd_pcm_plug_formats(slave_info->formats) & (1 << format)) == 0)
+ return -EINVAL;
+ if (snd_pcm_format_linear(format)) {
+ int width = snd_pcm_format_width(format);
+ int unsignd = snd_pcm_format_unsigned(format);
+ int big = snd_pcm_format_big_endian(format);
+ int format1;
+ int wid, width1=width;
+ int dwidth1 = 8;
+ for (wid = 0; wid < 4; ++wid) {
+ int end, big1 = big;
+ for (end = 0; end < 2; ++end) {
+ int sgn, unsignd1 = unsignd;
+ for (sgn = 0; sgn < 2; ++sgn) {
+ format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
+ if (format1 >= 0 &&
+ slave_info->formats & (1 << format1))
+ goto _found;
+ unsignd1 = !unsignd1;
}
- width1 += dwidth1;
+ big1 = !big1;
}
- return -EINVAL;
- _found:
- slave_params->format.format = format1;
- } else {
- unsigned int i;
- switch (format) {
- case SND_PCM_SFMT_MU_LAW:
+ if (width1 == 32) {
+ dwidth1 = -dwidth1;
+ width1 = width;
+ }
+ width1 += dwidth1;
+ }
+ return -EINVAL;
+ _found:
+ return format1;
+ } else {
+ unsigned int i;
+ switch (format) {
+ case SND_PCM_SFMT_MU_LAW:
#ifndef __KERNEL__
- case SND_PCM_SFMT_A_LAW:
- case SND_PCM_SFMT_IMA_ADPCM:
+ case SND_PCM_SFMT_A_LAW:
+ case SND_PCM_SFMT_IMA_ADPCM:
#endif
- for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) {
- int format1 = preferred_formats[i];
- if (slave_info->formats & (1 << format1)) {
- slave_params->format.format = format1;
- break;
- }
- }
- if (i == sizeof(preferred_formats)/sizeof(preferred_formats[0]))
- return -EINVAL;
+ for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) {
+ int format1 = preferred_formats[i];
+ if (slave_info->formats & (1 << format1))
+ return format1;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+}
+
+struct {
+ unsigned int rate;
+ unsigned int flag;
+} snd_pcm_rates[] = {
+ { 8000, SND_PCM_RATE_8000 },
+ { 11025, SND_PCM_RATE_11025 },
+ { 16000, SND_PCM_RATE_16000 },
+ { 22050, SND_PCM_RATE_22050 },
+ { 32000, SND_PCM_RATE_32000 },
+ { 44100, SND_PCM_RATE_44100 },
+ { 48000, SND_PCM_RATE_48000 },
+ { 88200, SND_PCM_RATE_88200 },
+ { 96000, SND_PCM_RATE_96000 },
+ { 176400, SND_PCM_RATE_176400 },
+ { 192000, SND_PCM_RATE_192000 }
+};
+
+int snd_pcm_plug_slave_rate(unsigned int rate, snd_pcm_params_info_t *slave_info)
+{
+ if (rate <= slave_info->min_rate)
+ return slave_info->min_rate;
+ else if (rate >= slave_info->max_rate)
+ return slave_info->max_rate;
+ else if (!(slave_info->rates & (SND_PCM_RATE_CONTINUOUS |
+ SND_PCM_RATE_KNOT))) {
+ unsigned int k;
+ unsigned int rate1 = 0, rate2 = 0;
+ int delta1, delta2;
+ for (k = 0; k < sizeof(snd_pcm_rates) /
+ sizeof(snd_pcm_rates[0]); ++k) {
+ if (!(snd_pcm_rates[k].flag & slave_info->rates))
+ continue;
+ if (snd_pcm_rates[k].rate < rate) {
+ rate1 = snd_pcm_rates[k].rate;
+ } else if (snd_pcm_rates[k].rate > rate) {
+ rate2 = snd_pcm_rates[k].rate;
break;
- default:
- return -EINVAL;
}
}
+ if (rate1 == 0)
+ return rate2;
+ if (rate2 == 0)
+ return rate1;
+ delta1 = rate - rate1;
+ delta2 = rate2 - rate;
+ if (delta1 < delta2)
+ return rate1;
+ else
+ return rate2;
+ }
+ return rate;
+}
+
+int snd_pcm_plug_slave_params(snd_pcm_params_t *params,
+ snd_pcm_info_t *slave_info,
+ snd_pcm_params_info_t *slave_params_info,
+ snd_pcm_params_t *slave_params)
+{
+ int slave_rate;
+ *slave_params = *params;
+ if ((slave_params_info->formats & (1 << params->format.format)) == 0) {
+ int slave_format = snd_pcm_plug_slave_format(params->format.format, slave_params_info);
+ if (slave_format < 0)
+ return slave_format;
+ slave_params->format.format = slave_format;
}
/* channels */
- if (params->format.channels < slave_info->min_channels ||
- params->format.channels > slave_info->max_channels) {
- unsigned int dst_channels = params->format.channels < slave_info->min_channels ?
- slave_info->min_channels : slave_info->max_channels;
- slave_params->format.channels = dst_channels;
- }
+ if (params->format.channels < slave_params_info->min_channels)
+ slave_params->format.channels = slave_params_info->min_channels;
+ else if (params->format.channels > slave_params_info->max_channels)
+ slave_params->format.channels = slave_params_info->max_channels;
/* rate */
- if (params->format.rate < slave_info->min_rate ||
- params->format.rate > slave_info->max_rate) {
- unsigned int dst_rate = params->format.rate < slave_info->min_rate ?
- slave_info->min_rate : slave_info->max_rate;
- slave_params->format.rate = dst_rate;
- }
-
+ slave_rate = snd_pcm_plug_slave_rate(params->format.rate, slave_params_info);
+ if (slave_rate < 0)
+ return slave_rate;
+ slave_params->format.rate = slave_rate;
+
/* interleave */
if (!(slave_info->flags & SND_PCM_INFO_INTERLEAVE))
slave_params->format.interleave = 0;
return 0;
}
+static int snd_pcm_hw_params_info(void *private, snd_pcm_params_info_t * info)
+{
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
+ int fd = hw->fd;
+ if (ioctl(fd, SND_PCM_IOCTL_PARAMS_INFO, info) < 0)
+ return -errno;
+ return 0;
+}
+
static int snd_pcm_hw_params(void *private, snd_pcm_params_t * params)
{
snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
close: snd_pcm_hw_close,
nonblock: snd_pcm_hw_nonblock,
info: snd_pcm_hw_info,
+ params_info: snd_pcm_hw_params_info,
params: snd_pcm_hw_params,
setup: snd_pcm_hw_setup,
channel_setup: snd_pcm_hw_channel_setup,
int (*close)(void *private);
int (*nonblock)(void *private, int nonblock);
int (*info)(void *private, snd_pcm_info_t *info);
+ int (*params_info)(void *private, snd_pcm_params_info_t *info);
int (*params)(void *private, snd_pcm_params_t *params);
int (*setup)(void *private, snd_pcm_setup_t *setup);
int (*channel_setup)(void *private, snd_pcm_channel_setup_t *setup);
} snd_pcm_plug_t;
unsigned int snd_pcm_plug_formats(unsigned int formats);
+int snd_pcm_plug_slave_format(int format, snd_pcm_params_info_t *slave_info);
+int snd_pcm_plug_slave_rate(unsigned int rate, snd_pcm_params_info_t *slave_info);
int snd_pcm_plug_slave_params(snd_pcm_params_t *params,
snd_pcm_info_t *slave_info,
+ snd_pcm_params_info_t *slave_params_info,
snd_pcm_params_t *slave_params);
int snd_pcm_plug_format(snd_pcm_plug_t *plug,
snd_pcm_params_t *params,
return 0;
}
- if (handle->setup.mmap_size == 0)
+ if (handle->setup.mmap_bytes == 0)
return -ENXIO;
- if ((err = handle->ops->mmap_data(handle->op_arg, (void**)&handle->mmap_data, handle->setup.mmap_size)) < 0)
+ if ((err = handle->ops->mmap_data(handle->op_arg, (void**)&handle->mmap_data, handle->setup.mmap_bytes)) < 0)
return err;
if (data)
*data = handle->mmap_data;
int err;
assert(handle);
assert(handle->mmap_data);
- if ((err = handle->ops->munmap_data(handle->op_arg, handle->mmap_data, handle->setup.mmap_size)) < 0)
+ if ((err = handle->ops->munmap_data(handle->op_arg, handle->mmap_data, handle->setup.mmap_bytes)) < 0)
return err;
free(handle->channels);
handle->channels = 0;
static int snd_pcm_multi_info(void *private, snd_pcm_info_t *info)
{
snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private;
- unsigned int i, channels;
+ unsigned int i;
int err;
snd_pcm_t *handle_0 = multi->slaves[0].handle;
- unsigned int channels0 = multi->slaves[0].channels_total;
err = snd_pcm_info(handle_0, info);
if (err < 0)
return err;
- info->buffer_size /= channels0;
- info->min_fragment_size /= channels0;
- info->max_fragment_size /= channels0;
- info->fragment_align /= channels0;
for (i = 1; i < multi->slaves_count; ++i) {
snd_pcm_t *handle_i = multi->slaves[i].handle;
snd_pcm_info_t info_i;
err = snd_pcm_info(handle_i, &info_i);
if (err < 0)
return err;
- channels0 = multi->slaves[i].channels_total;
- info_i.buffer_size /= channels0;
- info_i.min_fragment_size /= channels0;
- info_i.max_fragment_size /= channels0;
- info_i.fragment_align /= channels0;
info->flags &= info_i.flags;
+ }
+ if (multi->one_to_many)
+ info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
+ return 0;
+}
+
+static int snd_pcm_multi_params_info(void *private, snd_pcm_params_info_t *info)
+{
+ snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private;
+ unsigned int i;
+ int err;
+ snd_pcm_t *handle_0 = multi->slaves[0].handle;
+ err = snd_pcm_params_info(handle_0, info);
+ if (err < 0)
+ return err;
+ for (i = 1; i < multi->slaves_count; ++i) {
+ snd_pcm_t *handle_i = multi->slaves[i].handle;
+ snd_pcm_params_info_t info_i;
+ err = snd_pcm_params_info(handle_i, &info_i);
+ if (err < 0)
+ return err;
info->formats &= info_i.formats;
info->rates &= info_i.rates;
if (info_i.min_rate > info->min_rate)
if (info_i.max_fragments < info->max_fragments)
info->max_fragments = info_i.max_fragments;
}
- channels = multi->channels_count;
- info->buffer_size *= channels;
- info->min_fragment_size *= channels;
- info->max_fragment_size *= channels;
- info->fragment_align *= channels;
- info->min_channels = multi->channels_count;
- info->max_channels = multi->channels_count;
- if (multi->one_to_many)
- info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
return 0;
}
}
}
/* Loaded with a value != 0 if mmap is feasible */
- setup->mmap_size = !multi->one_to_many;
+ setup->mmap_bytes = !multi->one_to_many;
return 0;
}
close: snd_pcm_multi_close,
nonblock: snd_pcm_multi_nonblock,
info: snd_pcm_multi_info,
+ params_info: snd_pcm_multi_params_info,
params: snd_pcm_multi_params,
setup: snd_pcm_multi_setup,
channel_setup: snd_pcm_multi_channel_setup,
if ((err = snd_pcm_info(plug->slave, info)) < 0)
return err;
- info->formats = snd_pcm_plug_formats(info->formats);
- info->min_rate = 4000;
- info->max_rate = 192000;
- info->min_channels = 1;
- info->max_channels = 32;
- info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000;
+ info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
info->flags |= SND_PCM_INFO_INTERLEAVE | SND_PCM_INFO_NONINTERLEAVE;
+ return 0;
+}
- if (plug->slave->valid_setup) {
- info->buffer_size = snd_pcm_plug_client_size(plug, info->buffer_size);
- info->min_fragment_size = snd_pcm_plug_client_size(plug, info->min_fragment_size);
- info->max_fragment_size = snd_pcm_plug_client_size(plug, info->max_fragment_size);
- info->fragment_align = snd_pcm_plug_client_size(plug, info->fragment_align);
- info->fifo_size = snd_pcm_plug_client_size(plug, info->fifo_size);
- info->transfer_block_size = snd_pcm_plug_client_size(plug, info->transfer_block_size);
+static int snd_pcm_plug_params_info(void *private, snd_pcm_params_info_t *info)
+{
+ int err;
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) private;
+ snd_pcm_params_info_t slave_info;
+ int rate;
+ int slave_format, slave_rate;
+ unsigned int slave_channels;
+
+ memset(&info->formats, 0, (char*)(info + 1) - (char*) &info->formats);
+ info->req.fail_reason = 0;
+ info->req.fail_mask = 0;
+
+ if (info->req_mask & SND_PCM_PARAMS_RATE) {
+ info->min_rate = info->req.format.rate;
+ info->max_rate = info->req.format.rate;
+ } else {
+ info->min_rate = 4000;
+ info->max_rate = 192000;
}
- info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
+ /* ??? */
+ info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000;
+ if (info->req_mask & SND_PCM_PARAMS_CHANNELS) {
+ info->min_channels = info->req.format.channels;
+ info->max_channels = info->req.format.channels;
+ } else {
+ info->min_channels = 1;
+ info->max_channels = 32;
+ }
+
+ memset(&slave_info, 0, sizeof(slave_info));
+ if ((err = snd_pcm_params_info(plug->slave, &slave_info)) < 0)
+ return err;
+
+ if (info->req_mask & SND_PCM_PARAMS_FORMAT)
+ info->formats = 1 << info->req.format.format;
+ else
+ info->formats = snd_pcm_plug_formats(slave_info.formats);
+
+ info->min_fragments = slave_info.min_fragments;
+ info->max_fragments = slave_info.max_fragments;
+
+ if (!(info->req_mask & SND_PCM_PARAMS_FORMAT))
+ return 0;
+ slave_format = snd_pcm_plug_slave_format(info->req.format.format, &slave_info);
+ if (slave_format < 0) {
+ info->req.fail_mask = SND_PCM_PARAMS_FORMAT;
+ info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+ return -EINVAL;
+ }
+
+ if (!(info->req_mask & SND_PCM_PARAMS_RATE))
+ return 0;
+ slave_rate = snd_pcm_plug_slave_rate(info->req.format.rate, &slave_info);
+ if (slave_rate < 0) {
+ info->req.fail_mask = SND_PCM_PARAMS_RATE;
+ info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+ return -EINVAL;
+ }
+
+ if (!(info->req_mask & SND_PCM_PARAMS_CHANNELS))
+ return 0;
+ slave_channels = info->req.format.rate;
+ if (slave_channels < info->min_channels)
+ slave_channels = info->min_channels;
+ else if (slave_channels > info->max_channels)
+ slave_channels = info->max_channels;
+
+ slave_info.req_mask = (SND_PCM_PARAMS_FORMAT |
+ SND_PCM_PARAMS_CHANNELS |
+ SND_PCM_PARAMS_RATE);
+ slave_info.req.format.format = info->req.format.format;
+ slave_info.req.format.channels = info->req.format.channels;
+ slave_info.req.format.rate = info->req.format.rate;
+ if ((err = snd_pcm_params_info(plug->slave, &slave_info)) < 0) {
+ info->req.fail_mask = slave_info.req.fail_mask;
+ info->req.fail_reason = slave_info.req.fail_reason;
+ return err;
+ }
+ rate = info->req.format.rate;
+ info->buffer_size = slave_info.buffer_size * rate / slave_rate;
+ info->min_fragment_size = slave_info.min_fragment_size * rate / slave_rate;
+ info->max_fragment_size = slave_info.max_fragment_size * rate / slave_rate;
+ info->fragment_align = slave_info.fragment_align * rate / slave_rate;
return 0;
}
setup->frames_align = snd_pcm_plug_client_size(plug, setup->frames_align);
setup->frames_xrun_max = snd_pcm_plug_client_size(plug, setup->frames_xrun_max);
setup->frames_fill_max = snd_pcm_plug_client_size(plug, setup->frames_fill_max);
- setup->mmap_size = 0;
+ setup->mmap_bytes = 0;
if (plug->handle->stream == SND_PCM_STREAM_PLAYBACK)
setup->format = plug->first->src_format;
else
close: snd_pcm_plug_close,
nonblock: snd_pcm_plug_nonblock,
info: snd_pcm_plug_info,
+ params_info: snd_pcm_plug_params_info,
params: snd_pcm_plug_params,
setup: snd_pcm_plug_setup,
channel_setup: snd_pcm_plug_channel_setup,
{
snd_pcm_params_t slave_params, params1;
snd_pcm_info_t slave_info;
+ snd_pcm_params_info_t slave_params_info;
snd_pcm_plugin_t *plugin;
snd_pcm_plug_t *plug;
int err;
*/
memset(&slave_info, 0, sizeof(slave_info));
- slave_info.stream = plug->slave->stream;
if ((err = snd_pcm_info(plug->slave, &slave_info)) < 0) {
snd_pcm_plug_clear(plug);
return err;
}
+ memset(&slave_params_info, 0, sizeof(slave_params_info));
+ if ((err = snd_pcm_params_info(plug->slave, &slave_params_info)) < 0) {
+ snd_pcm_plug_clear(plug);
+ return err;
+ }
- if ((err = snd_pcm_plug_slave_params(params, &slave_info, &slave_params)) < 0)
+ if ((err = snd_pcm_plug_slave_params(params, &slave_info, &slave_params_info, &slave_params)) < 0)
return err;
plug->handle->ops->params = snd_pcm_plug_params;
plug->handle->ops->setup = snd_pcm_plug_setup;
plug->handle->ops->info = snd_pcm_plug_info;
+ plug->handle->ops->params_info = snd_pcm_plug_params_info;
plug->handle->op_arg = plug->slave->op_arg;
return 0;
} else {
pdprintf("params requested params: format = %i, rate = %i, channels = %i\n", slave_params.format.format, slave_params.format.rate, slave_params.format.channels);
err = snd_pcm_params(plug->slave, &slave_params);
- if (err < 0)
+ if (err < 0) {
+ params->fail_mask = slave_params.fail_mask;
+ params->fail_reason = slave_params.fail_reason;
return err;
+ }
err = snd_pcm_plug_action(plug, INIT, 0);
if (err < 0)
handle->ops->params = snd_pcm_plug_params;
handle->ops->setup = snd_pcm_plug_setup;
handle->ops->info = snd_pcm_plug_info;
+ handle->ops->params_info = snd_pcm_plug_params_info;
handle->op_arg = slave->op_arg;
handle->mode = slave->mode;
handle->private = plug;