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