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