18ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao/* Copyright (c) 2014 The Chromium OS Author. All rights reserved. 28ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao * Use of this source code is governed by a BSD-style license that can be 38ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao * found in the LICENSE file. 48ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao */ 58ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 68ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao#include "cras_audio_area.h" 7b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao#include "cras_util.h" 88ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao#include "linear_resampler.h" 98ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 108ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao/* A linear resampler. 118ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao * Members: 128ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao * num_channels - The number of channles in once frames. 138ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao * format_bytes - The size of one frame in bytes. 148ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao * src_offset - The accumulated offset for resampled src data. 158ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao * dst_offset - The accumulated offset for resampled dst data. 16f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao * to_times_100 - The numerator of the rate factor used for SRC. 17f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao * from_times_100 - The denominator of the rate factor used for SRC. 18084401521b297857987b2b3ece787c16cacdbbccHsin-Yu Chao * f - The rate factor used for linear resample. 198ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao */ 208ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chaostruct linear_resampler { 218ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao unsigned int num_channels; 228ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao unsigned int format_bytes; 238ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao unsigned int src_offset; 248ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao unsigned int dst_offset; 25f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao unsigned int to_times_100; 26f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao unsigned int from_times_100; 278ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao float f; 288ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao}; 298ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 308ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chaostruct linear_resampler *linear_resampler_create(unsigned int num_channels, 318ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao unsigned int format_bytes, 32084401521b297857987b2b3ece787c16cacdbbccHsin-Yu Chao float src_rate, 33084401521b297857987b2b3ece787c16cacdbbccHsin-Yu Chao float dst_rate) 348ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao{ 358ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao struct linear_resampler *lr; 368ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 378ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao lr = (struct linear_resampler *)calloc(1, sizeof(*lr)); 3866327949f776c34ccdcf8704d08627f07445819aCheng-Yi Chiang if (!lr) 3966327949f776c34ccdcf8704d08627f07445819aCheng-Yi Chiang return NULL; 408ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao lr->num_channels = num_channels; 418ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao lr->format_bytes = format_bytes; 428ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 43084401521b297857987b2b3ece787c16cacdbbccHsin-Yu Chao linear_resampler_set_rates(lr, src_rate, dst_rate); 448ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 458ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao return lr; 468ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao} 478ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 488ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chaovoid linear_resampler_destroy(struct linear_resampler *lr) 498ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao{ 508ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao if (lr) 518ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao free(lr); 528ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao} 538ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 548ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chaovoid linear_resampler_set_rates(struct linear_resampler *lr, 55084401521b297857987b2b3ece787c16cacdbbccHsin-Yu Chao float from, float to) 568ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao{ 578ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao lr->f = (float)to / from; 58f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao lr->to_times_100 = to * 100; 59f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao lr->from_times_100 = from * 100; 608ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao lr->src_offset = 0; 618ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao lr->dst_offset = 0; 628ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao} 638ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 64e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao/* Assuming the linear resampler transforms X frames of input buffer into 65e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * Y frames of output buffer. The resample method requires the last output 66e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * buffer at Y-1 be interpolated from input buffer in range (X-d, X-1) as 67e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * illustrated. 68e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * Input Index: ... X-1 <--floor--| X 69e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * Output Index: ... Y-1 |--ceiling-> Y 70e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * 71e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * That said, the calculation between input and output frames is based on 72e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * equations X-1 = floor(Y/f) and Y = ceil((X-1)*f). Note that in any case 73e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * when the resampled frames number isn't sufficient to consume the first 74e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * buffer at input or output offset(index 0), always count as one buffer 75e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao * used so the intput/output offset can always increment. 76e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao */ 77b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chaounsigned int linear_resampler_out_frames_to_in(struct linear_resampler *lr, 78b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao unsigned int frames) 79b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao{ 80e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao float in_frames; 81e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao if (frames == 0) 82e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao return 0; 83e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao 84e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao in_frames = (float)(lr->dst_offset + frames) / lr->f; 85e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao if ((in_frames > lr->src_offset)) 86e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao return 1 + (unsigned int)(in_frames - lr->src_offset); 87e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao else 88e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao return 1; 89b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao} 90b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao 91b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chaounsigned int linear_resampler_in_frames_to_out(struct linear_resampler *lr, 92b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao unsigned int frames) 93b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao{ 94e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao float out_frames; 95e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao if (frames == 0) 96e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao return 0; 97e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao 98e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao out_frames = lr->f * (lr->src_offset + frames - 1); 99e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao if (out_frames > lr->dst_offset) 100e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao return 1 + (unsigned int)(out_frames - lr->dst_offset); 101e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao else 102e3208d0c067f7d1339787ff3b6686e05ded995e3Hsin-Yu Chao return 1; 103b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao} 104b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao 105b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chaoint linear_resampler_needed(struct linear_resampler *lr) 106b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao{ 107f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao return lr->from_times_100 != lr->to_times_100; 108b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao} 109b3ebd1d69038925d04ba743aebfdbb8b9cf71d9bHsin-Yu Chao 1108ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chaounsigned int linear_resampler_resample(struct linear_resampler *lr, 1118ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao uint8_t *src, 1128ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao unsigned int *src_frames, 1138ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao uint8_t *dst, 1148ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao unsigned dst_frames) 1158ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao{ 1168ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao int ch; 1178ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao unsigned int src_idx = 0; 1188ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao unsigned int dst_idx = 0; 1198ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao float src_pos; 1208ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao int16_t *in, *out; 1218ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 1221dfe471f6c3fa82dbcb56310a37f47e2f98b3b98Hsin-Yu Chao /* Check for corner cases so that we can assume both src_idx and 1231dfe471f6c3fa82dbcb56310a37f47e2f98b3b98Hsin-Yu Chao * dst_idx are valid with value 0 in the loop below. */ 1241dfe471f6c3fa82dbcb56310a37f47e2f98b3b98Hsin-Yu Chao if (dst_frames == 0 || *src_frames == 0) { 1251dfe471f6c3fa82dbcb56310a37f47e2f98b3b98Hsin-Yu Chao *src_frames = 0; 1261dfe471f6c3fa82dbcb56310a37f47e2f98b3b98Hsin-Yu Chao return 0; 1271dfe471f6c3fa82dbcb56310a37f47e2f98b3b98Hsin-Yu Chao } 1288ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 1291dfe471f6c3fa82dbcb56310a37f47e2f98b3b98Hsin-Yu Chao for (dst_idx = 0; dst_idx <= dst_frames; dst_idx++) { 1308ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao src_pos = (float)(lr->dst_offset + dst_idx) / lr->f; 1318ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao if (src_pos > lr->src_offset) 1328ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao src_pos -= lr->src_offset; 1338ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao else 1348ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao src_pos = 0; 1358ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao src_idx = (unsigned int)src_pos; 1368ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 1378ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao if (src_pos > *src_frames - 1 || dst_idx >= dst_frames) { 1388ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao if (src_pos > *src_frames - 1) 1398ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao src_idx = *src_frames - 1; 1401dfe471f6c3fa82dbcb56310a37f47e2f98b3b98Hsin-Yu Chao /* When this loop stops, dst_idx is always at the last 1411dfe471f6c3fa82dbcb56310a37f47e2f98b3b98Hsin-Yu Chao * used index incremented by 1. */ 1428ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao break; 1438ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao } 1448ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 1458ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao in = (int16_t *)(src + src_idx * lr->format_bytes); 1468ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao out = (int16_t *)(dst + dst_idx * lr->format_bytes); 1478ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 148ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao /* Don't do linear interpolcation if src_pos falls on the 149ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao * last index. */ 150ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao if (src_idx == *src_frames - 1) { 151ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao for (ch = 0; ch < lr->num_channels; ch++) 152ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao out[ch] = in[ch]; 153ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao } else { 154ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao for (ch = 0; ch < lr->num_channels; ch++) { 155ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao out[ch] = in[ch] + (src_pos - src_idx) * 1568ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao (in[lr->num_channels + ch] - in[ch]); 157ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao } 1588ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao } 159ceea696aa34a404032555f9a169f2f7c84438e91Hsin-Yu Chao 1608ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao } 1618ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 1628ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao *src_frames = src_idx + 1; 1638ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 1648ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao lr->src_offset += *src_frames; 1658ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao lr->dst_offset += dst_idx; 166f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao while ((lr->src_offset > lr->from_times_100) && 167f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao (lr->dst_offset > lr->to_times_100)) { 168f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao lr->src_offset -= lr->from_times_100; 169f78e68857e174b7e6ec45377e31edaa63e09dc9aHsin-Yu Chao lr->dst_offset -= lr->to_times_100; 1708ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao } 1718ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao 1728ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao return dst_idx; 1738ddfd645c98089cf6631bef2c4cb79daa92b2757Hsin-Yu Chao} 174