blob: 30021a367192b82fd0fe7983365f70ce750e7058 [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#include <pjmedia/wsola.h>
21#include <pjmedia/circbuf.h>
22#include <pjmedia/errno.h>
23#include <pj/assert.h>
24#include <pj/log.h>
25#include <pj/math.h>
26#include <pj/pool.h>
27
28/*
29 * This file contains implementation of WSOLA using PJMEDIA_WSOLA_IMP_WSOLA
30 * or PJMEDIA_WSOLA_IMP_NULL
31 */
32#define THIS_FILE "wsola.c"
33
34/*
35 * http://trac.pjsip.org/repos/ticket/683:
36 * Workaround for segfault problem in the fixed point version of create_win()
37 * on ARM9 platform, possibly due to gcc optimization bug.
38 *
39 * For now, we will use linear window when floating point is disabled.
40 */
41#ifndef PJMEDIA_WSOLA_LINEAR_WIN
42# define PJMEDIA_WSOLA_LINEAR_WIN (!PJ_HAS_FLOATING_POINT)
43#endif
44
45
46#if 0
47# define TRACE_(x) PJ_LOG(4,x)
48#else
49# define TRACE_(x)
50#endif
51
52#if 0
53# define CHECK_(x) pj_assert(x)
54#else
55# define CHECK_(x)
56#endif
57
58
59#if (PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA) || \
60 (PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA_LITE)
61
62/*
63 * WSOLA implementation using WSOLA
64 */
65
66/* Buffer size including history, in frames */
67#define FRAME_CNT 6
68
69/* Number of history frames in buffer */
70#define HIST_CNT 1.5
71
72/* Template size, in msec */
73#define TEMPLATE_PTIME PJMEDIA_WSOLA_TEMPLATE_LENGTH_MSEC
74
75/* Hanning window size, in msec */
76#define HANNING_PTIME PJMEDIA_WSOLA_DELAY_MSEC
77
78/* Number of frames in erase buffer */
79#define ERASE_CNT ((unsigned)3)
80
81/* Minimum distance from template for find_pitch() of expansion, in frames */
82#define EXP_MIN_DIST 0.5
83
84/* Maximum distance from template for find_pitch() of expansion, in frames */
85#define EXP_MAX_DIST HIST_CNT
86
87/* Duration of a continuous synthetic frames after which the volume
88 * of the synthetic frame will be set to zero with fading-out effect.
89 */
90#define MAX_EXPAND_MSEC PJMEDIA_WSOLA_MAX_EXPAND_MSEC
91
92
93/* Buffer content:
94 *
95 * +---------+-----------+--------------------+
96 * | history | min_extra | more extra / empty |
97 * +---------+-----------+--------------------+
98 * ^ ^ ^ ^
99 * buf hist_size min_extra buf_size
100 *
101 * History size (hist_size) is a constant value, initialized upon creation.
102 *
103 * min_extra size is equal to HANNING_PTIME, this samples is useful for
104 * smoothening samples transition between generated frame & history
105 * (when PLC is invoked), or between generated samples & normal frame
106 * (after lost/PLC). Since min_extra samples need to be available at
107 * any time, this will introduce delay of HANNING_PTIME ms.
108 *
109 * More extra is excess samples produced by PLC (PLC frame generation may
110 * produce more than exact one frame).
111 *
112 * At any particular time, the buffer will contain at least (hist_size +
113 * min_extra) samples.
114 *
115 * A "save" operation will append the new frame to the end of the buffer,
116 * return the frame from samples right after history and shift the buffer
117 * by one frame.
118 *
119 */
120
121/* WSOLA structure */
122struct pjmedia_wsola
123{
124 unsigned clock_rate; /* Sampling rate. */
125 pj_uint16_t samples_per_frame; /* Samples per frame (const) */
126 pj_uint16_t channel_count; /* Channel countt (const) */
127 pj_uint16_t options; /* Options. */
128
129 pjmedia_circ_buf *buf; /* The buffer. */
130 pj_int16_t *erase_buf; /* Temporary erase buffer. */
131 pj_int16_t *merge_buf; /* Temporary merge buffer. */
132
133 pj_uint16_t buf_size; /* Total buffer size (const) */
134 pj_uint16_t hanning_size; /* Hanning window size (const) */
135 pj_uint16_t templ_size; /* Template size (const) */
136 pj_uint16_t hist_size; /* History size (const) */
137
138 pj_uint16_t min_extra; /* Minimum extra (const) */
139 unsigned max_expand_cnt; /* Max # of synthetic samples */
140 unsigned fade_out_pos; /* Last fade-out position */
141 pj_uint16_t expand_sr_min_dist;/* Minimum distance from template
142 for find_pitch() on expansion
143 (const) */
144 pj_uint16_t expand_sr_max_dist;/* Maximum distance from template
145 for find_pitch() on expansion
146 (const) */
147
148#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0
149 float *hanning; /* Hanning window. */
150#else
151 pj_uint16_t *hanning; /* Hanning window. */
152#endif
153
154 pj_timestamp ts; /* Running timestamp. */
155
156};
157
158#if (PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA_LITE)
159
160/* In this implementation, waveform similarity comparison is done by calculating
161 * the difference of total level between template frame and the target buffer
162 * for each template_cnt samples. The smallest difference value assumed to be
163 * the most similar block. This seems to be naive, however some tests show
164 * acceptable results and the processing speed is amazing.
165 *
166 * diff level = (template[1]+..+template[n]) - (target[1]+..+target[n])
167 */
168static pj_int16_t *find_pitch(pj_int16_t *frm, pj_int16_t *beg, pj_int16_t *end,
169 unsigned template_cnt, int first)
170{
171 pj_int16_t *sr, *best=beg;
172 int best_corr = 0x7FFFFFFF;
173 int frm_sum = 0;
174 unsigned i;
175
176 for (i = 0; i<template_cnt; ++i)
177 frm_sum += frm[i];
178
179 for (sr=beg; sr!=end; ++sr) {
180 int corr = frm_sum;
181 int abs_corr = 0;
182
183 /* Do calculation on 8 samples at once */
184 for (i = 0; i<template_cnt-8; i+=8) {
185 corr -= (int)sr[i+0] +
186 (int)sr[i+1] +
187 (int)sr[i+2] +
188 (int)sr[i+3] +
189 (int)sr[i+4] +
190 (int)sr[i+5] +
191 (int)sr[i+6] +
192 (int)sr[i+7];
193 }
194
195 /* Process remaining samples */
196 for (; i<template_cnt; ++i)
197 corr -= (int)sr[i];
198
199 abs_corr = corr > 0? corr : -corr;
200
201 if (first) {
202 if (abs_corr < best_corr) {
203 best_corr = abs_corr;
204 best = sr;
205 }
206 } else {
207 if (abs_corr <= best_corr) {
208 best_corr = abs_corr;
209 best = sr;
210 }
211 }
212 }
213
214 /*TRACE_((THIS_FILE, "found pitch at %u", best-beg));*/
215 return best;
216}
217
218#endif
219
220#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0
221/*
222 * Floating point version.
223 */
224
225#if (PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA)
226
227static pj_int16_t *find_pitch(pj_int16_t *frm, pj_int16_t *beg, pj_int16_t *end,
228 unsigned template_cnt, int first)
229{
230 pj_int16_t *sr, *best=beg;
231 double best_corr = 0;
232
233 for (sr=beg; sr!=end; ++sr) {
234 double corr = 0;
235 unsigned i;
236
237 /* Do calculation on 8 samples at once */
238 for (i=0; i<template_cnt-8; i += 8) {
239 corr += ((float)frm[i+0]) * ((float)sr[i+0]) +
240 ((float)frm[i+1]) * ((float)sr[i+1]) +
241 ((float)frm[i+2]) * ((float)sr[i+2]) +
242 ((float)frm[i+3]) * ((float)sr[i+3]) +
243 ((float)frm[i+4]) * ((float)sr[i+4]) +
244 ((float)frm[i+5]) * ((float)sr[i+5]) +
245 ((float)frm[i+6]) * ((float)sr[i+6]) +
246 ((float)frm[i+7]) * ((float)sr[i+7]);
247 }
248
249 /* Process remaining samples. */
250 for (; i<template_cnt; ++i) {
251 corr += ((float)frm[i]) * ((float)sr[i]);
252 }
253
254 if (first) {
255 if (corr > best_corr) {
256 best_corr = corr;
257 best = sr;
258 }
259 } else {
260 if (corr >= best_corr) {
261 best_corr = corr;
262 best = sr;
263 }
264 }
265 }
266
267 /*TRACE_((THIS_FILE, "found pitch at %u", best-beg));*/
268 return best;
269}
270
271#endif
272
273static void overlapp_add(pj_int16_t dst[], unsigned count,
274 pj_int16_t l[], pj_int16_t r[],
275 float w[])
276{
277 unsigned i;
278
279 for (i=0; i<count; ++i) {
280 dst[i] = (pj_int16_t)(l[i] * w[count-1-i] + r[i] * w[i]);
281 }
282}
283
284static void overlapp_add_simple(pj_int16_t dst[], unsigned count,
285 pj_int16_t l[], pj_int16_t r[])
286{
287 float step = (float)(1.0 / count), stepdown = 1.0;
288 unsigned i;
289
290 for (i=0; i<count; ++i) {
291 dst[i] = (pj_int16_t)(l[i] * stepdown + r[i] * (1-stepdown));
292 stepdown -= step;
293 }
294}
295
296static void create_win(pj_pool_t *pool, float **pw, unsigned count)
297{
298 unsigned i;
299 float *w = (float*)pj_pool_calloc(pool, count, sizeof(float));
300
301 *pw = w;
302
303 for (i=0;i<count; i++) {
304 w[i] = (float)(0.5 - 0.5 * cos(2.0 * PJ_PI * i / (count*2-1)) );
305 }
306}
307
308#else /* PJ_HAS_FLOATING_POINT */
309/*
310 * Fixed point version.
311 */
312#define WINDOW_BITS 15
313enum { WINDOW_MAX_VAL = (1 << WINDOW_BITS)-1 };
314
315#if (PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA)
316
317static pj_int16_t *find_pitch(pj_int16_t *frm, pj_int16_t *beg, pj_int16_t *end,
318 unsigned template_cnt, int first)
319{
320 pj_int16_t *sr, *best=beg;
321 pj_int64_t best_corr = 0;
322
323
324 for (sr=beg; sr!=end; ++sr) {
325 pj_int64_t corr = 0;
326 unsigned i;
327
328 /* Do calculation on 8 samples at once */
329 for (i=0; i<template_cnt-8; i+=8) {
330 corr += ((int)frm[i+0]) * ((int)sr[i+0]) +
331 ((int)frm[i+1]) * ((int)sr[i+1]) +
332 ((int)frm[i+2]) * ((int)sr[i+2]) +
333 ((int)frm[i+3]) * ((int)sr[i+3]) +
334 ((int)frm[i+4]) * ((int)sr[i+4]) +
335 ((int)frm[i+5]) * ((int)sr[i+5]) +
336 ((int)frm[i+6]) * ((int)sr[i+6]) +
337 ((int)frm[i+7]) * ((int)sr[i+7]);
338 }
339
340 /* Process remaining samples. */
341 for (; i<template_cnt; ++i) {
342 corr += ((int)frm[i]) * ((int)sr[i]);
343 }
344
345 if (first) {
346 if (corr > best_corr) {
347 best_corr = corr;
348 best = sr;
349 }
350 } else {
351 if (corr >= best_corr) {
352 best_corr = corr;
353 best = sr;
354 }
355 }
356 }
357
358 /*TRACE_((THIS_FILE, "found pitch at %u", best-beg));*/
359 return best;
360}
361
362#endif
363
364
365static void overlapp_add(pj_int16_t dst[], unsigned count,
366 pj_int16_t l[], pj_int16_t r[],
367 pj_uint16_t w[])
368{
369 unsigned i;
370
371 for (i=0; i<count; ++i) {
372 dst[i] = (pj_int16_t)(((int)(l[i]) * (int)(w[count-1-i]) +
373 (int)(r[i]) * (int)(w[i])) >> WINDOW_BITS);
374 }
375}
376
377static void overlapp_add_simple(pj_int16_t dst[], unsigned count,
378 pj_int16_t l[], pj_int16_t r[])
379{
380 int step = ((WINDOW_MAX_VAL+1) / count),
381 stepdown = WINDOW_MAX_VAL;
382 unsigned i;
383
384 for (i=0; i<count; ++i) {
385 dst[i]=(pj_int16_t)((l[i] * stepdown + r[i] * (1-stepdown)) >> WINDOW_BITS);
386 stepdown -= step;
387 }
388}
389
390#if PJ_HAS_INT64 && !PJMEDIA_WSOLA_LINEAR_WIN
391/* approx_cos():
392 * see: http://www.audiomulch.com/~rossb/code/sinusoids/
393 */
394static pj_uint32_t approx_cos( pj_uint32_t x )
395{
396 pj_uint32_t i,j,k;
397
398 if( x == 0 )
399 return 0xFFFFFFFF;
400
401 i = x << 1;
402 k = ((x + 0xBFFFFFFD) & 0x80000000) >> 30;
403 j = i - i * ((i & 0x80000000)>>30);
404 j = j >> 15;
405 j = (j * j + j) >> 1;
406 j = j - j * k;
407
408 return j;
409}
410#endif /* PJ_HAS_INT64 && .. */
411
412static void create_win(pj_pool_t *pool, pj_uint16_t **pw, unsigned count)
413{
414
415 unsigned i;
416 pj_uint16_t *w = (pj_uint16_t*)pj_pool_calloc(pool, count,
417 sizeof(pj_uint16_t));
418
419 *pw = w;
420
421 for (i=0; i<count; i++) {
422#if PJ_HAS_INT64 && !PJMEDIA_WSOLA_LINEAR_WIN
423 pj_uint32_t phase;
424 pj_uint64_t cos_val;
425
426 /* w[i] = (float)(0.5 - 0.5 * cos(2.0 * PJ_PI * i / (count*2-1)) ); */
427
428 phase = (pj_uint32_t)(PJ_INT64(0xFFFFFFFF) * i / (count*2-1));
429 cos_val = approx_cos(phase);
430
431 w[i] = (pj_uint16_t)(WINDOW_MAX_VAL -
432 (WINDOW_MAX_VAL * cos_val) / 0xFFFFFFFF);
433#else
434 /* Revert to linear */
435 w[i] = (pj_uint16_t)(i * WINDOW_MAX_VAL / count);
436#endif
437 }
438}
439
440#endif /* PJ_HAS_FLOATING_POINT */
441
442/* Apply fade-in to the buffer.
443 * - fade_cnt is the number of samples on which the volume
444 * will go from zero to 100%
445 * - fade_pos is current sample position within fade_cnt range.
446 * It is zero for the first sample, so the first sample will
447 * have zero volume. This value is increasing.
448 */
449static void fade_in(pj_int16_t buf[], int count,
450 int fade_in_pos, int fade_cnt)
451{
452#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0
453 float fade_pos = (float)fade_in_pos;
454#else
455 int fade_pos = fade_in_pos;
456#endif
457
458 if (fade_cnt - fade_pos < count) {
459 for (; fade_pos < fade_cnt; ++fade_pos, ++buf) {
460 *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt);
461 }
462 /* Leave the remaining samples as is */
463 } else {
464 pj_int16_t *end = buf + count;
465 for (; buf != end; ++fade_pos, ++buf) {
466 *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt);
467 }
468 }
469}
470
471/* Apply fade-out to the buffer. */
472static void wsola_fade_out(pjmedia_wsola *wsola,
473 pj_int16_t buf[], int count)
474{
475 pj_int16_t *end = buf + count;
476 int fade_cnt = wsola->max_expand_cnt;
477#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0
478 float fade_pos = (float)wsola->fade_out_pos;
479#else
480 int fade_pos = wsola->fade_out_pos;
481#endif
482
483 if (wsola->fade_out_pos == 0) {
484 pjmedia_zero_samples(buf, count);
485 } else if (fade_pos < count) {
486 for (; fade_pos; --fade_pos, ++buf) {
487 *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt);
488 }
489 if (buf != end)
490 pjmedia_zero_samples(buf, (unsigned)(end - buf));
491 wsola->fade_out_pos = 0;
492 } else {
493 for (; buf != end; --fade_pos, ++buf) {
494 *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt);
495 }
496 wsola->fade_out_pos -= count;
497 }
498}
499
500
501PJ_DEF(pj_status_t) pjmedia_wsola_create( pj_pool_t *pool,
502 unsigned clock_rate,
503 unsigned samples_per_frame,
504 unsigned channel_count,
505 unsigned options,
506 pjmedia_wsola **p_wsola)
507{
508 pjmedia_wsola *wsola;
509 pj_status_t status;
510
511 PJ_ASSERT_RETURN(pool && clock_rate && samples_per_frame && p_wsola,
512 PJ_EINVAL);
513 PJ_ASSERT_RETURN(clock_rate <= 65535, PJ_EINVAL);
514 PJ_ASSERT_RETURN(samples_per_frame < clock_rate, PJ_EINVAL);
515 PJ_ASSERT_RETURN(channel_count > 0, PJ_EINVAL);
516
517 /* Allocate wsola and initialize vars */
518 wsola = PJ_POOL_ZALLOC_T(pool, pjmedia_wsola);
519 wsola->clock_rate= (pj_uint16_t) clock_rate;
520 wsola->samples_per_frame = (pj_uint16_t) samples_per_frame;
521 wsola->channel_count = (pj_uint16_t) channel_count;
522 wsola->options = (pj_uint16_t) options;
523 wsola->max_expand_cnt = clock_rate * MAX_EXPAND_MSEC / 1000;
524 wsola->fade_out_pos = wsola->max_expand_cnt;
525
526 /* Create circular buffer */
527 wsola->buf_size = (pj_uint16_t) (samples_per_frame * FRAME_CNT);
528 status = pjmedia_circ_buf_create(pool, wsola->buf_size, &wsola->buf);
529 if (status != PJ_SUCCESS) {
530 PJ_LOG(3, (THIS_FILE, "Failed to create circular buf"));
531 return status;
532 }
533
534 /* Calculate history size */
535 wsola->hist_size = (pj_uint16_t)(HIST_CNT * samples_per_frame);
536
537 /* Calculate template size */
538 wsola->templ_size = (pj_uint16_t)(TEMPLATE_PTIME * clock_rate *
539 channel_count / 1000);
540 if (wsola->templ_size > samples_per_frame)
541 wsola->templ_size = wsola->samples_per_frame;
542
543 /* Calculate hanning window size */
544 wsola->hanning_size = (pj_uint16_t)(HANNING_PTIME * clock_rate *
545 channel_count / 1000);
546 if (wsola->hanning_size > wsola->samples_per_frame)
547 wsola->hanning_size = wsola->samples_per_frame;
548
549 pj_assert(wsola->templ_size <= wsola->hanning_size);
550
551 /* Create merge buffer */
552 wsola->merge_buf = (pj_int16_t*) pj_pool_calloc(pool,
553 wsola->hanning_size,
554 sizeof(pj_int16_t));
555
556 /* Setup with PLC */
557 if ((options & PJMEDIA_WSOLA_NO_PLC) == 0) {
558 wsola->min_extra = wsola->hanning_size;
559 wsola->expand_sr_min_dist = (pj_uint16_t)
560 (EXP_MIN_DIST * wsola->samples_per_frame);
561 wsola->expand_sr_max_dist = (pj_uint16_t)
562 (EXP_MAX_DIST * wsola->samples_per_frame);
563 }
564
565 /* Setup with hanning */
566 if ((options & PJMEDIA_WSOLA_NO_HANNING) == 0) {
567 create_win(pool, &wsola->hanning, wsola->hanning_size);
568 }
569
570 /* Setup with discard */
571 if ((options & PJMEDIA_WSOLA_NO_DISCARD) == 0) {
572 wsola->erase_buf = (pj_int16_t*)pj_pool_calloc(pool, samples_per_frame *
573 ERASE_CNT,
574 sizeof(pj_int16_t));
575 }
576
577 /* Generate dummy extra */
578 pjmedia_circ_buf_set_len(wsola->buf, wsola->hist_size + wsola->min_extra);
579
580 *p_wsola = wsola;
581 return PJ_SUCCESS;
582
583}
584
585PJ_DEF(pj_status_t) pjmedia_wsola_destroy(pjmedia_wsola *wsola)
586{
587 /* Nothing to do */
588 PJ_UNUSED_ARG(wsola);
589
590 return PJ_SUCCESS;
591}
592
593PJ_DEF(pj_status_t) pjmedia_wsola_set_max_expand(pjmedia_wsola *wsola,
594 unsigned msec)
595{
596 PJ_ASSERT_RETURN(wsola, PJ_EINVAL);
597 wsola->max_expand_cnt = msec * wsola->clock_rate / 1000;
598 return PJ_SUCCESS;
599}
600
601PJ_DEF(pj_status_t) pjmedia_wsola_reset( pjmedia_wsola *wsola,
602 unsigned options)
603{
604 PJ_ASSERT_RETURN(wsola && options==0, PJ_EINVAL);
605 PJ_UNUSED_ARG(options);
606
607 pjmedia_circ_buf_reset(wsola->buf);
608 pjmedia_circ_buf_set_len(wsola->buf, wsola->hist_size + wsola->min_extra);
609 pjmedia_zero_samples(wsola->buf->start, wsola->buf->len);
610 wsola->fade_out_pos = wsola->max_expand_cnt;
611
612 return PJ_SUCCESS;
613}
614
615
616static void expand(pjmedia_wsola *wsola, unsigned needed)
617{
618 unsigned generated = 0;
619 unsigned rep;
620
621 pj_int16_t *reg1, *reg2;
622 unsigned reg1_len, reg2_len;
623
624 pjmedia_circ_buf_pack_buffer(wsola->buf);
625 pjmedia_circ_buf_get_read_regions(wsola->buf, &reg1, &reg1_len,
626 &reg2, &reg2_len);
627 CHECK_(reg2_len == 0);
628
629 for (rep=1;; ++rep) {
630 pj_int16_t *start, *templ;
631 unsigned dist;
632
633 templ = reg1 + reg1_len - wsola->hanning_size;
634 CHECK_(templ - reg1 >= wsola->hist_size);
635
636 start = find_pitch(templ,
637 templ - wsola->expand_sr_max_dist,
638 templ - wsola->expand_sr_min_dist,
639 wsola->templ_size,
640 1);
641
642 /* Should we make sure that "start" is really aligned to
643 * channel #0, in case of stereo? Probably not necessary, as
644 * find_pitch() should have found the best match anyway.
645 */
646
647 if (wsola->options & PJMEDIA_WSOLA_NO_HANNING) {
648 overlapp_add_simple(wsola->merge_buf, wsola->hanning_size,
649 templ, start);
650 } else {
651 /* Check if pointers are in the valid range */
652 CHECK_(templ >= wsola->buf->buf &&
653 templ + wsola->hanning_size <=
654 wsola->buf->buf + wsola->buf->capacity);
655 CHECK_(start >= wsola->buf->buf &&
656 start + wsola->hanning_size <=
657 wsola->buf->buf + wsola->buf->capacity);
658
659 overlapp_add(wsola->merge_buf, wsola->hanning_size, templ,
660 start, wsola->hanning);
661 }
662
663 /* How many new samples do we have */
664 dist = (unsigned)(templ - start);
665
666 /* Not enough buffer to hold the result */
667 if (reg1_len + dist > wsola->buf_size) {
668 pj_assert(!"WSOLA buffer size may be to small!");
669 break;
670 }
671
672 /* Copy the "tail" (excess frame) to the end */
673 pjmedia_move_samples(templ + wsola->hanning_size,
674 start + wsola->hanning_size,
675 dist);
676
677 /* Copy the merged frame */
678 pjmedia_copy_samples(templ, wsola->merge_buf, wsola->hanning_size);
679
680 /* We have new samples */
681 reg1_len += dist;
682 pjmedia_circ_buf_set_len(wsola->buf, reg1_len);
683
684 generated += dist;
685
686 if (generated >= needed) {
687 TRACE_((THIS_FILE, "WSOLA frame expanded after %d iterations",
688 rep));
689 break;
690 }
691 }
692}
693
694
695static unsigned compress(pjmedia_wsola *wsola, pj_int16_t *buf, unsigned count,
696 unsigned del_cnt)
697{
698 unsigned samples_del = 0, rep;
699
700 for (rep=1; ; ++rep) {
701 pj_int16_t *start, *end;
702 unsigned dist;
703
704 if (count <= wsola->hanning_size + del_cnt) {
705 TRACE_((THIS_FILE, "Not enough samples to compress!"));
706 return samples_del;
707 }
708
709 // Make start distance to del_cnt, so discard will be performed in
710 // only one iteration.
711 //start = buf + (frmsz >> 1);
712 start = buf + del_cnt - samples_del;
713 end = start + wsola->samples_per_frame;
714
715 if (end + wsola->hanning_size > buf + count) {
716 end = buf+count-wsola->hanning_size;
717 }
718
719 CHECK_(start < end);
720
721 start = find_pitch(buf, start, end, wsola->templ_size, 0);
722 dist = (unsigned)(start - buf);
723
724 if (wsola->options & PJMEDIA_WSOLA_NO_HANNING) {
725 overlapp_add_simple(buf, wsola->hanning_size, buf, start);
726 } else {
727 overlapp_add(buf, wsola->hanning_size, buf, start, wsola->hanning);
728 }
729
730 pjmedia_move_samples(buf + wsola->hanning_size,
731 buf + wsola->hanning_size + dist,
732 count - wsola->hanning_size - dist);
733
734 count -= dist;
735 samples_del += dist;
736
737 if (samples_del >= del_cnt) {
738 TRACE_((THIS_FILE,
739 "Erased %d of %d requested after %d iteration(s)",
740 samples_del, del_cnt, rep));
741 break;
742 }
743 }
744
745 return samples_del;
746}
747
748
749
750PJ_DEF(pj_status_t) pjmedia_wsola_save( pjmedia_wsola *wsola,
751 pj_int16_t frm[],
752 pj_bool_t prev_lost)
753{
754 unsigned buf_len;
755 pj_status_t status;
756
757 buf_len = pjmedia_circ_buf_get_len(wsola->buf);
758
759 /* Update vars */
760 wsola->ts.u64 += wsola->samples_per_frame;
761
762 /* If previous frame was lost, smoothen this frame with the generated one */
763 if (prev_lost) {
764 pj_int16_t *reg1, *reg2;
765 unsigned reg1_len, reg2_len;
766 pj_int16_t *ola_left;
767
768 /* Trim excessive len */
769 if ((int)buf_len > wsola->hist_size + (wsola->min_extra<<1)) {
770 buf_len = wsola->hist_size + (wsola->min_extra<<1);
771 pjmedia_circ_buf_set_len(wsola->buf, buf_len);
772 }
773
774 pjmedia_circ_buf_get_read_regions(wsola->buf, &reg1, &reg1_len,
775 &reg2, &reg2_len);
776
777 CHECK_(pjmedia_circ_buf_get_len(wsola->buf) >=
778 (unsigned)(wsola->hist_size + (wsola->min_extra<<1)));
779
780 /* Continue applying fade out to the extra samples */
781 if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0) {
782 if (reg2_len == 0) {
783 wsola_fade_out(wsola, reg1 + reg1_len - (wsola->min_extra<<1),
784 (wsola->min_extra<<1));
785 } else if ((int)reg2_len >= (wsola->min_extra<<1)) {
786 wsola_fade_out(wsola, reg2 + reg2_len - (wsola->min_extra<<1),
787 (wsola->min_extra<<1));
788 } else {
789 unsigned tmp = (wsola->min_extra<<1) - reg2_len;
790 wsola_fade_out(wsola, reg1 + reg1_len - tmp, tmp);
791 wsola_fade_out(wsola, reg2, reg2_len);
792 }
793 }
794
795 /* Get the region in buffer to be merged with the frame */
796 if (reg2_len == 0) {
797 ola_left = reg1 + reg1_len - wsola->min_extra;
798 } else if (reg2_len >= wsola->min_extra) {
799 ola_left = reg2 + reg2_len - wsola->min_extra;
800 } else {
801 unsigned tmp;
802
803 tmp = wsola->min_extra - reg2_len;
804 pjmedia_copy_samples(wsola->merge_buf, reg1 + reg1_len - tmp, tmp);
805 pjmedia_copy_samples(wsola->merge_buf + tmp, reg2, reg2_len);
806 ola_left = wsola->merge_buf;
807 }
808
809 /* Apply fade-in to the frame before merging */
810 if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0) {
811 unsigned count = wsola->min_extra;
812 int fade_in_pos;
813
814 /* Scale fade_in position based on last fade-out */
815 fade_in_pos = wsola->fade_out_pos * count /
816 wsola->max_expand_cnt;
817
818 /* Fade-in it */
819 fade_in(frm, wsola->samples_per_frame,
820 fade_in_pos, count);
821 }
822
823 /* Merge it */
824 overlapp_add_simple(frm, wsola->min_extra, ola_left, frm);
825
826 /* Trim len */
827 buf_len -= wsola->min_extra;
828 pjmedia_circ_buf_set_len(wsola->buf, buf_len);
829
830 } else if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0 &&
831 wsola->fade_out_pos != wsola->max_expand_cnt)
832 {
833 unsigned count = wsola->min_extra;
834 int fade_in_pos;
835
836 /* Fade out the remaining synthetic samples */
837 if (buf_len > wsola->hist_size) {
838 pj_int16_t *reg1, *reg2;
839 unsigned reg1_len, reg2_len;
840
841 /* Number of samples to fade out */
842 count = buf_len - wsola->hist_size;
843
844 pjmedia_circ_buf_get_read_regions(wsola->buf, &reg1, &reg1_len,
845 &reg2, &reg2_len);
846
847 CHECK_(pjmedia_circ_buf_get_len(wsola->buf) >=
848 (unsigned)(wsola->hist_size + (wsola->min_extra<<1)));
849
850 /* Continue applying fade out to the extra samples */
851 if (reg2_len == 0) {
852 wsola_fade_out(wsola, reg1 + reg1_len - count, count);
853 } else if (reg2_len >= count) {
854 wsola_fade_out(wsola, reg2 + reg2_len - count, count);
855 } else {
856 unsigned tmp = count - reg2_len;
857 wsola_fade_out(wsola, reg1 + reg1_len - tmp, tmp);
858 wsola_fade_out(wsola, reg2, reg2_len);
859 }
860 }
861
862 /* Apply fade-in to the frame */
863 count = wsola->min_extra;
864
865 /* Scale fade_in position based on last fade-out */
866 fade_in_pos = wsola->fade_out_pos * count /
867 wsola->max_expand_cnt;
868
869 /* Fade it in */
870 fade_in(frm, wsola->samples_per_frame,
871 fade_in_pos, count);
872
873 }
874
875 wsola->fade_out_pos = wsola->max_expand_cnt;
876
877 status = pjmedia_circ_buf_write(wsola->buf, frm, wsola->samples_per_frame);
878 if (status != PJ_SUCCESS) {
879 TRACE_((THIS_FILE, "Failed writing to circbuf [err=%d]", status));
880 return status;
881 }
882
883 status = pjmedia_circ_buf_copy(wsola->buf, wsola->hist_size, frm,
884 wsola->samples_per_frame);
885 if (status != PJ_SUCCESS) {
886 TRACE_((THIS_FILE, "Failed copying from circbuf [err=%d]", status));
887 return status;
888 }
889
890 return pjmedia_circ_buf_adv_read_ptr(wsola->buf, wsola->samples_per_frame);
891}
892
893
894PJ_DEF(pj_status_t) pjmedia_wsola_generate( pjmedia_wsola *wsola,
895 pj_int16_t frm[])
896{
897 unsigned samples_len, samples_req;
898 pj_status_t status = PJ_SUCCESS;
899
900 CHECK_(pjmedia_circ_buf_get_len(wsola->buf) >= wsola->hist_size +
901 wsola->min_extra);
902
903 /* Calculate how many samples in the buffer */
904 samples_len = pjmedia_circ_buf_get_len(wsola->buf) - wsola->hist_size;
905
906 /* Calculate how many samples are required to be available in the buffer */
907 samples_req = wsola->samples_per_frame + (wsola->min_extra << 1);
908
909 wsola->ts.u64 += wsola->samples_per_frame;
910
911 if (samples_len < samples_req) {
912 /* Expand buffer */
913 expand(wsola, samples_req - samples_len);
914 TRACE_((THIS_FILE, "Buf size after expanded = %d",
915 pjmedia_circ_buf_get_len(wsola->buf)));
916 }
917
918 status = pjmedia_circ_buf_copy(wsola->buf, wsola->hist_size, frm,
919 wsola->samples_per_frame);
920 if (status != PJ_SUCCESS) {
921 TRACE_((THIS_FILE, "Failed copying from circbuf [err=%d]", status));
922 return status;
923 }
924
925 pjmedia_circ_buf_adv_read_ptr(wsola->buf, wsola->samples_per_frame);
926
927 /* Apply fade-out to the frame */
928 if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0) {
929 wsola_fade_out(wsola, frm, wsola->samples_per_frame);
930 }
931
932 return PJ_SUCCESS;
933}
934
935
936PJ_DEF(pj_status_t) pjmedia_wsola_discard( pjmedia_wsola *wsola,
937 pj_int16_t buf1[],
938 unsigned buf1_cnt,
939 pj_int16_t buf2[],
940 unsigned buf2_cnt,
941 unsigned *del_cnt)
942{
943 PJ_ASSERT_RETURN(wsola && buf1 && buf1_cnt && del_cnt, PJ_EINVAL);
944 PJ_ASSERT_RETURN(*del_cnt, PJ_EINVAL);
945
946 if (buf2_cnt == 0) {
947 /* The whole buffer is contiguous space, straight away. */
948 *del_cnt = compress(wsola, buf1, buf1_cnt, *del_cnt);
949 } else {
950 PJ_ASSERT_RETURN(buf2, PJ_EINVAL);
951
952 if (buf1_cnt < ERASE_CNT * wsola->samples_per_frame &&
953 buf2_cnt < ERASE_CNT * wsola->samples_per_frame &&
954 wsola->erase_buf == NULL)
955 {
956 /* We need erase_buf but WSOLA was created with
957 * PJMEDIA_WSOLA_NO_DISCARD flag.
958 */
959 pj_assert(!"WSOLA need erase buffer!");
960 return PJ_EINVALIDOP;
961 }
962
963 if (buf2_cnt >= ERASE_CNT * wsola->samples_per_frame) {
964 /* Enough space to perform compress in the second buffer. */
965 *del_cnt = compress(wsola, buf2, buf2_cnt, *del_cnt);
966 } else if (buf1_cnt >= ERASE_CNT * wsola->samples_per_frame) {
967 /* Enough space to perform compress in the first buffer, but then
968 * we need to re-arrange the buffers so there is no gap between
969 * buffers.
970 */
971 unsigned max;
972
973 *del_cnt = compress(wsola, buf1, buf1_cnt, *del_cnt);
974
975 max = *del_cnt;
976 if (max > buf2_cnt)
977 max = buf2_cnt;
978
979 pjmedia_move_samples(buf1 + buf1_cnt - (*del_cnt), buf2, max);
980
981 if (max < buf2_cnt) {
982 pjmedia_move_samples(buf2, buf2+(*del_cnt),
983 buf2_cnt-max);
984 }
985 } else {
986 /* Not enough samples in either buffers to perform compress.
987 * Need to combine the buffers in a contiguous space, the erase_buf.
988 */
989 unsigned buf_size = buf1_cnt + buf2_cnt;
990 pj_int16_t *rem; /* remainder */
991 unsigned rem_cnt;
992
993 if (buf_size > ERASE_CNT * wsola->samples_per_frame) {
994 buf_size = ERASE_CNT * wsola->samples_per_frame;
995
996 rem_cnt = buf1_cnt + buf2_cnt - buf_size;
997 rem = buf2 + buf2_cnt - rem_cnt;
998
999 } else {
1000 rem = NULL;
1001 rem_cnt = 0;
1002 }
1003
1004 pjmedia_copy_samples(wsola->erase_buf, buf1, buf1_cnt);
1005 pjmedia_copy_samples(wsola->erase_buf+buf1_cnt, buf2,
1006 buf_size-buf1_cnt);
1007
1008 *del_cnt = compress(wsola, wsola->erase_buf, buf_size, *del_cnt);
1009
1010 buf_size -= (*del_cnt);
1011
1012 /* Copy back to buffers */
1013 if (buf_size == buf1_cnt) {
1014 pjmedia_copy_samples(buf1, wsola->erase_buf, buf_size);
1015 if (rem_cnt) {
1016 pjmedia_move_samples(buf2, rem, rem_cnt);
1017 }
1018 } else if (buf_size < buf1_cnt) {
1019 pjmedia_copy_samples(buf1, wsola->erase_buf, buf_size);
1020 if (rem_cnt) {
1021 unsigned c = rem_cnt;
1022 if (c > buf1_cnt-buf_size) {
1023 c = buf1_cnt-buf_size;
1024 }
1025 pjmedia_copy_samples(buf1+buf_size, rem, c);
1026 rem += c;
1027 rem_cnt -= c;
1028 if (rem_cnt)
1029 pjmedia_move_samples(buf2, rem, rem_cnt);
1030 }
1031 } else {
1032 pjmedia_copy_samples(buf1, wsola->erase_buf, buf1_cnt);
1033 pjmedia_copy_samples(buf2, wsola->erase_buf+buf1_cnt,
1034 buf_size-buf1_cnt);
1035 if (rem_cnt) {
1036 pjmedia_move_samples(buf2+buf_size-buf1_cnt, rem,
1037 rem_cnt);
1038 }
1039 }
1040
1041 }
1042 }
1043
1044 return (*del_cnt) > 0 ? PJ_SUCCESS : PJ_ETOOSMALL;
1045}
1046
1047
1048#elif PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_NULL
1049/*
1050 * WSOLA implementation using NULL
1051 */
1052
1053struct pjmedia_wsola
1054{
1055 unsigned samples_per_frame;
1056};
1057
1058
1059PJ_DEF(pj_status_t) pjmedia_wsola_create( pj_pool_t *pool,
1060 unsigned clock_rate,
1061 unsigned samples_per_frame,
1062 unsigned channel_count,
1063 unsigned options,
1064 pjmedia_wsola **p_wsola)
1065{
1066 pjmedia_wsola *wsola;
1067
1068 wsola = PJ_POOL_ZALLOC_T(pool, struct pjmedia_wsola);
1069 wsola->samples_per_frame = samples_per_frame;
1070
1071 PJ_UNUSED_ARG(clock_rate);
1072 PJ_UNUSED_ARG(channel_count);
1073 PJ_UNUSED_ARG(options);
1074
1075 *p_wsola = wsola;
1076
1077 return PJ_SUCCESS;
1078}
1079
1080
1081PJ_DEF(pj_status_t) pjmedia_wsola_destroy(pjmedia_wsola *wsola)
1082{
1083 PJ_UNUSED_ARG(wsola);
1084 return PJ_SUCCESS;
1085}
1086
1087
1088PJ_DEF(pj_status_t) pjmedia_wsola_reset( pjmedia_wsola *wsola,
1089 unsigned options)
1090{
1091 PJ_UNUSED_ARG(wsola);
1092 PJ_UNUSED_ARG(options);
1093
1094 return PJ_SUCCESS;
1095}
1096
1097
1098PJ_DEF(pj_status_t) pjmedia_wsola_save( pjmedia_wsola *wsola,
1099 pj_int16_t frm[],
1100 pj_bool_t prev_lost)
1101{
1102 PJ_UNUSED_ARG(wsola);
1103 PJ_UNUSED_ARG(frm);
1104 PJ_UNUSED_ARG(prev_lost);
1105
1106 return PJ_SUCCESS;
1107}
1108
1109
1110PJ_DEF(pj_status_t) pjmedia_wsola_generate( pjmedia_wsola *wsola,
1111 pj_int16_t frm[])
1112{
1113 pjmedia_zero_samples(frm, wsola->samples_per_frame);
1114 return PJ_SUCCESS;
1115}
1116
1117
1118PJ_DEF(pj_status_t) pjmedia_wsola_discard( pjmedia_wsola *wsola,
1119 pj_int16_t buf1[],
1120 unsigned buf1_cnt,
1121 pj_int16_t buf2[],
1122 unsigned buf2_cnt,
1123 unsigned *del_cnt)
1124{
1125 CHECK_(buf1_cnt + buf2_cnt >= wsola->samples_per_frame);
1126
1127 PJ_UNUSED_ARG(buf1);
1128 PJ_UNUSED_ARG(buf1_cnt);
1129 PJ_UNUSED_ARG(buf2);
1130 PJ_UNUSED_ARG(buf2_cnt);
1131
1132 *del_cnt = wsola->samples_per_frame;
1133
1134 return PJ_SUCCESS;
1135}
1136
1137#endif /* #if PJMEDIA_WSOLA_IMP.. */
1138
1139