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