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, float dst_rate)
33 {
34 struct linear_resampler *lr;
35
36 lr = (struct linear_resampler *)calloc(1, sizeof(*lr));
37 if (!lr)
38 return NULL;
39 lr->num_channels = num_channels;
40 lr->format_bytes = format_bytes;
41
42 linear_resampler_set_rates(lr, src_rate, dst_rate);
43
44 return lr;
45 }
46
linear_resampler_destroy(struct linear_resampler * lr)47 void linear_resampler_destroy(struct linear_resampler *lr)
48 {
49 if (lr)
50 free(lr);
51 }
52
linear_resampler_set_rates(struct linear_resampler * lr,float from,float to)53 void linear_resampler_set_rates(struct linear_resampler *lr, float from,
54 float to)
55 {
56 lr->f = (float)to / from;
57 lr->to_times_100 = to * 100;
58 lr->from_times_100 = from * 100;
59 lr->src_offset = 0;
60 lr->dst_offset = 0;
61 }
62
63 /* Assuming the linear resampler transforms X frames of input buffer into
64 * Y frames of output buffer. The resample method requires the last output
65 * buffer at Y-1 be interpolated from input buffer in range (X-d, X-1) as
66 * illustrated.
67 * Input Index: ... X-1 <--floor--| X
68 * Output Index: ... Y-1 |--ceiling-> Y
69 *
70 * That said, the calculation between input and output frames is based on
71 * equations X-1 = floor(Y/f) and Y = ceil((X-1)*f). Note that in any case
72 * when the resampled frames number isn't sufficient to consume the first
73 * buffer at input or output offset(index 0), always count as one buffer
74 * used so the intput/output offset can always increment.
75 */
linear_resampler_out_frames_to_in(struct linear_resampler * lr,unsigned int frames)76 unsigned int linear_resampler_out_frames_to_in(struct linear_resampler *lr,
77 unsigned int frames)
78 {
79 float in_frames;
80 if (frames == 0)
81 return 0;
82
83 in_frames = (float)(lr->dst_offset + frames) / lr->f;
84 if ((in_frames > lr->src_offset))
85 return 1 + (unsigned int)(in_frames - lr->src_offset);
86 else
87 return 1;
88 }
89
linear_resampler_in_frames_to_out(struct linear_resampler * lr,unsigned int frames)90 unsigned int linear_resampler_in_frames_to_out(struct linear_resampler *lr,
91 unsigned int frames)
92 {
93 float out_frames;
94 if (frames == 0)
95 return 0;
96
97 out_frames = lr->f * (lr->src_offset + frames - 1);
98 if (out_frames > lr->dst_offset)
99 return 1 + (unsigned int)(out_frames - lr->dst_offset);
100 else
101 return 1;
102 }
103
linear_resampler_needed(struct linear_resampler * lr)104 int linear_resampler_needed(struct linear_resampler *lr)
105 {
106 return lr->from_times_100 != lr->to_times_100;
107 }
108
linear_resampler_resample(struct linear_resampler * lr,uint8_t * src,unsigned int * src_frames,uint8_t * dst,unsigned dst_frames)109 unsigned int linear_resampler_resample(struct linear_resampler *lr,
110 uint8_t *src, unsigned int *src_frames,
111 uint8_t *dst, unsigned dst_frames)
112 {
113 int ch;
114 unsigned int src_idx = 0;
115 unsigned int dst_idx = 0;
116 float src_pos;
117 int16_t *in, *out;
118
119 /* Check for corner cases so that we can assume both src_idx and
120 * dst_idx are valid with value 0 in the loop below. */
121 if (dst_frames == 0 || *src_frames == 0) {
122 *src_frames = 0;
123 return 0;
124 }
125
126 for (dst_idx = 0; dst_idx <= dst_frames; dst_idx++) {
127 src_pos = (float)(lr->dst_offset + dst_idx) / lr->f;
128 if (src_pos > lr->src_offset)
129 src_pos -= lr->src_offset;
130 else
131 src_pos = 0;
132 src_idx = (unsigned int)src_pos;
133
134 if (src_pos > *src_frames - 1 || dst_idx >= dst_frames) {
135 if (src_pos > *src_frames - 1)
136 src_idx = *src_frames - 1;
137 /* When this loop stops, dst_idx is always at the last
138 * used index incremented by 1. */
139 break;
140 }
141
142 in = (int16_t *)(src + src_idx * lr->format_bytes);
143 out = (int16_t *)(dst + dst_idx * lr->format_bytes);
144
145 /* Don't do linear interpolcation if src_pos falls on the
146 * last index. */
147 if (src_idx == *src_frames - 1) {
148 for (ch = 0; ch < lr->num_channels; ch++)
149 out[ch] = in[ch];
150 } else {
151 for (ch = 0; ch < lr->num_channels; ch++) {
152 out[ch] = in[ch] +
153 (src_pos - src_idx) *
154 (in[lr->num_channels + ch] -
155 in[ch]);
156 }
157 }
158 }
159
160 *src_frames = src_idx + 1;
161
162 lr->src_offset += *src_frames;
163 lr->dst_offset += dst_idx;
164 while ((lr->src_offset > lr->from_times_100) &&
165 (lr->dst_offset > lr->to_times_100)) {
166 lr->src_offset -= lr->from_times_100;
167 lr->dst_offset -= lr->to_times_100;
168 }
169
170 return dst_idx;
171 }
172