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 35struct 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 49static 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 58static 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 67static 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 */ 138static 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 189static 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 */ 259static 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 310static 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 321static 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 329static 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 360static 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 393static 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 402static void linear_close(void *obj) 403{ 404 free(obj); 405} 406 407static 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 415static void linear_dump(ATTRIBUTE_UNUSED void *rate, snd_output_t *out) 416{ 417 snd_output_printf(out, "Converter: linear-interpolation\n"); 418} 419 420static 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 434int 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