Home | History | Annotate | Download | only in aecm
      1 /*
      2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/modules/audio_processing/aecm/aecm_core.h"
     12 
     13 #include <arm_neon.h>
     14 #include <assert.h>
     15 
     16 #include "webrtc/common_audio/signal_processing/include/real_fft.h"
     17 
     18 // TODO(kma): Re-write the corresponding assembly file, the offset
     19 // generating script and makefile, to replace these C functions.
     20 
     21 // Square root of Hanning window in Q14.
     22 const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = {
     23   0,
     24   399, 798, 1196, 1594, 1990, 2386, 2780, 3172,
     25   3562, 3951, 4337, 4720, 5101, 5478, 5853, 6224,
     26   6591, 6954, 7313, 7668, 8019, 8364, 8705, 9040,
     27   9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514,
     28   11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553,
     29   13773, 13985, 14189, 14384, 14571, 14749, 14918, 15079,
     30   15231, 15373, 15506, 15631, 15746, 15851, 15947, 16034,
     31   16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384
     32 };
     33 
     34 static inline void AddLanes(uint32_t* ptr, uint32x4_t v) {
     35 #if defined(WEBRTC_ARCH_ARM64)
     36   *(ptr) = vaddvq_u32(v);
     37 #else
     38   uint32x2_t tmp_v;
     39   tmp_v = vadd_u32(vget_low_u32(v), vget_high_u32(v));
     40   tmp_v = vpadd_u32(tmp_v, tmp_v);
     41   *(ptr) = vget_lane_u32(tmp_v, 0);
     42 #endif
     43 }
     44 
     45 void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm,
     46                                        const uint16_t* far_spectrum,
     47                                        int32_t* echo_est,
     48                                        uint32_t* far_energy,
     49                                        uint32_t* echo_energy_adapt,
     50                                        uint32_t* echo_energy_stored) {
     51   int16_t* start_stored_p = aecm->channelStored;
     52   int16_t* start_adapt_p = aecm->channelAdapt16;
     53   int32_t* echo_est_p = echo_est;
     54   const int16_t* end_stored_p = aecm->channelStored + PART_LEN;
     55   const uint16_t* far_spectrum_p = far_spectrum;
     56   int16x8_t store_v, adapt_v;
     57   uint16x8_t spectrum_v;
     58   uint32x4_t echo_est_v_low, echo_est_v_high;
     59   uint32x4_t far_energy_v, echo_stored_v, echo_adapt_v;
     60 
     61   far_energy_v = vdupq_n_u32(0);
     62   echo_adapt_v = vdupq_n_u32(0);
     63   echo_stored_v = vdupq_n_u32(0);
     64 
     65   // Get energy for the delayed far end signal and estimated
     66   // echo using both stored and adapted channels.
     67   // The C code:
     68   //  for (i = 0; i < PART_LEN1; i++) {
     69   //      echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
     70   //                                         far_spectrum[i]);
     71   //      (*far_energy) += (uint32_t)(far_spectrum[i]);
     72   //      *echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i];
     73   //      (*echo_energy_stored) += (uint32_t)echo_est[i];
     74   //  }
     75   while (start_stored_p < end_stored_p) {
     76     spectrum_v = vld1q_u16(far_spectrum_p);
     77     adapt_v = vld1q_s16(start_adapt_p);
     78     store_v = vld1q_s16(start_stored_p);
     79 
     80     far_energy_v = vaddw_u16(far_energy_v, vget_low_u16(spectrum_v));
     81     far_energy_v = vaddw_u16(far_energy_v, vget_high_u16(spectrum_v));
     82 
     83     echo_est_v_low = vmull_u16(vreinterpret_u16_s16(vget_low_s16(store_v)),
     84                                vget_low_u16(spectrum_v));
     85     echo_est_v_high = vmull_u16(vreinterpret_u16_s16(vget_high_s16(store_v)),
     86                                 vget_high_u16(spectrum_v));
     87     vst1q_s32(echo_est_p, vreinterpretq_s32_u32(echo_est_v_low));
     88     vst1q_s32(echo_est_p + 4, vreinterpretq_s32_u32(echo_est_v_high));
     89 
     90     echo_stored_v = vaddq_u32(echo_est_v_low, echo_stored_v);
     91     echo_stored_v = vaddq_u32(echo_est_v_high, echo_stored_v);
     92 
     93     echo_adapt_v = vmlal_u16(echo_adapt_v,
     94                              vreinterpret_u16_s16(vget_low_s16(adapt_v)),
     95                              vget_low_u16(spectrum_v));
     96     echo_adapt_v = vmlal_u16(echo_adapt_v,
     97                              vreinterpret_u16_s16(vget_high_s16(adapt_v)),
     98                              vget_high_u16(spectrum_v));
     99 
    100     start_stored_p += 8;
    101     start_adapt_p += 8;
    102     far_spectrum_p += 8;
    103     echo_est_p += 8;
    104   }
    105 
    106   AddLanes(far_energy, far_energy_v);
    107   AddLanes(echo_energy_stored, echo_stored_v);
    108   AddLanes(echo_energy_adapt, echo_adapt_v);
    109 
    110   echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN],
    111                                              far_spectrum[PART_LEN]);
    112   *echo_energy_stored += (uint32_t)echo_est[PART_LEN];
    113   *far_energy += (uint32_t)far_spectrum[PART_LEN];
    114   *echo_energy_adapt += aecm->channelAdapt16[PART_LEN] * far_spectrum[PART_LEN];
    115 }
    116 
    117 void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore* aecm,
    118                                          const uint16_t* far_spectrum,
    119                                          int32_t* echo_est) {
    120   assert((uintptr_t)echo_est % 32 == 0);
    121   assert((uintptr_t)(aecm->channelStored) % 16 == 0);
    122   assert((uintptr_t)(aecm->channelAdapt16) % 16 == 0);
    123 
    124   // This is C code of following optimized code.
    125   // During startup we store the channel every block.
    126   //  memcpy(aecm->channelStored,
    127   //         aecm->channelAdapt16,
    128   //         sizeof(int16_t) * PART_LEN1);
    129   // Recalculate echo estimate
    130   //  for (i = 0; i < PART_LEN; i += 4) {
    131   //    echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
    132   //                                        far_spectrum[i]);
    133   //    echo_est[i + 1] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1],
    134   //                                            far_spectrum[i + 1]);
    135   //    echo_est[i + 2] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2],
    136   //                                            far_spectrum[i + 2]);
    137   //    echo_est[i + 3] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3],
    138   //                                            far_spectrum[i + 3]);
    139   //  }
    140   //  echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
    141   //                                     far_spectrum[i]);
    142   const uint16_t* far_spectrum_p = far_spectrum;
    143   int16_t* start_adapt_p = aecm->channelAdapt16;
    144   int16_t* start_stored_p = aecm->channelStored;
    145   const int16_t* end_stored_p = aecm->channelStored + PART_LEN;
    146   int32_t* echo_est_p = echo_est;
    147 
    148   uint16x8_t far_spectrum_v;
    149   int16x8_t adapt_v;
    150   uint32x4_t echo_est_v_low, echo_est_v_high;
    151 
    152   while (start_stored_p < end_stored_p) {
    153     far_spectrum_v = vld1q_u16(far_spectrum_p);
    154     adapt_v = vld1q_s16(start_adapt_p);
    155 
    156     vst1q_s16(start_stored_p, adapt_v);
    157 
    158     echo_est_v_low = vmull_u16(vget_low_u16(far_spectrum_v),
    159                                vget_low_u16(vreinterpretq_u16_s16(adapt_v)));
    160     echo_est_v_high = vmull_u16(vget_high_u16(far_spectrum_v),
    161                                 vget_high_u16(vreinterpretq_u16_s16(adapt_v)));
    162 
    163     vst1q_s32(echo_est_p, vreinterpretq_s32_u32(echo_est_v_low));
    164     vst1q_s32(echo_est_p + 4, vreinterpretq_s32_u32(echo_est_v_high));
    165 
    166     far_spectrum_p += 8;
    167     start_adapt_p += 8;
    168     start_stored_p += 8;
    169     echo_est_p += 8;
    170   }
    171   aecm->channelStored[PART_LEN] = aecm->channelAdapt16[PART_LEN];
    172   echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN],
    173                                              far_spectrum[PART_LEN]);
    174 }
    175 
    176 void WebRtcAecm_ResetAdaptiveChannelNeon(AecmCore* aecm) {
    177   assert((uintptr_t)(aecm->channelStored) % 16 == 0);
    178   assert((uintptr_t)(aecm->channelAdapt16) % 16 == 0);
    179   assert((uintptr_t)(aecm->channelAdapt32) % 32 == 0);
    180 
    181   // The C code of following optimized code.
    182   // for (i = 0; i < PART_LEN1; i++) {
    183   //   aecm->channelAdapt16[i] = aecm->channelStored[i];
    184   //   aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32(
    185   //              (int32_t)aecm->channelStored[i], 16);
    186   // }
    187 
    188   int16_t* start_stored_p = aecm->channelStored;
    189   int16_t* start_adapt16_p = aecm->channelAdapt16;
    190   int32_t* start_adapt32_p = aecm->channelAdapt32;
    191   const int16_t* end_stored_p = start_stored_p + PART_LEN;
    192 
    193   int16x8_t stored_v;
    194   int32x4_t adapt32_v_low, adapt32_v_high;
    195 
    196   while (start_stored_p < end_stored_p) {
    197     stored_v = vld1q_s16(start_stored_p);
    198     vst1q_s16(start_adapt16_p, stored_v);
    199 
    200     adapt32_v_low = vshll_n_s16(vget_low_s16(stored_v), 16);
    201     adapt32_v_high = vshll_n_s16(vget_high_s16(stored_v), 16);
    202 
    203     vst1q_s32(start_adapt32_p, adapt32_v_low);
    204     vst1q_s32(start_adapt32_p + 4, adapt32_v_high);
    205 
    206     start_stored_p += 8;
    207     start_adapt16_p += 8;
    208     start_adapt32_p += 8;
    209   }
    210   aecm->channelAdapt16[PART_LEN] = aecm->channelStored[PART_LEN];
    211   aecm->channelAdapt32[PART_LEN] = (int32_t)aecm->channelStored[PART_LEN] << 16;
    212 }
    213