/**
* Protocol version
*/
-#define SND_PCM_RATE_PLUGIN_VERSION 0x010002
+#define SND_PCM_RATE_PLUGIN_VERSION 0x010003
/** hw_params information for a single side */
typedef struct snd_pcm_rate_side_info {
unsigned int channels;
} snd_pcm_rate_info_t;
+enum {
+ SND_PCM_RATE_FLAG_INTERLEAVED = (1U << 0), /** only interleaved format */
+ SND_PCM_RATE_FLAG_SYNC_FORMATS = (1U << 1), /** both input and output formats have to be identical */
+};
+
/** Callback table of rate-converter */
typedef struct snd_pcm_rate_ops {
/**
* new ops since version 0x010002
*/
void (*dump)(void *obj, snd_output_t *out);
+ /**
+ * get the supported input and output formats (optional);
+ * new ops since version 0x010003
+ */
+ int (*get_supported_formats)(void *obj, uint64_t *in_formats,
+ uint64_t *out_formats,
+ unsigned int *flags);
} snd_pcm_rate_ops_t;
/** open function type */
snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames);
snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames);
} snd_pcm_rate_old_ops_t;
+
+/* old rate_ops for protocol version 0x010002 */
+typedef struct snd_pcm_rate_v2_ops {
+ void (*close)(void *obj);
+ int (*init)(void *obj, snd_pcm_rate_info_t *info);
+ void (*free)(void *obj);
+ void (*reset)(void *obj);
+ int (*adjust_pitch)(void *obj, snd_pcm_rate_info_t *info);
+ void (*convert)(void *obj,
+ const snd_pcm_channel_area_t *dst_areas,
+ snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
+ const snd_pcm_channel_area_t *src_areas,
+ snd_pcm_uframes_t src_offset, unsigned int src_frames);
+ void (*convert_s16)(void *obj, int16_t *dst, unsigned int dst_frames,
+ const int16_t *src, unsigned int src_frames);
+ snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames);
+ snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames);
+ unsigned int version;
+ int (*get_supported_rates)(void *obj, unsigned int *rate_min,
+ unsigned int *rate_max);
+ void (*dump)(void *obj, snd_output_t *out);
+} snd_pcm_rate_v2_ops_t;
#endif
#ifdef __cplusplus
void *open_func;
void *obj;
snd_pcm_rate_ops_t ops;
- unsigned int get_idx;
- unsigned int put_idx;
- int16_t *src_buf;
- int16_t *dst_buf;
+ unsigned int src_conv_idx;
+ unsigned int dst_conv_idx;
+ snd_pcm_channel_area_t *src_buf;
+ snd_pcm_channel_area_t *dst_buf;
int start_pending; /* start is triggered but not commited to slave */
snd_htimestamp_t trigger_tstamp;
unsigned int plugin_version;
unsigned int rate_min, rate_max;
+ snd_pcm_format_t orig_in_format;
+ snd_pcm_format_t orig_out_format;
+ uint64_t in_formats;
+ uint64_t out_formats;
+ unsigned int format_flags;
};
#define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */
-
#endif /* DOC_HIDDEN */
/* allocate a channel area and a temporary buffer for the given size */
snd_pcm_generic_hw_refine);
}
+/* evaluate the best matching available format to the given format */
+static int get_best_format(uint64_t mask, snd_pcm_format_t orig)
+{
+ int pwidth = snd_pcm_format_physical_width(orig);
+ int width = snd_pcm_format_width(orig);
+ int signd = snd_pcm_format_signed(orig);
+ int best_score = -1;
+ int match = -1;
+ int f, score;
+
+ for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) {
+ if (!(mask & (1ULL << f)))
+ continue;
+ score = 0;
+ if (snd_pcm_format_linear(f)) {
+ if (snd_pcm_format_physical_width(f) == pwidth)
+ score++;
+ if (snd_pcm_format_physical_width(f) >= pwidth)
+ score++;
+ if (snd_pcm_format_width(f) == width)
+ score++;
+ if (snd_pcm_format_signed(f) == signd)
+ score++;
+ }
+ if (score > best_score) {
+ match = f;
+ best_score = score;
+ }
+ }
+
+ return match;
+}
+
+/* set up the input and output formats from the available lists */
+static int choose_preferred_format(snd_pcm_rate_t *rate)
+{
+ uint64_t in_mask = rate->in_formats;
+ uint64_t out_mask = rate->out_formats;
+ int in, out;
+
+ if (!in_mask || !out_mask)
+ return 0;
+
+ if (rate->orig_in_format == rate->orig_out_format)
+ if (in_mask & out_mask & (1ULL << rate->orig_in_format))
+ return 0; /* nothing changed */
+
+ repeat:
+ in = get_best_format(in_mask, rate->orig_in_format);
+ out = get_best_format(out_mask, rate->orig_out_format);
+ if (in < 0 || out < 0)
+ return -ENOENT;
+
+ if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) &&
+ in != out) {
+ if (out_mask & (1ULL << in))
+ out = in;
+ else if (in_mask & (1ULL << out))
+ in = out;
+ else {
+ in_mask &= ~(1ULL << in);
+ out_mask &= ~(1ULL << out);
+ goto repeat;
+ }
+ }
+
+ rate->info.in.format = in;
+ rate->info.out.format = out;
+ return 0;
+}
+
static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_t *slave = rate->gen.slave;
snd_pcm_rate_side_info_t *sinfo, *cinfo;
- unsigned int channels, cwidth, swidth, chn;
+ unsigned int channels, cwidth, swidth, chn, acc;
+ int need_src_buf, need_dst_buf;
int err = snd_pcm_hw_params_slave(pcm, params,
snd_pcm_rate_hw_refine_cchange,
snd_pcm_rate_hw_refine_sprepare,
if (err < 0)
return err;
err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
+ if (err < 0)
+ return err;
+ err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc);
if (err < 0)
return err;
SNDMSG("rate plugin already in use");
return -EBUSY;
}
- err = rate->ops.init(rate->obj, &rate->info);
- if (err < 0)
- return err;
rate->pareas = rate_alloc_tmp_buf(rate, cinfo->format, channels,
cinfo->period_size);
rate->sareas = rate_alloc_tmp_buf(rate, sinfo->format, channels,
sinfo->period_size);
- if (!rate->pareas || !rate->sareas)
- goto error;
-
- if (rate->ops.convert_s16) {
- rate->get_idx = snd_pcm_linear_get_index(rate->info.in.format, SND_PCM_FORMAT_S16);
- rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, rate->info.out.format);
- free(rate->src_buf);
- rate->src_buf = malloc(channels * rate->info.in.period_size * 2);
- free(rate->dst_buf);
- rate->dst_buf = malloc(channels * rate->info.out.period_size * 2);
- if (! rate->src_buf || ! rate->dst_buf)
+ if (!rate->pareas || !rate->sareas) {
+ err = -ENOMEM;
+ goto error_pareas;
+ }
+
+ rate->orig_in_format = rate->info.in.format;
+ rate->orig_out_format = rate->info.out.format;
+ if (choose_preferred_format(rate) < 0) {
+ SNDERR("No matching format in rate plugin");
+ err = -EINVAL;
+ goto error_pareas;
+ }
+
+ err = rate->ops.init(rate->obj, &rate->info);
+ if (err < 0)
+ goto error_init;
+
+ rate_free_tmp_buf(&rate->src_buf);
+ rate_free_tmp_buf(&rate->dst_buf);
+
+ need_src_buf = need_dst_buf = 0;
+
+ if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) &&
+ !(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
+ acc == SND_PCM_ACCESS_RW_INTERLEAVED)) {
+ need_src_buf = need_dst_buf = 1;
+ } else {
+ if (rate->orig_in_format != rate->info.in.format)
+ need_src_buf = 1;
+ if (rate->orig_out_format != rate->info.out.format)
+ need_dst_buf = 1;
+ }
+
+ if (need_src_buf) {
+ rate->src_conv_idx =
+ snd_pcm_linear_convert_index(rate->orig_in_format,
+ rate->info.in.format);
+ rate->src_buf = rate_alloc_tmp_buf(rate, rate->info.in.format,
+ channels, rate->info.in.period_size);
+ if (!rate->src_buf) {
+ err = -ENOMEM;
goto error;
+ }
+ }
+
+ if (need_dst_buf) {
+ rate->dst_conv_idx =
+ snd_pcm_linear_convert_index(rate->info.out.format,
+ rate->orig_out_format);
+ rate->dst_buf = rate_alloc_tmp_buf(rate, rate->info.out.format,
+ channels, rate->info.out.period_size);
+ if (!rate->dst_buf) {
+ err = -ENOMEM;
+ goto error;
+ }
}
return 0;
error:
- rate_free_tmp_buf(&rate->pareas);
- rate_free_tmp_buf(&rate->sareas);
+ rate_free_tmp_buf(&rate->src_buf);
+ rate_free_tmp_buf(&rate->dst_buf);
+ error_init:
if (rate->ops.free)
rate->ops.free(rate->obj);
- return -ENOMEM;
+ error_pareas:
+ rate_free_tmp_buf(&rate->pareas);
+ rate_free_tmp_buf(&rate->sareas);
+ return err;
}
static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
rate_free_tmp_buf(&rate->sareas);
if (rate->ops.free)
rate->ops.free(rate->obj);
- free(rate->src_buf);
- free(rate->dst_buf);
- rate->src_buf = rate->dst_buf = NULL;
+ rate_free_tmp_buf(&rate->src_buf);
+ rate_free_tmp_buf(&rate->dst_buf);
return snd_pcm_hw_free(rate->gen.slave);
}
return 0;
}
-static void convert_to_s16(snd_pcm_rate_t *rate, int16_t *buf,
- const snd_pcm_channel_area_t *areas,
- snd_pcm_uframes_t offset, unsigned int frames,
- unsigned int channels)
-{
-#ifndef DOC_HIDDEN
-#define GET16_LABELS
-#include "plugin_ops.h"
-#undef GET16_LABELS
-#endif /* DOC_HIDDEN */
- void *get = get16_labels[rate->get_idx];
- const char *src;
- int16_t sample;
- const char *srcs[channels];
- int src_step[channels];
- unsigned int c;
-
- for (c = 0; c < channels; c++) {
- srcs[c] = snd_pcm_channel_area_addr(areas + c, offset);
- src_step[c] = snd_pcm_channel_area_step(areas + c);
- }
-
- while (frames--) {
- for (c = 0; c < channels; c++) {
- src = srcs[c];
- goto *get;
-#ifndef DOC_HIDDEN
-#define GET16_END after_get
-#include "plugin_ops.h"
-#undef GET16_END
-#endif /* DOC_HIDDEN */
- after_get:
- *buf++ = sample;
- srcs[c] += src_step[c];
- }
- }
-}
-
-static void convert_from_s16(snd_pcm_rate_t *rate, const int16_t *buf,
- const snd_pcm_channel_area_t *areas,
- snd_pcm_uframes_t offset, unsigned int frames,
- unsigned int channels)
-{
-#ifndef DOC_HIDDEN
-#define PUT16_LABELS
-#include "plugin_ops.h"
-#undef PUT16_LABELS
-#endif /* DOC_HIDDEN */
- void *put = put16_labels[rate->put_idx];
- char *dst;
- int16_t sample;
- char *dsts[channels];
- int dst_step[channels];
- unsigned int c;
-
- for (c = 0; c < channels; c++) {
- dsts[c] = snd_pcm_channel_area_addr(areas + c, offset);
- dst_step[c] = snd_pcm_channel_area_step(areas + c);
- }
-
- while (frames--) {
- for (c = 0; c < channels; c++) {
- dst = dsts[c];
- sample = *buf++;
- goto *put;
-#ifndef DOC_HIDDEN
-#define PUT16_END after_put
-#include "plugin_ops.h"
-#undef PUT16_END
-#endif /* DOC_HIDDEN */
- after_put:
- dsts[c] += dst_step[c];
- }
- }
-}
-
static void do_convert(const snd_pcm_channel_area_t *dst_areas,
snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
const snd_pcm_channel_area_t *src_areas,
unsigned int channels,
snd_pcm_rate_t *rate)
{
- if (rate->ops.convert_s16) {
- const int16_t *src;
- int16_t *dst;
- if (! rate->src_buf)
- src = (int16_t *)src_areas->addr + src_offset * channels;
- else {
- convert_to_s16(rate, rate->src_buf, src_areas, src_offset,
- src_frames, channels);
- src = rate->src_buf;
- }
- if (! rate->dst_buf)
- dst = (int16_t *)dst_areas->addr + dst_offset * channels;
- else
- dst = rate->dst_buf;
- rate->ops.convert_s16(rate->obj, dst, dst_frames, src, src_frames);
- if (dst == rate->dst_buf)
- convert_from_s16(rate, rate->dst_buf, dst_areas, dst_offset,
- dst_frames, channels);
+ const snd_pcm_channel_area_t *out_areas;
+ snd_pcm_uframes_t out_offset;
+
+ if (rate->dst_buf) {
+ out_areas = rate->dst_buf;
+ out_offset = 0;
} else {
- rate->ops.convert(rate->obj, dst_areas, dst_offset, dst_frames,
- src_areas, src_offset, src_frames);
+ out_areas = dst_areas;
+ out_offset = dst_offset;
+ }
+
+ if (rate->src_buf) {
+ snd_pcm_linear_convert(rate->src_buf, 0,
+ src_areas, src_offset,
+ channels, src_frames,
+ rate->src_conv_idx);
+ src_areas = rate->src_buf;
+ src_offset = 0;
}
+
+ if (rate->ops.convert)
+ rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames,
+ src_areas, src_offset, src_frames);
+ else
+ rate->ops.convert_s16(rate->obj,
+ snd_pcm_channel_area_addr(out_areas, out_offset),
+ dst_frames,
+ snd_pcm_channel_area_addr(src_areas, src_offset),
+ src_frames);
+ if (rate->dst_buf)
+ snd_pcm_linear_convert(dst_areas, dst_offset,
+ rate->dst_buf, 0,
+ channels, dst_frames,
+ rate->dst_conv_idx);
}
static inline void
return NULL;
}
+static void rate_initial_setup(snd_pcm_rate_t *rate)
+{
+ if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION)
+ rate->plugin_version = rate->ops.version;
+
+ if (rate->plugin_version >= 0x010002 &&
+ rate->ops.get_supported_rates)
+ rate->ops.get_supported_rates(rate->obj,
+ &rate->rate_min,
+ &rate->rate_max);
+
+ if (rate->plugin_version >= 0x010003 &&
+ rate->ops.get_supported_formats) {
+ rate->ops.get_supported_formats(rate->obj,
+ &rate->in_formats,
+ &rate->out_formats,
+ &rate->format_flags);
+ } else if (!rate->ops.convert && rate->ops.convert_s16) {
+ rate->in_formats = rate->out_formats =
+ 1ULL << SND_PCM_FORMAT_S16;
+ rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED;
+ }
+}
+
#ifdef PIC
static int is_builtin_plugin(const char *type)
{
lib = lib_name;
}
- rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
- rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
- rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
-
open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL);
if (open_conf_func) {
err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION,
&rate->obj, &rate->ops, converter_conf);
if (!err) {
- rate->plugin_version = rate->ops.version;
- if (rate->ops.get_supported_rates)
- rate->ops.get_supported_rates(rate->obj,
- &rate->rate_min,
- &rate->rate_max);
rate->open_func = open_conf_func;
return 0;
} else {
rate->open_func = open_func;
err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
- if (!err) {
- rate->plugin_version = rate->ops.version;
- if (rate->ops.get_supported_rates)
- rate->ops.get_supported_rates(rate->obj,
- &rate->rate_min,
- &rate->rate_max);
+ if (!err)
return 0;
- }
/* try to open with the old protocol version */
rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
&rate->obj, &rate->ops);
- if (err) {
- snd_dlobj_cache_put(open_func);
- rate->open_func = NULL;
- }
+ if (!err)
+ return 0;
+
+ snd_dlobj_cache_put(open_func);
+ rate->open_func = NULL;
return err;
}
#endif
rate->srate = srate;
rate->sformat = sformat;
+ rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
+ rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
+ rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
+
err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
if (err < 0) {
free(rate);
return err;
}
+ rate_initial_setup(rate);
+
pcm->ops = &snd_pcm_rate_ops;
pcm->fast_ops = &snd_pcm_rate_fast_ops;
pcm->private_data = rate;