/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h"

void WebRtcIsacfix_PitchFilterCore(int loopNumber,
                                   int16_t gain,
                                   int index,
                                   int16_t sign,
                                   int16_t* inputState,
                                   int16_t* outputBuf2,
                                   const int16_t* coefficient,
                                   int16_t* inputBuf,
                                   int16_t* outputBuf,
                                   int* index2) {
  int ind2t = *index2;
  int i = 0;
  int16_t* out2_pos2 = &outputBuf2[PITCH_BUFFSIZE - (index + 2)] + ind2t;
  int32_t w1, w2, w3, w4, w5, gain32, sign32;
  int32_t coef1, coef2, coef3, coef4, coef5 = 0;
  // Define damp factors as int32_t (pair of int16_t)
  int32_t kDampF0 = 0x0000F70A;
  int32_t kDampF1 = 0x51EC2000;
  int32_t kDampF2 = 0xF70A2000;
  int16_t* input1 = inputBuf + ind2t;
  int16_t* output1 = outputBuf + ind2t;
  int16_t* output2 = outputBuf2 + ind2t + PITCH_BUFFSIZE;

  // Load coefficients outside the loop and sign-extend gain and sign
  __asm __volatile (
    ".set     push                                        \n\t"
    ".set     noreorder                                   \n\t"
    "lwl      %[coef1],       3(%[coefficient])           \n\t"
    "lwl      %[coef2],       7(%[coefficient])           \n\t"
    "lwl      %[coef3],       11(%[coefficient])          \n\t"
    "lwl      %[coef4],       15(%[coefficient])          \n\t"
    "lwr      %[coef1],       0(%[coefficient])           \n\t"
    "lwr      %[coef2],       4(%[coefficient])           \n\t"
    "lwr      %[coef3],       8(%[coefficient])           \n\t"
    "lwr      %[coef4],       12(%[coefficient])          \n\t"
    "lhu      %[coef5],       16(%[coefficient])          \n\t"
    "seh      %[gain32],      %[gain]                     \n\t"
    "seh      %[sign32],      %[sign]                     \n\t"
    ".set     pop                                         \n\t"
    : [coef1] "=&r" (coef1), [coef2] "=&r" (coef2), [coef3] "=&r" (coef3),
      [coef4] "=&r" (coef4), [coef5] "=&r" (coef5), [gain32] "=&r" (gain32),
      [sign32] "=&r" (sign32)
    : [coefficient] "r" (coefficient), [gain] "r" (gain),
      [sign] "r" (sign)
    : "memory"
  );

  for (i = 0; i < loopNumber; i++) {
    __asm __volatile (
      ".set       push                                            \n\t"
      ".set       noreorder                                       \n\t"
      // Filter to get fractional pitch
      "li         %[w1],          8192                            \n\t"
      "mtlo       %[w1]                                           \n\t"
      "mthi       $0                                              \n\t"
      "lwl        %[w1],          3(%[out2_pos2])                 \n\t"
      "lwl        %[w2],          7(%[out2_pos2])                 \n\t"
      "lwl        %[w3],          11(%[out2_pos2])                \n\t"
      "lwl        %[w4],          15(%[out2_pos2])                \n\t"
      "lwr        %[w1],          0(%[out2_pos2])                 \n\t"
      "lwr        %[w2],          4(%[out2_pos2])                 \n\t"
      "lwr        %[w3],          8(%[out2_pos2])                 \n\t"
      "lwr        %[w4],          12(%[out2_pos2])                \n\t"
      "lhu        %[w5],          16(%[out2_pos2])                \n\t"
      "dpa.w.ph   $ac0,           %[w1],              %[coef1]    \n\t"
      "dpa.w.ph   $ac0,           %[w2],              %[coef2]    \n\t"
      "dpa.w.ph   $ac0,           %[w3],              %[coef3]    \n\t"
      "dpa.w.ph   $ac0,           %[w4],              %[coef4]    \n\t"
      "dpa.w.ph   $ac0,           %[w5],              %[coef5]    \n\t"
      "addiu      %[out2_pos2],   %[out2_pos2],       2           \n\t"
      "mthi       $0,             $ac1                            \n\t"
      "lwl        %[w2],          3(%[inputState])                \n\t"
      "lwl        %[w3],          7(%[inputState])                \n\t"
      // Fractional pitch shift & saturation
      "extr_s.h   %[w1],          $ac0,               14          \n\t"
      "li         %[w4],          16384                           \n\t"
      "lwr        %[w2],          0(%[inputState])                \n\t"
      "lwr        %[w3],          4(%[inputState])                \n\t"
      "mtlo       %[w4],          $ac1                            \n\t"
      // Shift low pass filter state
      "swl        %[w2],          5(%[inputState])                \n\t"
      "swl        %[w3],          9(%[inputState])                \n\t"
      "mul        %[w1],          %[gain32],          %[w1]       \n\t"
      "swr        %[w2],          2(%[inputState])                \n\t"
      "swr        %[w3],          6(%[inputState])                \n\t"
      // Low pass filter accumulation
      "dpa.w.ph   $ac1,           %[kDampF1],         %[w2]       \n\t"
      "dpa.w.ph   $ac1,           %[kDampF2],         %[w3]       \n\t"
      "lh         %[w4],          0(%[input1])                    \n\t"
      "addiu      %[input1],      %[input1],          2           \n\t"
      "shra_r.w   %[w1],          %[w1],              12          \n\t"
      "sh         %[w1],          0(%[inputState])                \n\t"
      "dpa.w.ph   $ac1,           %[kDampF0],         %[w1]       \n\t"
      // Low pass filter shift & saturation
      "extr_s.h   %[w2],          $ac1,               15          \n\t"
      "mul        %[w2],          %[w2],              %[sign32]   \n\t"
      // Buffer update
      "subu       %[w2],          %[w4],              %[w2]       \n\t"
      "shll_s.w   %[w2],          %[w2],              16          \n\t"
      "sra        %[w2],          %[w2],              16          \n\t"
      "sh         %[w2],          0(%[output1])                   \n\t"
      "addu       %[w2],          %[w2],              %[w4]       \n\t"
      "shll_s.w   %[w2],          %[w2],              16          \n\t"
      "addiu      %[output1],     %[output1],         2           \n\t"
      "sra        %[w2],          %[w2],              16          \n\t"
      "sh         %[w2],          0(%[output2])                   \n\t"
      "addiu      %[output2],     %[output2],         2           \n\t"
      ".set       pop                                             \n\t"
      : [w1] "=&r" (w1), [w2] "=&r" (w2), [w3] "=&r" (w3), [w4] "=&r" (w4),
        [w5] "=&r" (w5), [input1] "+r" (input1), [out2_pos2] "+r" (out2_pos2),
        [output1] "+r" (output1), [output2] "+r" (output2)
      : [coefficient] "r" (coefficient), [inputState] "r" (inputState),
        [gain32] "r" (gain32), [sign32] "r" (sign32), [kDampF0] "r" (kDampF0),
        [kDampF1] "r" (kDampF1), [kDampF2] "r" (kDampF2),
        [coef1] "r" (coef1), [coef2] "r" (coef2), [coef3] "r" (coef3),
        [coef4] "r" (coef4), [coef5] "r" (coef5)
      : "hi", "lo", "$ac1hi", "$ac1lo", "memory"
    );
  }
  (*index2) += loopNumber;
}
