Some rate converters have native float resolution, no need to loose information by converting to s16.
Signed-off-by: Pavel Hofman pavel.hofman@ivitera.com
diff --git a/include/pcm_rate.h b/include/pcm_rate.h index 4d70df2..e92900b 100644 --- a/include/pcm_rate.h +++ b/include/pcm_rate.h @@ -91,6 +91,11 @@ typedef struct snd_pcm_rate_ops { void (*convert_s16)(void *obj, int16_t *dst, unsigned int dst_frames, const int16_t *src, unsigned int src_frames); /** + * convert a float interleaved-data array; exclusive with convert + */ + void (*convert_float)(void *obj, float *dst, unsigned int dst_frames, + const float *src, unsigned int src_frames); + /** * compute the frame size for input */ snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames); diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index 70e30e5..9f4ec23 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -35,7 +35,7 @@ #include "iatomic.h"
#include "plugin_ops.h" - +typedef float float_t; #if 0 #define DEBUG_REFINE #endif @@ -66,8 +66,8 @@ struct _snd_pcm_rate { snd_pcm_rate_ops_t ops; unsigned int get_idx; unsigned int put_idx; - int16_t *src_buf; - int16_t *dst_buf; + void *src_buf; + void *dst_buf; int start_pending; /* start is triggered but not commited to slave */ snd_htimestamp_t trigger_tstamp; unsigned int plugin_version; @@ -310,13 +310,23 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) rate->sareas[chn].step = swidth; }
- if (rate->ops.convert_s16) { + if (rate->ops.convert_float) { + rate->get_idx = snd_pcm_linear_get_index(rate->info.in.format, SND_PCM_FORMAT_S32); + rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, rate->info.out.format); + free(rate->src_buf); + rate->src_buf = (float_t *) malloc(channels * rate->info.in.period_size * sizeof(float_t)); + free(rate->dst_buf); + rate->dst_buf = (float_t *) malloc(channels * rate->info.out.period_size * sizeof(float_t)); + if (! rate->src_buf || ! rate->dst_buf) + goto error; + } + else 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); + rate->src_buf = (int16_t *) malloc(channels * rate->info.in.period_size * 2); free(rate->dst_buf); - rate->dst_buf = malloc(channels * rate->info.out.period_size * 2); + rate->dst_buf = (int16_t *) malloc(channels * rate->info.out.period_size * 2); if (! rate->src_buf || ! rate->dst_buf) goto error; } @@ -503,6 +513,85 @@ static void convert_from_s16(snd_pcm_rate_t *rate, const int16_t *buf, } } } +static void convert_to_float(snd_pcm_rate_t *rate, float_t *buf, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, unsigned int frames, + unsigned int channels) +{ +#ifndef DOC_HIDDEN +#define GET32_LABELS +#include "plugin_ops.h" +#undef GET32_LABELS +#endif /* DOC_HIDDEN */ + void *get32 = get32_labels[rate->get_idx]; + const char *src; + int32_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]; + // converting the source format to int32 first + goto *get32; +#ifndef DOC_HIDDEN +#define GET32_END after_get +#include "plugin_ops.h" +#undef GET32_END +#endif /* DOC_HIDDEN */ + after_get: + // converting to float for the plugin + *buf++ = (float_t) sample; + srcs[c] += src_step[c]; + } + } +} + +static void convert_from_float(snd_pcm_rate_t *rate, const float_t *buf, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, unsigned int frames, + unsigned int channels) +{ +#ifndef DOC_HIDDEN +#define PUT32_LABELS +#include "plugin_ops.h" +#undef PUT32_LABELS +#endif /* DOC_HIDDEN */ + void *put32 = put32_labels[rate->put_idx]; + char *dst; + int32_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]; + // plugin returned float, converting to int32 first + sample = (int32_t) *buf++; + // converting int32 to the destination format + goto *put32; +#ifndef DOC_HIDDEN +#define PUT32_END after_put +#include "plugin_ops.h" +#undef PUT32_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, @@ -511,18 +600,36 @@ static void do_convert(const snd_pcm_channel_area_t *dst_areas, unsigned int channels, snd_pcm_rate_t *rate) { - if (rate->ops.convert_s16) { + if (rate->ops.convert_float) { + const float_t *src; + float_t *dst; + if (! rate->src_buf) + src = src_areas->addr + src_offset * sizeof(float_t) * channels; + else { + convert_to_float(rate, rate->src_buf, src_areas, src_offset, + src_frames, channels); + src = rate->src_buf; + } + if (! rate->dst_buf) + dst = dst_areas->addr + dst_offset * sizeof(float_t) * channels; + else + dst = rate->dst_buf; + rate->ops.convert_float(rate->obj, dst, dst_frames, src, src_frames); + if (dst == rate->dst_buf) + convert_from_float(rate, rate->dst_buf, dst_areas, dst_offset, + dst_frames, channels); + } else if (rate->ops.convert_s16) { const int16_t *src; int16_t *dst; if (! rate->src_buf) - src = src_areas->addr + src_offset * 2 * channels; + src = src_areas->addr + src_offset * sizeof(int16_t) * 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 = dst_areas->addr + dst_offset * 2 * channels; + dst = dst_areas->addr + dst_offset * sizeof(int16_t) * channels; else dst = rate->dst_buf; rate->ops.convert_s16(rate->obj, dst, dst_frames, src, src_frames); @@ -1409,7 +1516,7 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, } #endif
- if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) || + if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16 || rate->ops.convert_float) || ! rate->ops.input_frames || ! rate->ops.output_frames) { SNDERR("Inproper rate plugin %s initialization", type); snd_pcm_close(pcm);