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