cannam@154: /* Copyright (c) 2007-2008 CSIRO cannam@154: Copyright (c) 2007-2009 Xiph.Org Foundation cannam@154: Written by Jean-Marc Valin */ cannam@154: /** cannam@154: @file pitch.c cannam@154: @brief Pitch analysis cannam@154: */ cannam@154: cannam@154: /* cannam@154: Redistribution and use in source and binary forms, with or without cannam@154: modification, are permitted provided that the following conditions cannam@154: are met: cannam@154: cannam@154: - Redistributions of source code must retain the above copyright cannam@154: notice, this list of conditions and the following disclaimer. cannam@154: cannam@154: - Redistributions in binary form must reproduce the above copyright cannam@154: notice, this list of conditions and the following disclaimer in the cannam@154: documentation and/or other materials provided with the distribution. cannam@154: cannam@154: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS cannam@154: ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT cannam@154: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR cannam@154: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER cannam@154: OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, cannam@154: EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, cannam@154: PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR cannam@154: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF cannam@154: LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING cannam@154: NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS cannam@154: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cannam@154: */ cannam@154: cannam@154: #ifdef HAVE_CONFIG_H cannam@154: #include "config.h" cannam@154: #endif cannam@154: cannam@154: #include "pitch.h" cannam@154: #include "os_support.h" cannam@154: #include "modes.h" cannam@154: #include "stack_alloc.h" cannam@154: #include "mathops.h" cannam@154: #include "celt_lpc.h" cannam@154: cannam@154: static void find_best_pitch(opus_val32 *xcorr, opus_val16 *y, int len, cannam@154: int max_pitch, int *best_pitch cannam@154: #ifdef FIXED_POINT cannam@154: , int yshift, opus_val32 maxcorr cannam@154: #endif cannam@154: ) cannam@154: { cannam@154: int i, j; cannam@154: opus_val32 Syy=1; cannam@154: opus_val16 best_num[2]; cannam@154: opus_val32 best_den[2]; cannam@154: #ifdef FIXED_POINT cannam@154: int xshift; cannam@154: cannam@154: xshift = celt_ilog2(maxcorr)-14; cannam@154: #endif cannam@154: cannam@154: best_num[0] = -1; cannam@154: best_num[1] = -1; cannam@154: best_den[0] = 0; cannam@154: best_den[1] = 0; cannam@154: best_pitch[0] = 0; cannam@154: best_pitch[1] = 1; cannam@154: for (j=0;j0) cannam@154: { cannam@154: opus_val16 num; cannam@154: opus_val32 xcorr16; cannam@154: xcorr16 = EXTRACT16(VSHR32(xcorr[i], xshift)); cannam@154: #ifndef FIXED_POINT cannam@154: /* Considering the range of xcorr16, this should avoid both underflows cannam@154: and overflows (inf) when squaring xcorr16 */ cannam@154: xcorr16 *= 1e-12f; cannam@154: #endif cannam@154: num = MULT16_16_Q15(xcorr16,xcorr16); cannam@154: if (MULT16_32_Q15(num,best_den[1]) > MULT16_32_Q15(best_num[1],Syy)) cannam@154: { cannam@154: if (MULT16_32_Q15(num,best_den[0]) > MULT16_32_Q15(best_num[0],Syy)) cannam@154: { cannam@154: best_num[1] = best_num[0]; cannam@154: best_den[1] = best_den[0]; cannam@154: best_pitch[1] = best_pitch[0]; cannam@154: best_num[0] = num; cannam@154: best_den[0] = Syy; cannam@154: best_pitch[0] = i; cannam@154: } else { cannam@154: best_num[1] = num; cannam@154: best_den[1] = Syy; cannam@154: best_pitch[1] = i; cannam@154: } cannam@154: } cannam@154: } cannam@154: Syy += SHR32(MULT16_16(y[i+len],y[i+len]),yshift) - SHR32(MULT16_16(y[i],y[i]),yshift); cannam@154: Syy = MAX32(1, Syy); cannam@154: } cannam@154: } cannam@154: cannam@154: static void celt_fir5(opus_val16 *x, cannam@154: const opus_val16 *num, cannam@154: int N) cannam@154: { cannam@154: int i; cannam@154: opus_val16 num0, num1, num2, num3, num4; cannam@154: opus_val32 mem0, mem1, mem2, mem3, mem4; cannam@154: num0=num[0]; cannam@154: num1=num[1]; cannam@154: num2=num[2]; cannam@154: num3=num[3]; cannam@154: num4=num[4]; cannam@154: mem0=0; cannam@154: mem1=0; cannam@154: mem2=0; cannam@154: mem3=0; cannam@154: mem4=0; cannam@154: for (i=0;i>1;i++) cannam@154: x_lp[i] = SHR32(HALF32(HALF32(x[0][(2*i-1)]+x[0][(2*i+1)])+x[0][2*i]), shift); cannam@154: x_lp[0] = SHR32(HALF32(HALF32(x[0][1])+x[0][0]), shift); cannam@154: if (C==2) cannam@154: { cannam@154: for (i=1;i>1;i++) cannam@154: x_lp[i] += SHR32(HALF32(HALF32(x[1][(2*i-1)]+x[1][(2*i+1)])+x[1][2*i]), shift); cannam@154: x_lp[0] += SHR32(HALF32(HALF32(x[1][1])+x[1][0]), shift); cannam@154: } cannam@154: cannam@154: _celt_autocorr(x_lp, ac, NULL, 0, cannam@154: 4, len>>1, arch); cannam@154: cannam@154: /* Noise floor -40 dB */ cannam@154: #ifdef FIXED_POINT cannam@154: ac[0] += SHR32(ac[0],13); cannam@154: #else cannam@154: ac[0] *= 1.0001f; cannam@154: #endif cannam@154: /* Lag windowing */ cannam@154: for (i=1;i<=4;i++) cannam@154: { cannam@154: /*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/ cannam@154: #ifdef FIXED_POINT cannam@154: ac[i] -= MULT16_32_Q15(2*i*i, ac[i]); cannam@154: #else cannam@154: ac[i] -= ac[i]*(.008f*i)*(.008f*i); cannam@154: #endif cannam@154: } cannam@154: cannam@154: _celt_lpc(lpc, ac, 4); cannam@154: for (i=0;i<4;i++) cannam@154: { cannam@154: tmp = MULT16_16_Q15(QCONST16(.9f,15), tmp); cannam@154: lpc[i] = MULT16_16_Q15(lpc[i], tmp); cannam@154: } cannam@154: /* Add a zero */ cannam@154: lpc2[0] = lpc[0] + QCONST16(.8f,SIG_SHIFT); cannam@154: lpc2[1] = lpc[1] + MULT16_16_Q15(c1,lpc[0]); cannam@154: lpc2[2] = lpc[2] + MULT16_16_Q15(c1,lpc[1]); cannam@154: lpc2[3] = lpc[3] + MULT16_16_Q15(c1,lpc[2]); cannam@154: lpc2[4] = MULT16_16_Q15(c1,lpc[3]); cannam@154: celt_fir5(x_lp, lpc2, len>>1); cannam@154: } cannam@154: cannam@154: /* Pure C implementation. */ cannam@154: #ifdef FIXED_POINT cannam@154: opus_val32 cannam@154: #else cannam@154: void cannam@154: #endif cannam@154: celt_pitch_xcorr_c(const opus_val16 *_x, const opus_val16 *_y, cannam@154: opus_val32 *xcorr, int len, int max_pitch, int arch) cannam@154: { cannam@154: cannam@154: #if 0 /* This is a simple version of the pitch correlation that should work cannam@154: well on DSPs like Blackfin and TI C5x/C6x */ cannam@154: int i, j; cannam@154: #ifdef FIXED_POINT cannam@154: opus_val32 maxcorr=1; cannam@154: #endif cannam@154: #if !defined(OVERRIDE_PITCH_XCORR) cannam@154: (void)arch; cannam@154: #endif cannam@154: for (i=0;i0); cannam@154: celt_sig_assert((((unsigned char *)_x-(unsigned char *)NULL)&3)==0); cannam@154: for (i=0;i0); cannam@154: celt_assert(max_pitch>0); cannam@154: lag = len+max_pitch; cannam@154: cannam@154: ALLOC(x_lp4, len>>2, opus_val16); cannam@154: ALLOC(y_lp4, lag>>2, opus_val16); cannam@154: ALLOC(xcorr, max_pitch>>1, opus_val32); cannam@154: cannam@154: /* Downsample by 2 again */ cannam@154: for (j=0;j>2;j++) cannam@154: x_lp4[j] = x_lp[2*j]; cannam@154: for (j=0;j>2;j++) cannam@154: y_lp4[j] = y[2*j]; cannam@154: cannam@154: #ifdef FIXED_POINT cannam@154: xmax = celt_maxabs16(x_lp4, len>>2); cannam@154: ymax = celt_maxabs16(y_lp4, lag>>2); cannam@154: shift = celt_ilog2(MAX32(1, MAX32(xmax, ymax)))-11; cannam@154: if (shift>0) cannam@154: { cannam@154: for (j=0;j>2;j++) cannam@154: x_lp4[j] = SHR16(x_lp4[j], shift); cannam@154: for (j=0;j>2;j++) cannam@154: y_lp4[j] = SHR16(y_lp4[j], shift); cannam@154: /* Use double the shift for a MAC */ cannam@154: shift *= 2; cannam@154: } else { cannam@154: shift = 0; cannam@154: } cannam@154: #endif cannam@154: cannam@154: /* Coarse search with 4x decimation */ cannam@154: cannam@154: #ifdef FIXED_POINT cannam@154: maxcorr = cannam@154: #endif cannam@154: celt_pitch_xcorr(x_lp4, y_lp4, xcorr, len>>2, max_pitch>>2, arch); cannam@154: cannam@154: find_best_pitch(xcorr, y_lp4, len>>2, max_pitch>>2, best_pitch cannam@154: #ifdef FIXED_POINT cannam@154: , 0, maxcorr cannam@154: #endif cannam@154: ); cannam@154: cannam@154: /* Finer search with 2x decimation */ cannam@154: #ifdef FIXED_POINT cannam@154: maxcorr=1; cannam@154: #endif cannam@154: for (i=0;i>1;i++) cannam@154: { cannam@154: opus_val32 sum; cannam@154: xcorr[i] = 0; cannam@154: if (abs(i-2*best_pitch[0])>2 && abs(i-2*best_pitch[1])>2) cannam@154: continue; cannam@154: #ifdef FIXED_POINT cannam@154: sum = 0; cannam@154: for (j=0;j>1;j++) cannam@154: sum += SHR32(MULT16_16(x_lp[j],y[i+j]), shift); cannam@154: #else cannam@154: sum = celt_inner_prod(x_lp, y+i, len>>1, arch); cannam@154: #endif cannam@154: xcorr[i] = MAX32(-1, sum); cannam@154: #ifdef FIXED_POINT cannam@154: maxcorr = MAX32(maxcorr, sum); cannam@154: #endif cannam@154: } cannam@154: find_best_pitch(xcorr, y, len>>1, max_pitch>>1, best_pitch cannam@154: #ifdef FIXED_POINT cannam@154: , shift+1, maxcorr cannam@154: #endif cannam@154: ); cannam@154: cannam@154: /* Refine by pseudo-interpolation */ cannam@154: if (best_pitch[0]>0 && best_pitch[0]<(max_pitch>>1)-1) cannam@154: { cannam@154: opus_val32 a, b, c; cannam@154: a = xcorr[best_pitch[0]-1]; cannam@154: b = xcorr[best_pitch[0]]; cannam@154: c = xcorr[best_pitch[0]+1]; cannam@154: if ((c-a) > MULT16_32_Q15(QCONST16(.7f,15),b-a)) cannam@154: offset = 1; cannam@154: else if ((a-c) > MULT16_32_Q15(QCONST16(.7f,15),b-c)) cannam@154: offset = -1; cannam@154: else cannam@154: offset = 0; cannam@154: } else { cannam@154: offset = 0; cannam@154: } cannam@154: *pitch = 2*best_pitch[0]-offset; cannam@154: cannam@154: RESTORE_STACK; cannam@154: } cannam@154: cannam@154: #ifdef FIXED_POINT cannam@154: static opus_val16 compute_pitch_gain(opus_val32 xy, opus_val32 xx, opus_val32 yy) cannam@154: { cannam@154: opus_val32 x2y2; cannam@154: int sx, sy, shift; cannam@154: opus_val32 g; cannam@154: opus_val16 den; cannam@154: if (xy == 0 || xx == 0 || yy == 0) cannam@154: return 0; cannam@154: sx = celt_ilog2(xx)-14; cannam@154: sy = celt_ilog2(yy)-14; cannam@154: shift = sx + sy; cannam@154: x2y2 = SHR32(MULT16_16(VSHR32(xx, sx), VSHR32(yy, sy)), 14); cannam@154: if (shift & 1) { cannam@154: if (x2y2 < 32768) cannam@154: { cannam@154: x2y2 <<= 1; cannam@154: shift--; cannam@154: } else { cannam@154: x2y2 >>= 1; cannam@154: shift++; cannam@154: } cannam@154: } cannam@154: den = celt_rsqrt_norm(x2y2); cannam@154: g = MULT16_32_Q15(den, xy); cannam@154: g = VSHR32(g, (shift>>1)-1); cannam@154: return EXTRACT16(MIN32(g, Q15ONE)); cannam@154: } cannam@154: #else cannam@154: static opus_val16 compute_pitch_gain(opus_val32 xy, opus_val32 xx, opus_val32 yy) cannam@154: { cannam@154: return xy/celt_sqrt(1+xx*yy); cannam@154: } cannam@154: #endif cannam@154: cannam@154: static const int second_check[16] = {0, 0, 3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2}; cannam@154: opus_val16 remove_doubling(opus_val16 *x, int maxperiod, int minperiod, cannam@154: int N, int *T0_, int prev_period, opus_val16 prev_gain, int arch) cannam@154: { cannam@154: int k, i, T, T0; cannam@154: opus_val16 g, g0; cannam@154: opus_val16 pg; cannam@154: opus_val32 xy,xx,yy,xy2; cannam@154: opus_val32 xcorr[3]; cannam@154: opus_val32 best_xy, best_yy; cannam@154: int offset; cannam@154: int minperiod0; cannam@154: VARDECL(opus_val32, yy_lookup); cannam@154: SAVE_STACK; cannam@154: cannam@154: minperiod0 = minperiod; cannam@154: maxperiod /= 2; cannam@154: minperiod /= 2; cannam@154: *T0_ /= 2; cannam@154: prev_period /= 2; cannam@154: N /= 2; cannam@154: x += maxperiod; cannam@154: if (*T0_>=maxperiod) cannam@154: *T0_=maxperiod-1; cannam@154: cannam@154: T = T0 = *T0_; cannam@154: ALLOC(yy_lookup, maxperiod+1, opus_val32); cannam@154: dual_inner_prod(x, x, x-T0, N, &xx, &xy, arch); cannam@154: yy_lookup[0] = xx; cannam@154: yy=xx; cannam@154: for (i=1;i<=maxperiod;i++) cannam@154: { cannam@154: yy = yy+MULT16_16(x[-i],x[-i])-MULT16_16(x[N-i],x[N-i]); cannam@154: yy_lookup[i] = MAX32(0, yy); cannam@154: } cannam@154: yy = yy_lookup[T0]; cannam@154: best_xy = xy; cannam@154: best_yy = yy; cannam@154: g = g0 = compute_pitch_gain(xy, xx, yy); cannam@154: /* Look for any pitch at T/k */ cannam@154: for (k=2;k<=15;k++) cannam@154: { cannam@154: int T1, T1b; cannam@154: opus_val16 g1; cannam@154: opus_val16 cont=0; cannam@154: opus_val16 thresh; cannam@154: T1 = celt_udiv(2*T0+k, 2*k); cannam@154: if (T1 < minperiod) cannam@154: break; cannam@154: /* Look for another strong correlation at T1b */ cannam@154: if (k==2) cannam@154: { cannam@154: if (T1+T0>maxperiod) cannam@154: T1b = T0; cannam@154: else cannam@154: T1b = T0+T1; cannam@154: } else cannam@154: { cannam@154: T1b = celt_udiv(2*second_check[k]*T0+k, 2*k); cannam@154: } cannam@154: dual_inner_prod(x, &x[-T1], &x[-T1b], N, &xy, &xy2, arch); cannam@154: xy = HALF32(xy + xy2); cannam@154: yy = HALF32(yy_lookup[T1] + yy_lookup[T1b]); cannam@154: g1 = compute_pitch_gain(xy, xx, yy); cannam@154: if (abs(T1-prev_period)<=1) cannam@154: cont = prev_gain; cannam@154: else if (abs(T1-prev_period)<=2 && 5*k*k < T0) cannam@154: cont = HALF16(prev_gain); cannam@154: else cannam@154: cont = 0; cannam@154: thresh = MAX16(QCONST16(.3f,15), MULT16_16_Q15(QCONST16(.7f,15),g0)-cont); cannam@154: /* Bias against very high pitch (very short period) to avoid false-positives cannam@154: due to short-term correlation */ cannam@154: if (T1<3*minperiod) cannam@154: thresh = MAX16(QCONST16(.4f,15), MULT16_16_Q15(QCONST16(.85f,15),g0)-cont); cannam@154: else if (T1<2*minperiod) cannam@154: thresh = MAX16(QCONST16(.5f,15), MULT16_16_Q15(QCONST16(.9f,15),g0)-cont); cannam@154: if (g1 > thresh) cannam@154: { cannam@154: best_xy = xy; cannam@154: best_yy = yy; cannam@154: T = T1; cannam@154: g = g1; cannam@154: } cannam@154: } cannam@154: best_xy = MAX32(0, best_xy); cannam@154: if (best_yy <= best_xy) cannam@154: pg = Q15ONE; cannam@154: else cannam@154: pg = SHR32(frac_div32(best_xy,best_yy+1),16); cannam@154: cannam@154: for (k=0;k<3;k++) cannam@154: xcorr[k] = celt_inner_prod(x, x-(T+k-1), N, arch); cannam@154: if ((xcorr[2]-xcorr[0]) > MULT16_32_Q15(QCONST16(.7f,15),xcorr[1]-xcorr[0])) cannam@154: offset = 1; cannam@154: else if ((xcorr[0]-xcorr[2]) > MULT16_32_Q15(QCONST16(.7f,15),xcorr[1]-xcorr[2])) cannam@154: offset = -1; cannam@154: else cannam@154: offset = 0; cannam@154: if (pg > g) cannam@154: pg = g; cannam@154: *T0_ = 2*T+offset; cannam@154: cannam@154: if (*T0_