1 /*
2  * Rate converter plugin using libavresample
3  * Copyright (c) 2014 by Anton Khirnov
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  */
15 
16 #include <stdio.h>
17 #include <alsa/asoundlib.h>
18 #include <alsa/pcm_rate.h>
19 
20 #include <libavresample/avresample.h>
21 #include <libavutil/channel_layout.h>
22 #include <libavutil/opt.h>
23 #include <libavutil/mathematics.h>
24 #include <libavutil/samplefmt.h>
25 
26 
27 static unsigned int filter_size = 16;
28 static unsigned int phase_shift = 10; /* auto-adjusts */
29 static double cutoff = 0; /* auto-adjusts */
30 
31 struct rate_src {
32 	AVAudioResampleContext *avr;
33 
34 	unsigned int in_rate;
35 	unsigned int out_rate;
36 	unsigned int channels;
37 };
38 
input_frames(void * obj ATTRIBUTE_UNUSED,snd_pcm_uframes_t frames)39 static snd_pcm_uframes_t input_frames(void *obj ATTRIBUTE_UNUSED,
40 				      snd_pcm_uframes_t frames)
41 {
42 	return frames;
43 }
44 
output_frames(void * obj ATTRIBUTE_UNUSED,snd_pcm_uframes_t frames)45 static snd_pcm_uframes_t output_frames(void *obj ATTRIBUTE_UNUSED,
46 				       snd_pcm_uframes_t frames)
47 {
48 	return frames;
49 }
50 
pcm_src_free(void * obj)51 static void pcm_src_free(void *obj)
52 {
53 	struct rate_src *rate = obj;
54 	avresample_free(&rate->avr);
55 }
56 
pcm_src_init(void * obj,snd_pcm_rate_info_t * info)57 static int pcm_src_init(void *obj, snd_pcm_rate_info_t *info)
58 {
59 	struct rate_src *rate = obj;
60 	int i, ir, or;
61 
62 	if (!rate->avr || rate->channels != info->channels) {
63 		int ret;
64 
65 		pcm_src_free(rate);
66 		rate->channels = info->channels;
67 		ir = rate->in_rate = info->in.rate;
68 		or = rate->out_rate = info->out.rate;
69 		i = av_gcd(or, ir);
70 		if (or > ir) {
71 			phase_shift = or/i;
72 		} else {
73 			phase_shift = ir/i;
74 		}
75 		if (cutoff <= 0.0) {
76 			cutoff = 1.0 - 1.0/filter_size;
77 			if (cutoff < 0.80)
78 				cutoff = 0.80;
79 		}
80 
81 		rate->avr = avresample_alloc_context();
82 		if (!rate->avr)
83 			return -ENOMEM;
84 
85 		av_opt_set_int(rate->avr, "in_sample_rate",     info->in.rate,  0);
86 		av_opt_set_int(rate->avr, "out_sample_rate",    info->out.rate, 0);
87 		av_opt_set_int(rate->avr, "in_sample_format",   AV_SAMPLE_FMT_S16, 0);
88 		av_opt_set_int(rate->avr, "out_sample_format",  AV_SAMPLE_FMT_S16, 0);
89 		av_opt_set_int(rate->avr, "in_channel_layout",  av_get_default_channel_layout(rate->channels), 0);
90 		av_opt_set_int(rate->avr, "out_channel_layout", av_get_default_channel_layout(rate->channels), 0);
91 
92 		av_opt_set_int(rate->avr, "filter_size",        filter_size, 0);
93 		av_opt_set_int(rate->avr, "phase_shift",        phase_shift, 0);
94 		av_opt_set_double(rate->avr, "cutoff",          cutoff,      0);
95 
96 		ret = avresample_open(rate->avr);
97 		if (ret < 0) {
98 			avresample_free(&rate->avr);
99 			return -EINVAL;
100 		}
101 	}
102 
103 	return 0;
104 }
105 
pcm_src_adjust_pitch(void * obj,snd_pcm_rate_info_t * info)106 static int pcm_src_adjust_pitch(void *obj, snd_pcm_rate_info_t *info)
107 {
108 	struct rate_src *rate = obj;
109 
110 	if (info->out.rate != rate->out_rate || info->in.rate != rate->in_rate)
111 		pcm_src_init(obj, info);
112 	return 0;
113 }
114 
pcm_src_reset(void * obj)115 static void pcm_src_reset(void *obj)
116 {
117 	struct rate_src *rate = obj;
118 
119 	if (rate->avr) {
120 #if 0
121 		avresample_close(rate->avr);
122 		avresample_open(rate->avr);
123 #endif
124 	}
125 }
126 
pcm_src_convert_s16(void * obj,int16_t * dst,unsigned int dst_frames,const int16_t * src,unsigned int src_frames)127 static void pcm_src_convert_s16(void *obj, int16_t *dst,
128 				unsigned int dst_frames,
129 				const int16_t *src,
130 				unsigned int src_frames)
131 {
132 	struct rate_src *rate = obj;
133 	int chans = rate->channels;
134 	unsigned int total_in = avresample_get_delay(rate->avr) + src_frames;
135 
136 	avresample_convert(rate->avr, (uint8_t **)&dst, dst_frames * chans * 2, dst_frames,
137 	                   (uint8_t **)&src, src_frames * chans * 2, src_frames);
138 
139 	avresample_set_compensation(rate->avr,
140                                     total_in - src_frames > filter_size ? 0 : 1, src_frames);
141 }
142 
pcm_src_close(void * obj)143 static void pcm_src_close(void *obj)
144 {
145 	pcm_src_free(obj);
146 }
147 
148 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
get_supported_rates(void * obj ATTRIBUTE_UNUSED,unsigned int * rate_min,unsigned int * rate_max)149 static int get_supported_rates(void *obj ATTRIBUTE_UNUSED,
150 			       unsigned int *rate_min,
151 			       unsigned int *rate_max)
152 {
153 	*rate_min = *rate_max = 0; /* both unlimited */
154 	return 0;
155 }
156 
dump(void * obj ATTRIBUTE_UNUSED,snd_output_t * out)157 static void dump(void *obj ATTRIBUTE_UNUSED, snd_output_t *out)
158 {
159 	snd_output_printf(out, "Converter: libavr\n");
160 }
161 #endif
162 
163 static snd_pcm_rate_ops_t pcm_src_ops = {
164 	.close = pcm_src_close,
165 	.init = pcm_src_init,
166 	.free = pcm_src_free,
167 	.reset = pcm_src_reset,
168 	.adjust_pitch = pcm_src_adjust_pitch,
169 	.convert_s16 = pcm_src_convert_s16,
170 	.input_frames = input_frames,
171 	.output_frames = output_frames,
172 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
173 	.version = SND_PCM_RATE_PLUGIN_VERSION,
174 	.get_supported_rates = get_supported_rates,
175 	.dump = dump,
176 #endif
177 };
178 
pcm_src_open(unsigned int version,void ** objp,snd_pcm_rate_ops_t * ops)179 int pcm_src_open(unsigned int version, void **objp, snd_pcm_rate_ops_t *ops)
180 
181 {
182 	struct rate_src *rate;
183 
184 #if SND_PCM_RATE_PLUGIN_VERSION < 0x010002
185 	if (version != SND_PCM_RATE_PLUGIN_VERSION) {
186 		fprintf(stderr, "Invalid rate plugin version %x\n", version);
187 		return -EINVAL;
188 	}
189 #endif
190 	rate = calloc(1, sizeof(*rate));
191 	if (!rate)
192 		return -ENOMEM;
193 
194 	*objp = rate;
195 	rate->avr = NULL;
196 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
197 	if (version == 0x010001)
198 		memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_old_ops_t));
199 	else
200 #endif
201 		*ops = pcm_src_ops;
202 	return 0;
203 }
204 
SND_PCM_RATE_PLUGIN_ENTRY(lavrate)205 int SND_PCM_RATE_PLUGIN_ENTRY(lavrate)(unsigned int version, void **objp,
206 			snd_pcm_rate_ops_t *ops)
207 {
208 	return pcm_src_open(version, objp, ops);
209 }
SND_PCM_RATE_PLUGIN_ENTRY(lavrate_higher)210 int SND_PCM_RATE_PLUGIN_ENTRY(lavrate_higher)(unsigned int version,
211 			void **objp, snd_pcm_rate_ops_t *ops)
212 {
213 	filter_size = 64;
214 	return pcm_src_open(version, objp, ops);
215 }
SND_PCM_RATE_PLUGIN_ENTRY(lavrate_high)216 int SND_PCM_RATE_PLUGIN_ENTRY(lavrate_high)(unsigned int version,
217 			void **objp, snd_pcm_rate_ops_t *ops)
218 {
219 	filter_size = 32;
220 	return pcm_src_open(version, objp, ops);
221 }
SND_PCM_RATE_PLUGIN_ENTRY(lavrate_fast)222 int SND_PCM_RATE_PLUGIN_ENTRY(lavrate_fast)(unsigned int version,
223 			void **objp, snd_pcm_rate_ops_t *ops)
224 {
225 	filter_size = 8;
226 	return pcm_src_open(version, objp, ops);
227 }
SND_PCM_RATE_PLUGIN_ENTRY(lavrate_faster)228 int SND_PCM_RATE_PLUGIN_ENTRY(lavrate_faster)(unsigned int version,
229 			void **objp, snd_pcm_rate_ops_t *ops)
230 {
231 	filter_size = 4;
232 	return pcm_src_open(version, objp, ops);
233 }
234 
235 
236