2 * Linear rate converter plugin
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>
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "pcm_local.h"
26 #include "pcm_plugin.h"
29 #include "plugin_ops.h"
32 /* LINEAR_DIV needs to be large enough to handle resampling from 192000 -> 8000 */
33 #define LINEAR_DIV_SHIFT 19
34 #define LINEAR_DIV (1<<LINEAR_DIV_SHIFT)
40 unsigned int pitch_shift; /* for expand interpolation */
41 unsigned int channels;
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);
50 static snd_pcm_uframes_t input_frames(void *obj, snd_pcm_uframes_t frames)
52 struct rate_linear *rate = obj;
55 /* Round toward zero */
56 return muldiv_near(frames, LINEAR_DIV, rate->pitch);
59 static snd_pcm_uframes_t output_frames(void *obj, snd_pcm_uframes_t frames)
61 struct rate_linear *rate = obj;
64 /* Round toward zero */
65 return muldiv_near(frames, rate->pitch, LINEAR_DIV);
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)
76 #include "plugin_ops.h"
79 void *get = get16_labels[rate->get_idx];
80 void *put = put16_labels[rate->put_idx];
81 unsigned int get_threshold = rate->pitch;
83 unsigned int src_frames1;
84 unsigned int dst_frames1;
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];
93 int src_step, dst_step;
94 int16_t old_sample = 0;
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);
103 new_sample = rate->old_sample[channel];
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) {
111 #define GET16_END after_get
112 #include "plugin_ops.h"
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;
122 #define PUT16_END after_put
123 #include "plugin_ops.h"
129 if (pos >= get_threshold) {
134 rate->old_sample[channel] = new_sample;
138 /* optimized version for S16 format */
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)
145 unsigned int channel;
146 unsigned int src_frames1;
147 unsigned int dst_frames1;
148 unsigned int get_threshold = rate->pitch;
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];
156 int src_step, dst_step;
157 int16_t old_sample = 0;
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;
166 new_sample = rate->old_sample[channel];
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)
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;
181 if (pos >= get_threshold) {
186 rate->old_sample[channel] = new_sample;
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)
198 #include "plugin_ops.h"
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;
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];
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);
226 while (src_frames1 < src_frames) {
229 #define GET16_END after_get
230 #include "plugin_ops.h"
236 pos += get_increment;
237 if (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;
243 #define PUT16_END after_put
244 #include "plugin_ops.h"
249 if (CHECK_SANITY(dst_frames1 > dst_frames)) {
250 SNDERR("dst_frames overflow");
254 old_sample = new_sample;
259 /* optimized version for S16 format */
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)
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;
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];
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 ;
288 while (src_frames1 < src_frames) {
293 pos += get_increment;
294 if (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;
301 if (CHECK_SANITY(dst_frames1 > dst_frames)) {
302 SNDERR("dst_frames overflow");
306 old_sample = new_sample;
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)
317 struct rate_linear *rate = obj;
318 rate->func(rate, dst_areas, dst_offset, dst_frames,
319 src_areas, src_offset, src_frames);
322 static void linear_free(void *obj)
324 struct rate_linear *rate = obj;
326 free(rate->old_sample);
327 rate->old_sample = NULL;
330 static int linear_init(void *obj, snd_pcm_rate_info_t *info)
332 struct rate_linear *rate = obj;
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;
340 rate->func = linear_expand;
341 /* pitch is get_threshold */
343 if (info->in.format == info->out.format && info->in.format == SND_PCM_FORMAT_S16)
344 rate->func = linear_shrink_s16;
346 rate->func = linear_shrink;
347 /* pitch is get_increment */
349 rate->pitch = (((u_int64_t)info->out.rate * LINEAR_DIV) +
350 (info->in.rate / 2)) / info->in.rate;
351 rate->channels = info->channels;
353 free(rate->old_sample);
354 rate->old_sample = malloc(sizeof(*rate->old_sample) * rate->channels);
355 if (! rate->old_sample)
361 static int linear_adjust_pitch(void *obj, snd_pcm_rate_info_t *info)
363 struct rate_linear *rate = obj;
364 snd_pcm_uframes_t cframes;
366 rate->pitch = (((u_int64_t)info->out.period_size * LINEAR_DIV) +
367 (info->in.period_size/2) ) / info->in.period_size;
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)
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);
383 cframes = cframes_new;
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))
394 static void linear_reset(void *obj)
396 struct rate_linear *rate = obj;
399 if (rate->old_sample)
400 memset(rate->old_sample, 0, sizeof(*rate->old_sample) * rate->channels);
403 static void linear_close(void *obj)
408 static int get_supported_rates(ATTRIBUTE_UNUSED void *rate,
409 unsigned int *rate_min, unsigned int *rate_max)
411 *rate_min = SND_PCM_PLUGIN_RATE_MIN;
412 *rate_max = SND_PCM_PLUGIN_RATE_MAX;
416 static void linear_dump(ATTRIBUTE_UNUSED void *rate, snd_output_t *out)
418 snd_output_printf(out, "Converter: linear-interpolation\n");
421 static const snd_pcm_rate_ops_t linear_ops = {
422 .close = linear_close,
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,
435 int SND_PCM_RATE_PLUGIN_ENTRY(linear) (ATTRIBUTE_UNUSED unsigned int version,
436 void **objp, snd_pcm_rate_ops_t *ops)
438 struct rate_linear *rate;
440 rate = calloc(1, sizeof(*rate));