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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23#include <inttypes.h> 24#include <byteswap.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 192000 -> 8000 */ 33#define LINEAR_DIV_SHIFT 19 34#define LINEAR_DIV (1<<LINEAR_DIV_SHIFT) 35 36struct 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 50static 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 59static 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 68static 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 */ 139static 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 190static 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 */ 260static 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 311static 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 322static 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 330static 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 = (((u_int64_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 361static 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 = (((u_int64_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 394static 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 403static void linear_close(void *obj) 404{ 405 free(obj); 406} 407 408static 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 416static void linear_dump(ATTRIBUTE_UNUSED void *rate, snd_output_t *out) 417{ 418 snd_output_printf(out, "Converter: linear-interpolation\n"); 419} 420 421static 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 435int 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