1 /*
2 * Linear rate converter plugin
3 *
4 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
5 * 2004 by Jaroslav Kysela <perex@perex.cz>
6 * 2006 by Takashi Iwai <tiwai@suse.de>
7 *
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as
10 * published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <inttypes.h>
24 #include "bswap.h"
25 #include "pcm_local.h"
26 #include "pcm_plugin.h"
27 #include "pcm_rate.h"
28
29 #include "plugin_ops.h"
30
31
32 /* LINEAR_DIV needs to be large enough to handle resampling from 768000 -> 8000 */
33 #define LINEAR_DIV_SHIFT 19
34 #define LINEAR_DIV (1<<LINEAR_DIV_SHIFT)
35
36 struct rate_linear {
37 unsigned int get_idx;
38 unsigned int put_idx;
39 unsigned int pitch;
40 unsigned int pitch_shift; /* for expand interpolation */
41 unsigned int channels;
42 int16_t *old_sample;
43 void (*func)(struct rate_linear *rate,
44 const snd_pcm_channel_area_t *dst_areas,
45 snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
46 const snd_pcm_channel_area_t *src_areas,
47 snd_pcm_uframes_t src_offset, unsigned int src_frames);
48 };
49
input_frames(void * obj,snd_pcm_uframes_t frames)50 static snd_pcm_uframes_t input_frames(void *obj, snd_pcm_uframes_t frames)
51 {
52 struct rate_linear *rate = obj;
53 if (frames == 0)
54 return 0;
55 /* Round toward zero */
56 return muldiv_near(frames, LINEAR_DIV, rate->pitch);
57 }
58
output_frames(void * obj,snd_pcm_uframes_t frames)59 static snd_pcm_uframes_t output_frames(void *obj, snd_pcm_uframes_t frames)
60 {
61 struct rate_linear *rate = obj;
62 if (frames == 0)
63 return 0;
64 /* Round toward zero */
65 return muldiv_near(frames, rate->pitch, LINEAR_DIV);
66 }
67
linear_expand(struct rate_linear * rate,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)68 static void linear_expand(struct rate_linear *rate,
69 const snd_pcm_channel_area_t *dst_areas,
70 snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
71 const snd_pcm_channel_area_t *src_areas,
72 snd_pcm_uframes_t src_offset, unsigned int src_frames)
73 {
74 #define GET16_LABELS
75 #define PUT16_LABELS
76 #include "plugin_ops.h"
77 #undef GET16_LABELS
78 #undef PUT16_LABELS
79 void *get = get16_labels[rate->get_idx];
80 void *put = put16_labels[rate->put_idx];
81 unsigned int get_threshold = rate->pitch;
82 unsigned int channel;
83 unsigned int src_frames1;
84 unsigned int dst_frames1;
85 int16_t sample = 0;
86 unsigned int pos;
87
88 for (channel = 0; channel < rate->channels; ++channel) {
89 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
90 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
91 const char *src;
92 char *dst;
93 int src_step, dst_step;
94 int16_t old_sample = 0;
95 int16_t new_sample;
96 int old_weight, new_weight;
97 src = snd_pcm_channel_area_addr(src_area, src_offset);
98 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
99 src_step = snd_pcm_channel_area_step(src_area);
100 dst_step = snd_pcm_channel_area_step(dst_area);
101 src_frames1 = 0;
102 dst_frames1 = 0;
103 new_sample = rate->old_sample[channel];
104 pos = get_threshold;
105 while (dst_frames1 < dst_frames) {
106 if (pos >= get_threshold) {
107 pos -= get_threshold;
108 old_sample = new_sample;
109 if (src_frames1 < src_frames) {
110 goto *get;
111 #define GET16_END after_get
112 #include "plugin_ops.h"
113 #undef GET16_END
114 after_get:
115 new_sample = sample;
116 }
117 }
118 new_weight = (pos << (16 - rate->pitch_shift)) / (get_threshold >> rate->pitch_shift);
119 old_weight = 0x10000 - new_weight;
120 sample = (old_sample * old_weight + new_sample * new_weight) >> 16;
121 goto *put;
122 #define PUT16_END after_put
123 #include "plugin_ops.h"
124 #undef PUT16_END
125 after_put:
126 dst += dst_step;
127 dst_frames1++;
128 pos += LINEAR_DIV;
129 if (pos >= get_threshold) {
130 src += src_step;
131 src_frames1++;
132 }
133 }
134 rate->old_sample[channel] = new_sample;
135 }
136 }
137
138 /* optimized version for S16 format */
linear_expand_s16(struct rate_linear * rate,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)139 static void linear_expand_s16(struct rate_linear *rate,
140 const snd_pcm_channel_area_t *dst_areas,
141 snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
142 const snd_pcm_channel_area_t *src_areas,
143 snd_pcm_uframes_t src_offset, unsigned int src_frames)
144 {
145 unsigned int channel;
146 unsigned int src_frames1;
147 unsigned int dst_frames1;
148 unsigned int get_threshold = rate->pitch;
149 unsigned int pos;
150
151 for (channel = 0; channel < rate->channels; ++channel) {
152 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
153 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
154 const int16_t *src;
155 int16_t *dst;
156 int src_step, dst_step;
157 int16_t old_sample = 0;
158 int16_t new_sample;
159 int old_weight, new_weight;
160 src = snd_pcm_channel_area_addr(src_area, src_offset);
161 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
162 src_step = snd_pcm_channel_area_step(src_area) >> 1;
163 dst_step = snd_pcm_channel_area_step(dst_area) >> 1;
164 src_frames1 = 0;
165 dst_frames1 = 0;
166 new_sample = rate->old_sample[channel];
167 pos = get_threshold;
168 while (dst_frames1 < dst_frames) {
169 if (pos >= get_threshold) {
170 pos -= get_threshold;
171 old_sample = new_sample;
172 if (src_frames1 < src_frames)
173 new_sample = *src;
174 }
175 new_weight = (pos << (16 - rate->pitch_shift)) / (get_threshold >> rate->pitch_shift);
176 old_weight = 0x10000 - new_weight;
177 *dst = (old_sample * old_weight + new_sample * new_weight) >> 16;
178 dst += dst_step;
179 dst_frames1++;
180 pos += LINEAR_DIV;
181 if (pos >= get_threshold) {
182 src += src_step;
183 src_frames1++;
184 }
185 }
186 rate->old_sample[channel] = new_sample;
187 }
188 }
189
linear_shrink(struct rate_linear * rate,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)190 static void linear_shrink(struct rate_linear *rate,
191 const snd_pcm_channel_area_t *dst_areas,
192 snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
193 const snd_pcm_channel_area_t *src_areas,
194 snd_pcm_uframes_t src_offset, unsigned int src_frames)
195 {
196 #define GET16_LABELS
197 #define PUT16_LABELS
198 #include "plugin_ops.h"
199 #undef GET16_LABELS
200 #undef PUT16_LABELS
201 void *get = get16_labels[rate->get_idx];
202 void *put = put16_labels[rate->put_idx];
203 unsigned int get_increment = rate->pitch;
204 unsigned int channel;
205 unsigned int src_frames1;
206 unsigned int dst_frames1;
207 int16_t sample = 0;
208 unsigned int pos;
209
210 for (channel = 0; channel < rate->channels; ++channel) {
211 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
212 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
213 const char *src;
214 char *dst;
215 int src_step, dst_step;
216 int16_t old_sample = 0;
217 int16_t new_sample = 0;
218 int old_weight, new_weight;
219 pos = LINEAR_DIV - get_increment; /* Force first sample to be copied */
220 src = snd_pcm_channel_area_addr(src_area, src_offset);
221 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
222 src_step = snd_pcm_channel_area_step(src_area);
223 dst_step = snd_pcm_channel_area_step(dst_area);
224 src_frames1 = 0;
225 dst_frames1 = 0;
226 while (src_frames1 < src_frames) {
227
228 goto *get;
229 #define GET16_END after_get
230 #include "plugin_ops.h"
231 #undef GET16_END
232 after_get:
233 new_sample = sample;
234 src += src_step;
235 src_frames1++;
236 pos += get_increment;
237 if (pos >= LINEAR_DIV) {
238 pos -= LINEAR_DIV;
239 old_weight = (pos << (32 - LINEAR_DIV_SHIFT)) / (get_increment >> (LINEAR_DIV_SHIFT - 16));
240 new_weight = 0x10000 - old_weight;
241 sample = (old_sample * old_weight + new_sample * new_weight) >> 16;
242 goto *put;
243 #define PUT16_END after_put
244 #include "plugin_ops.h"
245 #undef PUT16_END
246 after_put:
247 dst += dst_step;
248 dst_frames1++;
249 if (CHECK_SANITY(dst_frames1 > dst_frames)) {
250 SNDERR("dst_frames overflow");
251 break;
252 }
253 }
254 old_sample = new_sample;
255 }
256 }
257 }
258
259 /* optimized version for S16 format */
linear_shrink_s16(struct rate_linear * rate,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)260 static void linear_shrink_s16(struct rate_linear *rate,
261 const snd_pcm_channel_area_t *dst_areas,
262 snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
263 const snd_pcm_channel_area_t *src_areas,
264 snd_pcm_uframes_t src_offset, unsigned int src_frames)
265 {
266 unsigned int get_increment = rate->pitch;
267 unsigned int channel;
268 unsigned int src_frames1;
269 unsigned int dst_frames1;
270 unsigned int pos = 0;
271
272 for (channel = 0; channel < rate->channels; ++channel) {
273 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
274 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
275 const int16_t *src;
276 int16_t *dst;
277 int src_step, dst_step;
278 int16_t old_sample = 0;
279 int16_t new_sample = 0;
280 int old_weight, new_weight;
281 pos = LINEAR_DIV - get_increment; /* Force first sample to be copied */
282 src = snd_pcm_channel_area_addr(src_area, src_offset);
283 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
284 src_step = snd_pcm_channel_area_step(src_area) >> 1;
285 dst_step = snd_pcm_channel_area_step(dst_area) >> 1 ;
286 src_frames1 = 0;
287 dst_frames1 = 0;
288 while (src_frames1 < src_frames) {
289
290 new_sample = *src;
291 src += src_step;
292 src_frames1++;
293 pos += get_increment;
294 if (pos >= LINEAR_DIV) {
295 pos -= LINEAR_DIV;
296 old_weight = (pos << (32 - LINEAR_DIV_SHIFT)) / (get_increment >> (LINEAR_DIV_SHIFT - 16));
297 new_weight = 0x10000 - old_weight;
298 *dst = (old_sample * old_weight + new_sample * new_weight) >> 16;
299 dst += dst_step;
300 dst_frames1++;
301 if (CHECK_SANITY(dst_frames1 > dst_frames)) {
302 SNDERR("dst_frames overflow");
303 break;
304 }
305 }
306 old_sample = new_sample;
307 }
308 }
309 }
310
linear_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)311 static void linear_convert(void *obj,
312 const snd_pcm_channel_area_t *dst_areas,
313 snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
314 const snd_pcm_channel_area_t *src_areas,
315 snd_pcm_uframes_t src_offset, unsigned int src_frames)
316 {
317 struct rate_linear *rate = obj;
318 rate->func(rate, dst_areas, dst_offset, dst_frames,
319 src_areas, src_offset, src_frames);
320 }
321
linear_free(void * obj)322 static void linear_free(void *obj)
323 {
324 struct rate_linear *rate = obj;
325
326 free(rate->old_sample);
327 rate->old_sample = NULL;
328 }
329
linear_init(void * obj,snd_pcm_rate_info_t * info)330 static int linear_init(void *obj, snd_pcm_rate_info_t *info)
331 {
332 struct rate_linear *rate = obj;
333
334 rate->get_idx = snd_pcm_linear_get_index(info->in.format, SND_PCM_FORMAT_S16);
335 rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, info->out.format);
336 if (info->in.rate < info->out.rate) {
337 if (info->in.format == info->out.format && info->in.format == SND_PCM_FORMAT_S16)
338 rate->func = linear_expand_s16;
339 else
340 rate->func = linear_expand;
341 /* pitch is get_threshold */
342 } else {
343 if (info->in.format == info->out.format && info->in.format == SND_PCM_FORMAT_S16)
344 rate->func = linear_shrink_s16;
345 else
346 rate->func = linear_shrink;
347 /* pitch is get_increment */
348 }
349 rate->pitch = (((uint64_t)info->out.rate * LINEAR_DIV) +
350 (info->in.rate / 2)) / info->in.rate;
351 rate->channels = info->channels;
352
353 free(rate->old_sample);
354 rate->old_sample = malloc(sizeof(*rate->old_sample) * rate->channels);
355 if (! rate->old_sample)
356 return -ENOMEM;
357
358 return 0;
359 }
360
linear_adjust_pitch(void * obj,snd_pcm_rate_info_t * info)361 static int linear_adjust_pitch(void *obj, snd_pcm_rate_info_t *info)
362 {
363 struct rate_linear *rate = obj;
364 snd_pcm_uframes_t cframes;
365
366 rate->pitch = (((uint64_t)info->out.period_size * LINEAR_DIV) +
367 (info->in.period_size/2) ) / info->in.period_size;
368
369 cframes = input_frames(rate, info->out.period_size);
370 while (cframes != info->in.period_size) {
371 snd_pcm_uframes_t cframes_new;
372 if (cframes > info->in.period_size)
373 rate->pitch++;
374 else
375 rate->pitch--;
376 cframes_new = input_frames(rate, info->out.period_size);
377 if ((cframes > info->in.period_size && cframes_new < info->in.period_size) ||
378 (cframes < info->in.period_size && cframes_new > info->in.period_size)) {
379 SNDERR("invalid pcm period_size %ld -> %ld",
380 info->in.period_size, info->out.period_size);
381 return -EIO;
382 }
383 cframes = cframes_new;
384 }
385 if (rate->pitch >= LINEAR_DIV) {
386 /* shift for expand linear interpolation */
387 rate->pitch_shift = 0;
388 while ((rate->pitch >> rate->pitch_shift) >= (1 << 16))
389 rate->pitch_shift++;
390 }
391 return 0;
392 }
393
linear_reset(void * obj)394 static void linear_reset(void *obj)
395 {
396 struct rate_linear *rate = obj;
397
398 /* for expand */
399 if (rate->old_sample)
400 memset(rate->old_sample, 0, sizeof(*rate->old_sample) * rate->channels);
401 }
402
linear_close(void * obj)403 static void linear_close(void *obj)
404 {
405 free(obj);
406 }
407
get_supported_rates(ATTRIBUTE_UNUSED void * rate,unsigned int * rate_min,unsigned int * rate_max)408 static int get_supported_rates(ATTRIBUTE_UNUSED void *rate,
409 unsigned int *rate_min, unsigned int *rate_max)
410 {
411 *rate_min = SND_PCM_PLUGIN_RATE_MIN;
412 *rate_max = SND_PCM_PLUGIN_RATE_MAX;
413 return 0;
414 }
415
linear_dump(ATTRIBUTE_UNUSED void * rate,snd_output_t * out)416 static void linear_dump(ATTRIBUTE_UNUSED void *rate, snd_output_t *out)
417 {
418 snd_output_printf(out, "Converter: linear-interpolation\n");
419 }
420
421 static const snd_pcm_rate_ops_t linear_ops = {
422 .close = linear_close,
423 .init = linear_init,
424 .free = linear_free,
425 .reset = linear_reset,
426 .adjust_pitch = linear_adjust_pitch,
427 .convert = linear_convert,
428 .input_frames = input_frames,
429 .output_frames = output_frames,
430 .version = SND_PCM_RATE_PLUGIN_VERSION,
431 .get_supported_rates = get_supported_rates,
432 .dump = linear_dump,
433 };
434
SND_PCM_RATE_PLUGIN_ENTRY(linear)435 int SND_PCM_RATE_PLUGIN_ENTRY(linear) (ATTRIBUTE_UNUSED unsigned int version,
436 void **objp, snd_pcm_rate_ops_t *ops)
437 {
438 struct rate_linear *rate;
439
440 rate = calloc(1, sizeof(*rate));
441 if (! rate)
442 return -ENOMEM;
443
444 *objp = rate;
445 *ops = linear_ops;
446 return 0;
447 }
448