diff mbox series

[alsa-plugins,1/2] rate-lav: Support multiple formats

Message ID 20210617100359.12197-2-tiwai@suse.de
State New
Headers show
Series Support multiple formats | expand

Commit Message

Takashi Iwai June 17, 2021, 10:03 a.m. UTC
Now that ALSA rate plugin core allows each rate plugin dealing with
multiple formats, this patch extends the rate-lav plugin to accept
more formats, namely, U8, S16 and S32.  The code has been carefully
modified so that it should still be compilable with old alsa-lib.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 rate-lav/rate_lavrate.c | 115 ++++++++++++++++++++++++++++++++++------
 1 file changed, 99 insertions(+), 16 deletions(-)
diff mbox series

Patch

diff --git a/rate-lav/rate_lavrate.c b/rate-lav/rate_lavrate.c
index e9c6740ac870..2ab1d8894e63 100644
--- a/rate-lav/rate_lavrate.c
+++ b/rate-lav/rate_lavrate.c
@@ -32,6 +32,8 @@  struct rate_src {
 	unsigned int in_rate;
 	unsigned int out_rate;
 	unsigned int channels;
+
+	unsigned int version;
 };
 
 static snd_pcm_uframes_t input_frames(void *obj ATTRIBUTE_UNUSED,
@@ -52,9 +54,34 @@  static void pcm_src_free(void *obj)
 	swr_free(&rate->avr);
 }
 
+static int to_av_format(snd_pcm_format_t f)
+{
+	switch (f) {
+	case SND_PCM_FORMAT_FLOAT:
+		return AV_SAMPLE_FMT_FLT;
+	case SND_PCM_FORMAT_U8:
+		return AV_SAMPLE_FMT_U8;
+	case SND_PCM_FORMAT_S16:
+		return AV_SAMPLE_FMT_S16;
+	case SND_PCM_FORMAT_S32:
+	default:
+		return AV_SAMPLE_FMT_S32;
+	}
+}
+
+static int support_multi_format(struct rate_src *rate)
+{
+#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
+	return rate->version >= 0x010003;
+#else
+	return 0;
+#endif
+}
+
 static int pcm_src_init(void *obj, snd_pcm_rate_info_t *info)
 {
 	struct rate_src *rate = obj;
+	int fmt;
 
 	if (!rate->avr || rate->channels != info->channels) {
 		int ret;
@@ -74,8 +101,12 @@  static int pcm_src_init(void *obj, snd_pcm_rate_info_t *info)
 					  av_get_default_channel_layout(rate->channels), 0);
 		av_opt_set_int(rate->avr, "in_sample_rate", rate->in_rate, 0);
 		av_opt_set_int(rate->avr, "out_sample_rate", rate->out_rate, 0);
-		av_opt_set_sample_fmt(rate->avr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
-		av_opt_set_sample_fmt(rate->avr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
+		fmt = support_multi_format(rate) ? info->in.format : SND_PCM_FORMAT_S16;
+		av_opt_set_sample_fmt(rate->avr, "in_sample_fmt",
+				      to_av_format(fmt), 0);
+		fmt = support_multi_format(rate) ? info->out.format : SND_PCM_FORMAT_S16;
+		av_opt_set_sample_fmt(rate->avr, "out_sample_fmt",
+				      to_av_format(fmt), 0);
 
 		ret = swr_init(rate->avr);
 		if (ret < 0) {
@@ -109,12 +140,10 @@  static void pcm_src_reset(void *obj)
 	}
 }
 
-static void pcm_src_convert_s16(void *obj, int16_t *dst,
-				unsigned int dst_frames,
-				const int16_t *src,
-				unsigned int src_frames)
+static void do_convert(struct rate_src *rate,
+		       void *dst, unsigned int dst_frames,
+		       const void *src, unsigned int src_frames)
 {
-	struct rate_src *rate = obj;
 	unsigned int total_in = swr_get_delay(rate->avr, rate->in_rate) + src_frames;
 
 	swr_convert(rate->avr, (uint8_t **)&dst, dst_frames,
@@ -125,6 +154,38 @@  static void pcm_src_convert_s16(void *obj, int16_t *dst,
 			     src_frames);
 }
 
+#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
+static inline void *get_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset)
+{
+	return (char *)area->addr + (area->first + area->step * offset) / 8;
+}
+
+static void pcm_src_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)
+{
+	struct rate_src *rate = obj;
+	const void *src = get_addr(src_areas, src_offset);
+	void *dst = get_addr(dst_areas, dst_offset);
+
+	do_convert(rate, dst, dst_frames, src, src_frames);
+}
+#endif
+
+static void pcm_src_convert_s16(void *obj, int16_t *dst,
+				unsigned int dst_frames,
+				const int16_t *src,
+				unsigned int src_frames)
+{
+	struct rate_src *rate = obj;
+
+	do_convert(rate, dst, dst_frames, src, src_frames);
+}
+
 static void pcm_src_close(void *obj)
 {
 	pcm_src_free(obj);
@@ -145,12 +206,29 @@  static void dump(void *obj ATTRIBUTE_UNUSED, snd_output_t *out)
 }
 #endif
 
+#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
+static int get_supported_formats(void *obj, uint64_t *in_formats,
+				 uint64_t *out_formats,
+				 unsigned int *flags)
+{
+	*in_formats = *out_formats =
+		(1ULL << SND_PCM_FORMAT_U8) |
+		(1ULL << SND_PCM_FORMAT_S16) |
+		(1ULL << SND_PCM_FORMAT_S32);
+	*flags = SND_PCM_RATE_FLAG_INTERLEAVED;
+	return 0;
+}
+#endif
+
 static snd_pcm_rate_ops_t pcm_src_ops = {
 	.close = pcm_src_close,
 	.init = pcm_src_init,
 	.free = pcm_src_free,
 	.reset = pcm_src_reset,
 	.adjust_pitch = pcm_src_adjust_pitch,
+#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
+	.convert = pcm_src_convert,
+#endif
 	.convert_s16 = pcm_src_convert_s16,
 	.input_frames = input_frames,
 	.output_frames = output_frames,
@@ -159,30 +237,35 @@  static snd_pcm_rate_ops_t pcm_src_ops = {
 	.get_supported_rates = get_supported_rates,
 	.dump = dump,
 #endif
+#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
+	.get_supported_formats = get_supported_formats,
+#endif
 };
 
 int pcm_src_open(unsigned int version, void **objp, snd_pcm_rate_ops_t *ops)
 {
 	struct rate_src *rate;
 
-#if SND_PCM_RATE_PLUGIN_VERSION < 0x010002
-	if (version != SND_PCM_RATE_PLUGIN_VERSION) {
-		fprintf(stderr, "Invalid rate plugin version %x\n", version);
-		return -EINVAL;
-	}
-#endif
 	rate = calloc(1, sizeof(*rate));
 	if (!rate)
 		return -ENOMEM;
 
 	*objp = rate;
 	rate->avr = NULL;
+	rate->version = version;
 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
-	if (version == 0x010001)
+	if (version == 0x010001) {
 		memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_old_ops_t));
-	else
+		return 0;
+	}
+#endif
+#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
+	if (version == 0x010002) {
+		memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_v2_ops_t));
+		return 0;
+	}
 #endif
-		*ops = pcm_src_ops;
+	*ops = pcm_src_ops;
 	return 0;
 }