1 /* Copyright (c) 2014 The Chromium OS Author. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include "cras_audio_area.h"
7 #include "cras_util.h"
8 #include "linear_resampler.h"
9
10 /* A linear resampler.
11 * Members:
12 * num_channels - The number of channles in once frames.
13 * format_bytes - The size of one frame in bytes.
14 * src_offset - The accumulated offset for resampled src data.
15 * dst_offset - The accumulated offset for resampled dst data.
16 * to_times_100 - The numerator of the rate factor used for SRC.
17 * from_times_100 - The denominator of the rate factor used for SRC.
18 * f - The rate factor used for linear resample.
19 */
20 struct linear_resampler {
21 unsigned int num_channels;
22 unsigned int format_bytes;
23 unsigned int src_offset;
24 unsigned int dst_offset;
25 unsigned int to_times_100;
26 unsigned int from_times_100;
27 float f;
28 };
29
linear_resampler_create(unsigned int num_channels,unsigned int format_bytes,float src_rate,float dst_rate)30 struct linear_resampler *linear_resampler_create(unsigned int num_channels,
31 unsigned int format_bytes,
32 float src_rate,
33 float dst_rate)
34 {
35 struct linear_resampler *lr;
36
37 lr = (struct linear_resampler *)calloc(1, sizeof(*lr));
38 if (!lr)
39 return NULL;
40 lr->num_channels = num_channels;
41 lr->format_bytes = format_bytes;
42
43 linear_resampler_set_rates(lr, src_rate, dst_rate);
44
45 return lr;
46 }
47
linear_resampler_destroy(struct linear_resampler * lr)48 void linear_resampler_destroy(struct linear_resampler *lr)
49 {
50 if (lr)
51 free(lr);
52 }
53
linear_resampler_set_rates(struct linear_resampler * lr,float from,float to)54 void linear_resampler_set_rates(struct linear_resampler *lr,
55 float from, float to)
56 {
57 lr->f = (float)to / from;
58 lr->to_times_100 = to * 100;
59 lr->from_times_100 = from * 100;
60 lr->src_offset = 0;
61 lr->dst_offset = 0;
62 }
63
64 /* Assuming the linear resampler transforms X frames of input buffer into
65 * Y frames of output buffer. The resample method requires the last output
66 * buffer at Y-1 be interpolated from input buffer in range (X-d, X-1) as
67 * illustrated.
68 * Input Index: ... X-1 <--floor--| X
69 * Output Index: ... Y-1 |--ceiling-> Y
70 *
71 * That said, the calculation between input and output frames is based on
72 * equations X-1 = floor(Y/f) and Y = ceil((X-1)*f). Note that in any case
73 * when the resampled frames number isn't sufficient to consume the first
74 * buffer at input or output offset(index 0), always count as one buffer
75 * used so the intput/output offset can always increment.
76 */
linear_resampler_out_frames_to_in(struct linear_resampler * lr,unsigned int frames)77 unsigned int linear_resampler_out_frames_to_in(struct linear_resampler *lr,
78 unsigned int frames)
79 {
80 float in_frames;
81 if (frames == 0)
82 return 0;
83
84 in_frames = (float)(lr->dst_offset + frames) / lr->f;
85 if ((in_frames > lr->src_offset))
86 return 1 + (unsigned int)(in_frames - lr->src_offset);
87 else
88 return 1;
89 }
90
linear_resampler_in_frames_to_out(struct linear_resampler * lr,unsigned int frames)91 unsigned int linear_resampler_in_frames_to_out(struct linear_resampler *lr,
92 unsigned int frames)
93 {
94 float out_frames;
95 if (frames == 0)
96 return 0;
97
98 out_frames = lr->f * (lr->src_offset + frames - 1);
99 if (out_frames > lr->dst_offset)
100 return 1 + (unsigned int)(out_frames - lr->dst_offset);
101 else
102 return 1;
103 }
104
linear_resampler_needed(struct linear_resampler * lr)105 int linear_resampler_needed(struct linear_resampler *lr)
106 {
107 return lr->from_times_100 != lr->to_times_100;
108 }
109
linear_resampler_resample(struct linear_resampler * lr,uint8_t * src,unsigned int * src_frames,uint8_t * dst,unsigned dst_frames)110 unsigned int linear_resampler_resample(struct linear_resampler *lr,
111 uint8_t *src,
112 unsigned int *src_frames,
113 uint8_t *dst,
114 unsigned dst_frames)
115 {
116 int ch;
117 unsigned int src_idx = 0;
118 unsigned int dst_idx = 0;
119 float src_pos;
120 int16_t *in, *out;
121
122 /* Check for corner cases so that we can assume both src_idx and
123 * dst_idx are valid with value 0 in the loop below. */
124 if (dst_frames == 0 || *src_frames == 0) {
125 *src_frames = 0;
126 return 0;
127 }
128
129 for (dst_idx = 0; dst_idx <= dst_frames; dst_idx++) {
130 src_pos = (float)(lr->dst_offset + dst_idx) / lr->f;
131 if (src_pos > lr->src_offset)
132 src_pos -= lr->src_offset;
133 else
134 src_pos = 0;
135 src_idx = (unsigned int)src_pos;
136
137 if (src_pos > *src_frames - 1 || dst_idx >= dst_frames) {
138 if (src_pos > *src_frames - 1)
139 src_idx = *src_frames - 1;
140 /* When this loop stops, dst_idx is always at the last
141 * used index incremented by 1. */
142 break;
143 }
144
145 in = (int16_t *)(src + src_idx * lr->format_bytes);
146 out = (int16_t *)(dst + dst_idx * lr->format_bytes);
147
148 /* Don't do linear interpolcation if src_pos falls on the
149 * last index. */
150 if (src_idx == *src_frames - 1) {
151 for (ch = 0; ch < lr->num_channels; ch++)
152 out[ch] = in[ch];
153 } else {
154 for (ch = 0; ch < lr->num_channels; ch++) {
155 out[ch] = in[ch] + (src_pos - src_idx) *
156 (in[lr->num_channels + ch] - in[ch]);
157 }
158 }
159
160 }
161
162 *src_frames = src_idx + 1;
163
164 lr->src_offset += *src_frames;
165 lr->dst_offset += dst_idx;
166 while ((lr->src_offset > lr->from_times_100) &&
167 (lr->dst_offset > lr->to_times_100)) {
168 lr->src_offset -= lr->from_times_100;
169 lr->dst_offset -= lr->to_times_100;
170 }
171
172 return dst_idx;
173 }
174