blob: 7f1066fd2d387ca8e74b973912ddd3875eac9d48 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <pjmedia/resample.h>
22
23#include <pjmedia/errno.h>
24#include <pj/assert.h>
25#include <pj/log.h>
26#include <pj/pool.h>
27
28
29#if PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_LIBRESAMPLE
30
31#include <third_party/resample/include/resamplesubs.h>
32
33#define THIS_FILE "resample.c"
34
35
36
37struct pjmedia_resample
38{
39 double factor; /* Conversion factor = rate_out / rate_in. */
40 pj_bool_t large_filter; /* Large filter? */
41 pj_bool_t high_quality; /* Not fast? */
42 unsigned xoff; /* History and lookahead size, in samples */
43 unsigned frame_size; /* Samples per frame. */
44 unsigned channel_cnt; /* Channel count. */
45
46 /* Buffer for monochannel */
47 pj_int16_t *buffer; /* Input buffer. */
48
49 /* Buffer for multichannel */
50 pj_int16_t **in_buffer; /* Array of input buffer for each channel. */
51 pj_int16_t *tmp_buffer; /* Temporary output buffer for processing. */
52};
53
54
55PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool,
56 pj_bool_t high_quality,
57 pj_bool_t large_filter,
58 unsigned channel_count,
59 unsigned rate_in,
60 unsigned rate_out,
61 unsigned samples_per_frame,
62 pjmedia_resample **p_resample)
63{
64 pjmedia_resample *resample;
65
66 PJ_ASSERT_RETURN(pool && p_resample && rate_in &&
67 rate_out && samples_per_frame, PJ_EINVAL);
68
69 resample = PJ_POOL_ZALLOC_T(pool, pjmedia_resample);
70 PJ_ASSERT_RETURN(resample, PJ_ENOMEM);
71
72 /*
73 * If we're downsampling, always use the fast algorithm since it seems
74 * to yield the same quality.
75 */
76 if (rate_out < rate_in) {
77 //no this is not a good idea. It sounds pretty good with speech,
78 //but very poor with background noise etc.
79 //high_quality = 0;
80 }
81
82 resample->factor = rate_out * 1.0 / rate_in;
83 resample->large_filter = large_filter;
84 resample->high_quality = high_quality;
85 resample->channel_cnt = channel_count;
86 resample->frame_size = samples_per_frame;
87
88 if (high_quality) {
89 /* This is a bug in xoff calculation, thanks Stephane Lussier
90 * of Macadamian dot com.
91 * resample->xoff = large_filter ? 32 : 6;
92 */
93 resample->xoff = res_GetXOFF(resample->factor, (char)large_filter);
94 } else {
95 resample->xoff = 1;
96 }
97
98 if (channel_count == 1) {
99 unsigned size;
100
101 /* Allocate input buffer */
102 size = (samples_per_frame + 2*resample->xoff) * sizeof(pj_int16_t);
103 resample->buffer = (pj_int16_t*) pj_pool_alloc(pool, size);
104 PJ_ASSERT_RETURN(resample->buffer, PJ_ENOMEM);
105
106 pjmedia_zero_samples(resample->buffer, resample->xoff*2);
107
108 } else if (channel_count > 1) {
109 unsigned i, size;
110
111 /* Allocate input buffer table */
112 size = channel_count * sizeof(pj_int16_t*);
113 resample->in_buffer = (pj_int16_t**)pj_pool_alloc(pool, size);
114
115 /* Allocate input buffer */
116 size = (samples_per_frame/channel_count + 2*resample->xoff) *
117 sizeof(pj_int16_t);
118 for (i = 0; i < channel_count; ++i) {
119 resample->in_buffer[i] = (pj_int16_t*)pj_pool_alloc(pool, size);
120 PJ_ASSERT_RETURN(resample->in_buffer, PJ_ENOMEM);
121 pjmedia_zero_samples(resample->in_buffer[i], resample->xoff*2);
122 }
123
124 /* Allocate temporary output buffer */
125 size = (unsigned) (resample->frame_size * sizeof(pj_int16_t) *
126 resample->factor / channel_count + 0.5);
127 resample->tmp_buffer = (pj_int16_t*) pj_pool_alloc(pool, size);
128 PJ_ASSERT_RETURN(resample->tmp_buffer, PJ_ENOMEM);
129 }
130
131 *p_resample = resample;
132
133 PJ_LOG(5,(THIS_FILE, "resample created: %s qualiy, %s filter, in/out "
134 "rate=%d/%d",
135 (high_quality?"high":"low"),
136 (large_filter?"large":"small"),
137 rate_in, rate_out));
138 return PJ_SUCCESS;
139}
140
141
142
143PJ_DEF(void) pjmedia_resample_run( pjmedia_resample *resample,
144 const pj_int16_t *input,
145 pj_int16_t *output )
146{
147 PJ_ASSERT_ON_FAIL(resample, return);
148
149 /* Okay chaps, here's how we do resampling.
150 *
151 * The original resample algorithm requires xoff samples *before* the
152 * input buffer as history, and another xoff samples *after* the
153 * end of the input buffer as lookahead. Since application can only
154 * supply framesize buffer on each run, PJMEDIA needs to arrange the
155 * buffer to meet these requirements.
156 *
157 * So here comes the trick.
158 *
159 * First of all, because of the history and lookahead requirement,
160 * resample->buffer need to accomodate framesize+2*xoff samples in its
161 * buffer. This is done when the buffer is created.
162 *
163 * On the first run, the input frame (supplied by application) is
164 * copied to resample->buffer at 2*xoff position. The first 2*xoff
165 * samples are initially zeroed (in the initialization). The resample
166 * algorithm then invoked at resample->buffer+xoff ONLY, thus giving
167 * it one xoff at the beginning as zero, and one xoff at the end
168 * as the end of the original input. The resample algorithm will see
169 * that the first xoff samples in the input as zero.
170 *
171 * So here's the layout of resample->buffer on the first run.
172 *
173 * run 0
174 * +------+------+--------------+
175 * | 0000 | 0000 | frame0... |
176 * +------+------+--------------+
177 * ^ ^ ^ ^
178 * 0 xoff 2*xoff size+2*xoff
179 *
180 * (Note again: resample algorithm is called at resample->buffer+xoff)
181 *
182 * At the end of the run, 2*xoff samples from the end of
183 * resample->buffer are copied to the beginning of resample->buffer.
184 * The first xoff part of this will be used as history for the next
185 * run, and the second xoff part of this is actually the start of
186 * resampling for the next run.
187 *
188 * And the first run completes, the function returns.
189 *
190 *
191 * On the next run, the input frame supplied by application is again
192 * copied at 2*xoff position in the resample->buffer, and the
193 * resample algorithm is again invoked at resample->buffer+xoff
194 * position. So effectively, the resample algorithm will start its
195 * operation on the last xoff from the previous frame, and gets the
196 * history from the last 2*xoff of the previous frame, and the look-
197 * ahead from the last xoff of current frame.
198 *
199 * So on this run, the buffer layout is:
200 *
201 * run 1
202 * +------+------+--------------+
203 * | frm0 | frm0 | frame1... |
204 * +------+------+--------------+
205 * ^ ^ ^ ^
206 * 0 xoff 2*xoff size+2*xoff
207 *
208 * As you can see from above diagram, the resampling algorithm is
209 * actually called from the last xoff part of previous frame (frm0).
210 *
211 * And so on the process continues for the next frame, and the next,
212 * and the next, ...
213 *
214 */
215 if (resample->channel_cnt == 1) {
216 pj_int16_t *dst_buf;
217 const pj_int16_t *src_buf;
218
219 /* Prepare input frame */
220 dst_buf = resample->buffer + resample->xoff*2;
221 pjmedia_copy_samples(dst_buf, input, resample->frame_size);
222
223 /* Resample */
224 if (resample->high_quality) {
225 res_Resample(resample->buffer + resample->xoff, output,
226 resample->factor, (pj_uint16_t)resample->frame_size,
227 (char)resample->large_filter, (char)PJ_TRUE);
228 } else {
229 res_SrcLinear(resample->buffer + resample->xoff, output,
230 resample->factor, (pj_uint16_t)resample->frame_size);
231 }
232
233 /* Update history */
234 dst_buf = resample->buffer;
235 src_buf = input + resample->frame_size - resample->xoff*2;
236 pjmedia_copy_samples(dst_buf, src_buf, resample->xoff * 2);
237
238 } else { /* Multichannel */
239 unsigned i, j;
240
241 for (i = 0; i < resample->channel_cnt; ++i) {
242 pj_int16_t *dst_buf;
243 const pj_int16_t *src_buf;
244 unsigned mono_frm_sz_in;
245 unsigned mono_frm_sz_out;
246
247 mono_frm_sz_in = resample->frame_size / resample->channel_cnt;
248 mono_frm_sz_out = (unsigned)(mono_frm_sz_in * resample->factor + 0.5);
249
250 /* Deinterleave input */
251 dst_buf = resample->in_buffer[i] + resample->xoff*2;
252 src_buf = input + i;
253 for (j = 0; j < mono_frm_sz_in; ++j) {
254 *dst_buf++ = *src_buf;
255 src_buf += resample->channel_cnt;
256 }
257
258 /* Resample this channel */
259 if (resample->high_quality) {
260 res_Resample(resample->in_buffer[i] + resample->xoff,
261 resample->tmp_buffer, resample->factor,
262 (pj_uint16_t)mono_frm_sz_in,
263 (char)resample->large_filter, (char)PJ_TRUE);
264 } else {
265 res_SrcLinear( resample->in_buffer[i],
266 resample->tmp_buffer,
267 resample->factor,
268 (pj_uint16_t)mono_frm_sz_in);
269 }
270
271 /* Update history */
272 dst_buf = resample->in_buffer[i];
273 src_buf = resample->in_buffer[i] + mono_frm_sz_in;
274 pjmedia_copy_samples(dst_buf, src_buf, resample->xoff * 2);
275
276 /* Reinterleave output */
277 dst_buf = output + i;
278 src_buf = resample->tmp_buffer;
279 for (j = 0; j < mono_frm_sz_out; ++j) {
280 *dst_buf = *src_buf++;
281 dst_buf += resample->channel_cnt;
282 }
283 }
284 }
285}
286
287PJ_DEF(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample)
288{
289 PJ_ASSERT_RETURN(resample != NULL, 0);
290 return resample->frame_size;
291}
292
293PJ_DEF(void) pjmedia_resample_destroy(pjmedia_resample *resample)
294{
295 PJ_UNUSED_ARG(resample);
296}
297
298
299#elif PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_NONE
300
301/*
302 * This is the configuration when sample rate conversion is disabled.
303 */
304PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool,
305 pj_bool_t high_quality,
306 pj_bool_t large_filter,
307 unsigned channel_count,
308 unsigned rate_in,
309 unsigned rate_out,
310 unsigned samples_per_frame,
311 pjmedia_resample **p_resample)
312{
313 PJ_UNUSED_ARG(pool);
314 PJ_UNUSED_ARG(high_quality);
315 PJ_UNUSED_ARG(large_filter);
316 PJ_UNUSED_ARG(channel_count);
317 PJ_UNUSED_ARG(rate_in);
318 PJ_UNUSED_ARG(rate_out);
319 PJ_UNUSED_ARG(samples_per_frame);
320 PJ_UNUSED_ARG(p_resample);
321
322 return PJ_EINVALIDOP;
323}
324
325PJ_DEF(void) pjmedia_resample_run( pjmedia_resample *resample,
326 const pj_int16_t *input,
327 pj_int16_t *output )
328{
329 PJ_UNUSED_ARG(resample);
330 PJ_UNUSED_ARG(input);
331 PJ_UNUSED_ARG(output);
332}
333
334PJ_DEF(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample)
335{
336 PJ_UNUSED_ARG(resample);
337 return 0;
338}
339
340PJ_DEF(void) pjmedia_resample_destroy(pjmedia_resample *resample)
341{
342 PJ_UNUSED_ARG(resample);
343}
344
345#endif /* PJMEDIA_RESAMPLE_IMP */
346