For Android ARMv7 platforms, added a feature of dynamically detecting the existence of Neon,
and when it's present, switch to some functions optimized for Neon at run time.
Review URL: http://webrtc-codereview.appspot.com/268002
git-svn-id: http://webrtc.googlecode.com/svn/trunk@1096 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/Android.mk b/Android.mk
index bd050e6..245c424 100644
--- a/Android.mk
+++ b/Android.mk
@@ -54,6 +54,7 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
+include $(LOCAL_PATH)/../../external/webrtc/android-webrtc.mk
LOCAL_ARM_MODE := arm
LOCAL_MODULE := libwebrtc_audio_preprocessing
@@ -71,6 +72,17 @@
libwebrtc_aecm \
libwebrtc_system_wrappers
+# Add Neon libraries.
+ifneq (,$(filter '-DWEBRTC_DETECT_ARM_NEON',$(MY_WEBRTC_COMMON_DEFS)))
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+ libwebrtc_aecm_neon \
+ libwebrtc_ns_neon
+else ifeq ($(ARCH_ARM_HAVE_NEON),true)
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+ libwebrtc_aecm_neon \
+ libwebrtc_ns_neon
+endif
+
LOCAL_STATIC_LIBRARIES := \
libprotobuf-cpp-2.3.0-lite
diff --git a/android-webrtc.mk b/android-webrtc.mk
index 9a8c861..eb620bb 100644
--- a/android-webrtc.mk
+++ b/android-webrtc.mk
@@ -21,8 +21,9 @@
# '-DWEBRTC_MODULE_UTILITY_VIDEO' [module media_file] [module utility]
ifeq ($(TARGET_ARCH),arm)
MY_WEBRTC_COMMON_DEFS += \
- '-DWEBRTC_ARM_INLINE_CALLS' \
'-DWEBRTC_ARCH_ARM'
+# '-DWEBRTC_DETECT_ARM_NEON' # only used in a build configuration without Neon
+# TODO(kma): figure out if the above define could be moved to NDK build only.
# TODO(kma): test if the code under next two macros works with generic GCC compilers
ifeq ($(ARCH_ARM_HAVE_NEON),true)
diff --git a/src/modules/audio_processing/aecm/Android.mk b/src/modules/audio_processing/aecm/Android.mk
index 916c5a8..c33a957 100644
--- a/src/modules/audio_processing/aecm/Android.mk
+++ b/src/modules/audio_processing/aecm/Android.mk
@@ -6,6 +6,9 @@
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
+#############################
+# Build the non-neon library.
+
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
@@ -21,21 +24,16 @@
aecm_core.c
# Flags passed to both C and C++ files.
-LOCAL_CFLAGS := \
- $(MY_WEBRTC_COMMON_DEFS)
-
-ifeq ($(ARCH_ARM_HAVE_NEON),true)
-LOCAL_SRC_FILES += \
- aecm_core_neon.c
-LOCAL_CFLAGS += \
- $(MY_ARM_CFLAGS_NEON)
-endif
+LOCAL_CFLAGS := $(MY_WEBRTC_COMMON_DEFS)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/interface \
$(LOCAL_PATH)/../utility \
$(LOCAL_PATH)/../../.. \
- $(LOCAL_PATH)/../../../common_audio/signal_processing/include
+ $(LOCAL_PATH)/../../../common_audio/signal_processing/include \
+ $(LOCAL_PATH)/../../../system_wrappers/interface
+
+LOCAL_STATIC_LIBRARIES += libwebrtc_system_wrappers
LOCAL_SHARED_LIBRARIES := \
libcutils \
@@ -46,3 +44,31 @@
include external/stlport/libstlport.mk
endif
include $(BUILD_STATIC_LIBRARY)
+
+#########################
+# Build the neon library.
+
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libwebrtc_aecm_neon
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := aecm_core_neon.c
+
+# Flags passed to both C and C++ files.
+LOCAL_CFLAGS := \
+ $(MY_WEBRTC_COMMON_DEFS) \
+ -mfpu=neon \
+ -flax-vector-conversions
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/interface \
+ $(LOCAL_PATH)/../../.. \
+ $(LOCAL_PATH)/../../../common_audio/signal_processing/include
+
+ifndef NDK_ROOT
+include external/stlport/libstlport.mk
+endif
+include $(BUILD_STATIC_LIBRARY)
diff --git a/src/modules/audio_processing/aecm/aecm_core.c b/src/modules/audio_processing/aecm/aecm_core.c
index 4ad705e..f2e4683 100644
--- a/src/modules/audio_processing/aecm/aecm_core.c
+++ b/src/modules/audio_processing/aecm/aecm_core.c
@@ -13,8 +13,9 @@
#include <assert.h>
#include <stdlib.h>
-#include "echo_control_mobile.h"
+#include "cpu_features_wrapper.h"
#include "delay_estimator_wrapper.h"
+#include "echo_control_mobile.h"
#include "ring_buffer.h"
#include "typedefs.h"
@@ -263,6 +264,13 @@
HANDLE logFile = NULL;
#endif
+// Declare function pointers.
+CalcLinearEnergies WebRtcAecm_CalcLinearEnergies;
+StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel;
+ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel;
+WindowAndFFT WebRtcAecm_WindowAndFFT;
+InverseFFTAndWindow WebRtcAecm_InverseFFTAndWindow;
+
int WebRtcAecm_CreateCore(AecmCore_t **aecmInst)
{
AecmCore_t *aecm = malloc(sizeof(AecmCore_t));
@@ -346,6 +354,194 @@
aecm->mseChannelCount = 0;
}
+static void WindowAndFFTC(WebRtc_Word16* fft,
+ const WebRtc_Word16* time_signal,
+ complex16_t* freq_signal,
+ int time_signal_scaling)
+{
+ int i, j;
+
+ memset(fft, 0, sizeof(WebRtc_Word16) * PART_LEN4);
+ // FFT of signal
+ for (i = 0, j = 0; i < PART_LEN; i++, j += 2)
+ {
+ // Window time domain signal and insert into real part of
+ // transformation array |fft|
+ fft[j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(
+ (time_signal[i] << time_signal_scaling),
+ WebRtcAecm_kSqrtHanning[i],
+ 14);
+ fft[PART_LEN2 + j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(
+ (time_signal[i + PART_LEN] << time_signal_scaling),
+ WebRtcAecm_kSqrtHanning[PART_LEN - i],
+ 14);
+ // Inserting zeros in imaginary parts not necessary since we
+ // initialized the array with all zeros
+ }
+
+ WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
+ WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1);
+
+ // Take only the first PART_LEN2 samples
+ for (i = 0, j = 0; j < PART_LEN2; i += 1, j += 2)
+ {
+ freq_signal[i].real = fft[j];
+
+ // The imaginary part has to switch sign
+ freq_signal[i].imag = - fft[j+1];
+ }
+}
+
+static void InverseFFTAndWindowC(AecmCore_t* aecm,
+ WebRtc_Word16* fft,
+ complex16_t* efw,
+ WebRtc_Word16* output,
+ const WebRtc_Word16* nearendClean)
+{
+ int i, j, outCFFT;
+ WebRtc_Word32 tmp32no1;
+
+ // Synthesis
+ for (i = 1; i < PART_LEN; i++)
+ {
+ j = WEBRTC_SPL_LSHIFT_W32(i, 1);
+ fft[j] = efw[i].real;
+
+ // mirrored data, even
+ fft[PART_LEN4 - j] = efw[i].real;
+ fft[j + 1] = -efw[i].imag;
+
+ //mirrored data, odd
+ fft[PART_LEN4 - (j - 1)] = efw[i].imag;
+ }
+ fft[0] = efw[0].real;
+ fft[1] = -efw[0].imag;
+
+ fft[PART_LEN2] = efw[PART_LEN].real;
+ fft[PART_LEN2 + 1] = -efw[PART_LEN].imag;
+
+ // inverse FFT, result should be scaled with outCFFT
+ WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
+ outCFFT = WebRtcSpl_ComplexIFFT(fft, PART_LEN_SHIFT, 1);
+
+ //take only the real values and scale with outCFFT
+ for (i = 0; i < PART_LEN2; i++)
+ {
+ j = WEBRTC_SPL_LSHIFT_W32(i, 1);
+ fft[i] = fft[j];
+ }
+
+ for (i = 0; i < PART_LEN; i++)
+ {
+ fft[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+ fft[i],
+ WebRtcAecm_kSqrtHanning[i],
+ 14);
+ tmp32no1 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)fft[i],
+ outCFFT - aecm->dfaCleanQDomain);
+ fft[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
+ tmp32no1 + aecm->outBuf[i],
+ WEBRTC_SPL_WORD16_MIN);
+ output[i] = fft[i];
+
+ tmp32no1 = WEBRTC_SPL_MUL_16_16_RSFT(
+ fft[PART_LEN + i],
+ WebRtcAecm_kSqrtHanning[PART_LEN - i],
+ 14);
+ tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1,
+ outCFFT - aecm->dfaCleanQDomain);
+ aecm->outBuf[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(
+ WEBRTC_SPL_WORD16_MAX,
+ tmp32no1,
+ WEBRTC_SPL_WORD16_MIN);
+ }
+
+#ifdef ARM_WINM_LOG_
+ // measure tick end
+ QueryPerformanceCounter((LARGE_INTEGER*)&end);
+ diff__ = ((end - start) * 1000) / (freq/1000);
+ milliseconds = (unsigned int)(diff__ & 0xffffffff);
+ WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL);
+#endif
+
+ // Copy the current block to the old position (aecm->outBuf is shifted elsewhere)
+ memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(WebRtc_Word16) * PART_LEN);
+ memcpy(aecm->dBufNoisy, aecm->dBufNoisy + PART_LEN, sizeof(WebRtc_Word16) * PART_LEN);
+ if (nearendClean != NULL)
+ {
+ memcpy(aecm->dBufClean, aecm->dBufClean + PART_LEN, sizeof(WebRtc_Word16) * PART_LEN);
+ }
+}
+
+static void CalcLinearEnergiesC(AecmCore_t* aecm,
+ const WebRtc_UWord16* far_spectrum,
+ WebRtc_Word32* echo_est,
+ WebRtc_UWord32* far_energy,
+ WebRtc_UWord32* echo_energy_adapt,
+ WebRtc_UWord32* echo_energy_stored)
+{
+ int i;
+
+ // Get energy for the delayed far end signal and estimated
+ // echo using both stored and adapted channels.
+ for (i = 0; i < PART_LEN1; i++)
+ {
+ echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
+ far_spectrum[i]);
+ (*far_energy) += (WebRtc_UWord32)(far_spectrum[i]);
+ (*echo_energy_adapt) += WEBRTC_SPL_UMUL_16_16(aecm->channelAdapt16[i],
+ far_spectrum[i]);
+ (*echo_energy_stored) += (WebRtc_UWord32)echo_est[i];
+ }
+}
+
+static void StoreAdaptiveChannelC(AecmCore_t* aecm,
+ const WebRtc_UWord16* far_spectrum,
+ WebRtc_Word32* echo_est)
+{
+ int i;
+
+ // During startup we store the channel every block.
+ memcpy(aecm->channelStored, aecm->channelAdapt16, sizeof(WebRtc_Word16) * PART_LEN1);
+ // Recalculate echo estimate
+ for (i = 0; i < PART_LEN; i += 4)
+ {
+ echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
+ far_spectrum[i]);
+ echo_est[i + 1] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1],
+ far_spectrum[i + 1]);
+ echo_est[i + 2] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2],
+ far_spectrum[i + 2]);
+ echo_est[i + 3] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3],
+ far_spectrum[i + 3]);
+ }
+ echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
+ far_spectrum[i]);
+}
+
+static void ResetAdaptiveChannelC(AecmCore_t* aecm)
+{
+ int i;
+
+ // The stored channel has a significantly lower MSE than the adaptive one for
+ // two consecutive calculations. Reset the adaptive channel.
+ memcpy(aecm->channelAdapt16, aecm->channelStored,
+ sizeof(WebRtc_Word16) * PART_LEN1);
+ // Restore the W32 channel
+ for (i = 0; i < PART_LEN; i += 4)
+ {
+ aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32(
+ (WebRtc_Word32)aecm->channelStored[i], 16);
+ aecm->channelAdapt32[i + 1] = WEBRTC_SPL_LSHIFT_W32(
+ (WebRtc_Word32)aecm->channelStored[i + 1], 16);
+ aecm->channelAdapt32[i + 2] = WEBRTC_SPL_LSHIFT_W32(
+ (WebRtc_Word32)aecm->channelStored[i + 2], 16);
+ aecm->channelAdapt32[i + 3] = WEBRTC_SPL_LSHIFT_W32(
+ (WebRtc_Word32)aecm->channelStored[i + 3], 16);
+ }
+ aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)aecm->channelStored[i], 16);
+}
+
// WebRtcAecm_InitCore(...)
//
// This function initializes the AECM instant created with WebRtcAecm_CreateCore(...)
@@ -463,6 +659,23 @@
assert(PART_LEN % 16 == 0);
+ // Initialize function pointers.
+ WebRtcAecm_WindowAndFFT = WindowAndFFTC;
+ WebRtcAecm_InverseFFTAndWindow = InverseFFTAndWindowC;
+ WebRtcAecm_CalcLinearEnergies = CalcLinearEnergiesC;
+ WebRtcAecm_StoreAdaptiveChannel = StoreAdaptiveChannelC;
+ WebRtcAecm_ResetAdaptiveChannel = ResetAdaptiveChannelC;
+
+#ifdef WEBRTC_DETECT_ARM_NEON
+ uint64_t features = WebRtc_GetCPUFeaturesARM();
+ if ((features & kCPUFeatureNEON) != 0)
+ {
+ WebRtcAecm_InitNeon();
+ }
+#elif defined(WEBRTC_ARCH_ARM_NEON)
+ WebRtcAecm_InitNeon();
+#endif
+
return 0;
}
@@ -1890,194 +2103,3 @@
aecm->farBufReadPos += readLen;
}
-#if !(defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM_NEON))
-
-void WebRtcAecm_WindowAndFFT(WebRtc_Word16* fft,
- const WebRtc_Word16* time_signal,
- complex16_t* freq_signal,
- int time_signal_scaling)
-{
- int i, j;
-
- memset(fft, 0, sizeof(WebRtc_Word16) * PART_LEN4);
- // FFT of signal
- for (i = 0, j = 0; i < PART_LEN; i++, j += 2)
- {
- // Window time domain signal and insert into real part of
- // transformation array |fft|
- fft[j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(
- (time_signal[i] << time_signal_scaling),
- WebRtcAecm_kSqrtHanning[i],
- 14);
- fft[PART_LEN2 + j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(
- (time_signal[i + PART_LEN] << time_signal_scaling),
- WebRtcAecm_kSqrtHanning[PART_LEN - i],
- 14);
- // Inserting zeros in imaginary parts not necessary since we
- // initialized the array with all zeros
- }
-
- WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
- WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1);
-
- // Take only the first PART_LEN2 samples
- for (i = 0, j = 0; j < PART_LEN2; i += 1, j += 2)
- {
- freq_signal[i].real = fft[j];
-
- // The imaginary part has to switch sign
- freq_signal[i].imag = - fft[j+1];
- }
-}
-
-void WebRtcAecm_InverseFFTAndWindow(AecmCore_t* aecm,
- WebRtc_Word16* fft,
- complex16_t* efw,
- WebRtc_Word16* output,
- const WebRtc_Word16* nearendClean)
-{
- int i, j, outCFFT;
- WebRtc_Word32 tmp32no1;
-
- // Synthesis
- for (i = 1; i < PART_LEN; i++)
- {
- j = WEBRTC_SPL_LSHIFT_W32(i, 1);
- fft[j] = efw[i].real;
-
- // mirrored data, even
- fft[PART_LEN4 - j] = efw[i].real;
- fft[j + 1] = -efw[i].imag;
-
- //mirrored data, odd
- fft[PART_LEN4 - (j - 1)] = efw[i].imag;
- }
- fft[0] = efw[0].real;
- fft[1] = -efw[0].imag;
-
- fft[PART_LEN2] = efw[PART_LEN].real;
- fft[PART_LEN2 + 1] = -efw[PART_LEN].imag;
-
- // inverse FFT, result should be scaled with outCFFT
- WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
- outCFFT = WebRtcSpl_ComplexIFFT(fft, PART_LEN_SHIFT, 1);
-
- //take only the real values and scale with outCFFT
- for (i = 0; i < PART_LEN2; i++)
- {
- j = WEBRTC_SPL_LSHIFT_W32(i, 1);
- fft[i] = fft[j];
- }
-
- for (i = 0; i < PART_LEN; i++)
- {
- fft[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
- fft[i],
- WebRtcAecm_kSqrtHanning[i],
- 14);
- tmp32no1 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)fft[i],
- outCFFT - aecm->dfaCleanQDomain);
- fft[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
- tmp32no1 + aecm->outBuf[i],
- WEBRTC_SPL_WORD16_MIN);
- output[i] = fft[i];
-
- tmp32no1 = WEBRTC_SPL_MUL_16_16_RSFT(
- fft[PART_LEN + i],
- WebRtcAecm_kSqrtHanning[PART_LEN - i],
- 14);
- tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1,
- outCFFT - aecm->dfaCleanQDomain);
- aecm->outBuf[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(
- WEBRTC_SPL_WORD16_MAX,
- tmp32no1,
- WEBRTC_SPL_WORD16_MIN);
- }
-
-#ifdef ARM_WINM_LOG_
- // measure tick end
- QueryPerformanceCounter((LARGE_INTEGER*)&end);
- diff__ = ((end - start) * 1000) / (freq/1000);
- milliseconds = (unsigned int)(diff__ & 0xffffffff);
- WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL);
-#endif
-
- // Copy the current block to the old position (aecm->outBuf is shifted elsewhere)
- memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(WebRtc_Word16) * PART_LEN);
- memcpy(aecm->dBufNoisy, aecm->dBufNoisy + PART_LEN, sizeof(WebRtc_Word16) * PART_LEN);
- if (nearendClean != NULL)
- {
- memcpy(aecm->dBufClean, aecm->dBufClean + PART_LEN, sizeof(WebRtc_Word16) * PART_LEN);
- }
-}
-
-void WebRtcAecm_CalcLinearEnergies(AecmCore_t* aecm,
- const WebRtc_UWord16* far_spectrum,
- WebRtc_Word32* echo_est,
- WebRtc_UWord32* far_energy,
- WebRtc_UWord32* echo_energy_adapt,
- WebRtc_UWord32* echo_energy_stored)
-{
- int i;
-
- // Get energy for the delayed far end signal and estimated
- // echo using both stored and adapted channels.
- for (i = 0; i < PART_LEN1; i++)
- {
- echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
- far_spectrum[i]);
- (*far_energy) += (WebRtc_UWord32)(far_spectrum[i]);
- (*echo_energy_adapt) += WEBRTC_SPL_UMUL_16_16(aecm->channelAdapt16[i],
- far_spectrum[i]);
- (*echo_energy_stored) += (WebRtc_UWord32)echo_est[i];
- }
-}
-
-void WebRtcAecm_StoreAdaptiveChannel(AecmCore_t* aecm,
- const WebRtc_UWord16* far_spectrum,
- WebRtc_Word32* echo_est)
-{
- int i;
-
- // During startup we store the channel every block.
- memcpy(aecm->channelStored, aecm->channelAdapt16, sizeof(WebRtc_Word16) * PART_LEN1);
- // Recalculate echo estimate
- for (i = 0; i < PART_LEN; i += 4)
- {
- echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
- far_spectrum[i]);
- echo_est[i + 1] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1],
- far_spectrum[i + 1]);
- echo_est[i + 2] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2],
- far_spectrum[i + 2]);
- echo_est[i + 3] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3],
- far_spectrum[i + 3]);
- }
- echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
- far_spectrum[i]);
-}
-
-void WebRtcAecm_ResetAdaptiveChannel(AecmCore_t* aecm)
-{
- int i;
-
- // The stored channel has a significantly lower MSE than the adaptive one for
- // two consecutive calculations. Reset the adaptive channel.
- memcpy(aecm->channelAdapt16, aecm->channelStored,
- sizeof(WebRtc_Word16) * PART_LEN1);
- // Restore the W32 channel
- for (i = 0; i < PART_LEN; i += 4)
- {
- aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32(
- (WebRtc_Word32)aecm->channelStored[i], 16);
- aecm->channelAdapt32[i + 1] = WEBRTC_SPL_LSHIFT_W32(
- (WebRtc_Word32)aecm->channelStored[i + 1], 16);
- aecm->channelAdapt32[i + 2] = WEBRTC_SPL_LSHIFT_W32(
- (WebRtc_Word32)aecm->channelStored[i + 2], 16);
- aecm->channelAdapt32[i + 3] = WEBRTC_SPL_LSHIFT_W32(
- (WebRtc_Word32)aecm->channelStored[i + 3], 16);
- }
- aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)aecm->channelStored[i], 16);
-}
-
-#endif // !(defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM_NEON))
diff --git a/src/modules/audio_processing/aecm/aecm_core.h b/src/modules/audio_processing/aecm/aecm_core.h
index dede6d3..0ec62ec 100644
--- a/src/modules/audio_processing/aecm/aecm_core.h
+++ b/src/modules/audio_processing/aecm/aecm_core.h
@@ -332,32 +332,44 @@
void WebRtcAecm_FetchFarFrame(AecmCore_t * const aecm, WebRtc_Word16 * const farend,
const int farLen, const int knownDelay);
-///////////////////////////////////////////////////////////////////////////////////////////////
-// Some internal functions shared by ARM NEON and generic C code:
+///////////////////////////////////////////////////////////////////////////////
+// Some function pointers, for internal functions shared by ARM NEON and
+// generic C code.
//
+typedef void (*CalcLinearEnergies)(
+ AecmCore_t* aecm,
+ const WebRtc_UWord16* far_spectrum,
+ WebRtc_Word32* echoEst,
+ WebRtc_UWord32* far_energy,
+ WebRtc_UWord32* echo_energy_adapt,
+ WebRtc_UWord32* echo_energy_stored);
+extern CalcLinearEnergies WebRtcAecm_CalcLinearEnergies;
-void WebRtcAecm_CalcLinearEnergies(AecmCore_t* aecm,
- const WebRtc_UWord16* far_spectrum,
- WebRtc_Word32* echoEst,
- WebRtc_UWord32* far_energy,
- WebRtc_UWord32* echo_energy_adapt,
- WebRtc_UWord32* echo_energy_stored);
+typedef void (*StoreAdaptiveChannel)(
+ AecmCore_t* aecm,
+ const WebRtc_UWord16* far_spectrum,
+ WebRtc_Word32* echo_est);
+extern StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel;
-void WebRtcAecm_StoreAdaptiveChannel(AecmCore_t* aecm,
- const WebRtc_UWord16* far_spectrum,
- WebRtc_Word32* echo_est);
+typedef void (*ResetAdaptiveChannel)(AecmCore_t* aecm);
+extern ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel;
-void WebRtcAecm_ResetAdaptiveChannel(AecmCore_t *aecm);
+typedef void (*WindowAndFFT)(
+ WebRtc_Word16* fft,
+ const WebRtc_Word16* time_signal,
+ complex16_t* freq_signal,
+ int time_signal_scaling);
+extern WindowAndFFT WebRtcAecm_WindowAndFFT;
-void WebRtcAecm_WindowAndFFT(WebRtc_Word16* fft,
- const WebRtc_Word16* time_signal,
- complex16_t* freq_signal,
- int time_signal_scaling);
+typedef void (*InverseFFTAndWindow)(
+ AecmCore_t* aecm,
+ WebRtc_Word16* fft, complex16_t* efw,
+ WebRtc_Word16* output,
+ const WebRtc_Word16* nearendClean);
+extern InverseFFTAndWindow WebRtcAecm_InverseFFTAndWindow;
-void WebRtcAecm_InverseFFTAndWindow(AecmCore_t* aecm,
- WebRtc_Word16* fft,
- complex16_t* efw,
- WebRtc_Word16* output,
- const WebRtc_Word16* nearendClean);
+// Initialization of the above function pointers for ARM Neon.
+void WebRtcAecm_InitNeon(void);
+
#endif
diff --git a/src/modules/audio_processing/aecm/aecm_core_neon.c b/src/modules/audio_processing/aecm/aecm_core_neon.c
index 86ced1e..ab448b4 100644
--- a/src/modules/audio_processing/aecm/aecm_core_neon.c
+++ b/src/modules/audio_processing/aecm/aecm_core_neon.c
@@ -7,7 +7,6 @@
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
-#if defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM_NEON)
#include "aecm_core.h"
@@ -16,299 +15,289 @@
// Square root of Hanning window in Q14.
-static const WebRtc_Word16 kSqrtHanningReversed[] __attribute__ ((aligned (8))) = {
- 16384, 16373, 16354, 16325,
- 16286, 16237, 16179, 16111,
- 16034, 15947, 15851, 15746,
- 15631, 15506, 15373, 15231,
- 15079, 14918, 14749, 14571,
- 14384, 14189, 13985, 13773,
- 13553, 13325, 13089, 12845,
- 12594, 12335, 12068, 11795,
- 11514, 11227, 10933, 10633,
- 10326, 10013, 9695, 9370,
- 9040, 8705, 8364, 8019,
- 7668, 7313, 6954, 6591,
- 6224, 5853, 5478, 5101,
- 4720, 4337, 3951, 3562,
- 3172, 2780, 2386, 1990,
- 1594, 1196, 798, 399
+static const WebRtc_Word16 kSqrtHanningReversed[] __attribute__((aligned(8))) = {
+ 16384, 16373, 16354, 16325,
+ 16286, 16237, 16179, 16111,
+ 16034, 15947, 15851, 15746,
+ 15631, 15506, 15373, 15231,
+ 15079, 14918, 14749, 14571,
+ 14384, 14189, 13985, 13773,
+ 13553, 13325, 13089, 12845,
+ 12594, 12335, 12068, 11795,
+ 11514, 11227, 10933, 10633,
+ 10326, 10013, 9695, 9370,
+ 9040, 8705, 8364, 8019,
+ 7668, 7313, 6954, 6591,
+ 6224, 5853, 5478, 5101,
+ 4720, 4337, 3951, 3562,
+ 3172, 2780, 2386, 1990,
+ 1594, 1196, 798, 399
};
-void WebRtcAecm_WindowAndFFT(WebRtc_Word16* fft,
+static void WindowAndFFTNeon(WebRtc_Word16* fft,
const WebRtc_Word16* time_signal,
complex16_t* freq_signal,
- int time_signal_scaling)
-{
- int i, j;
+ int time_signal_scaling) {
+ int i, j;
- int16x4_t tmp16x4_scaling = vdup_n_s16(time_signal_scaling);
- __asm__("vmov.i16 d21, #0" ::: "d21");
+ int16x4_t tmp16x4_scaling = vdup_n_s16(time_signal_scaling);
+ __asm__("vmov.i16 d21, #0" ::: "d21");
- for(i = 0, j = 0; i < PART_LEN; i += 4, j += 8)
- {
- int16x4_t tmp16x4_0;
- int16x4_t tmp16x4_1;
- int32x4_t tmp32x4_0;
+ for (i = 0, j = 0; i < PART_LEN; i += 4, j += 8) {
+ int16x4_t tmp16x4_0;
+ int16x4_t tmp16x4_1;
+ int32x4_t tmp32x4_0;
- /* Window near end */
- // fft[j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((time_signal[i]
- // << time_signal_scaling), WebRtcAecm_kSqrtHanning[i], 14);
- __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&time_signal[i]));
- tmp16x4_0 = vshl_s16(tmp16x4_0, tmp16x4_scaling);
+ /* Window near end */
+ // fft[j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((time_signal[i]
+ // << time_signal_scaling), WebRtcAecm_kSqrtHanning[i], 14);
+ __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&time_signal[i]));
+ tmp16x4_0 = vshl_s16(tmp16x4_0, tmp16x4_scaling);
- __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&WebRtcAecm_kSqrtHanning[i]));
- tmp32x4_0 = vmull_s16(tmp16x4_0, tmp16x4_1);
+ __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&WebRtcAecm_kSqrtHanning[i]));
+ tmp32x4_0 = vmull_s16(tmp16x4_0, tmp16x4_1);
- __asm__("vshrn.i32 d20, %q0, #14" : : "w"(tmp32x4_0) : "d20");
- __asm__("vst2.16 {d20, d21}, [%0, :128]" : : "r"(&fft[j]) : "q10");
+ __asm__("vshrn.i32 d20, %q0, #14" : : "w"(tmp32x4_0) : "d20");
+ __asm__("vst2.16 {d20, d21}, [%0, :128]" : : "r"(&fft[j]) : "q10");
- // fft[PART_LEN2 + j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(
- // (time_signal[PART_LEN + i] << time_signal_scaling),
- // WebRtcAecm_kSqrtHanning[PART_LEN - i], 14);
- __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&time_signal[i + PART_LEN]));
- tmp16x4_0 = vshl_s16(tmp16x4_0, tmp16x4_scaling);
+ // fft[PART_LEN2 + j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(
+ // (time_signal[PART_LEN + i] << time_signal_scaling),
+ // WebRtcAecm_kSqrtHanning[PART_LEN - i], 14);
+ __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&time_signal[i + PART_LEN]));
+ tmp16x4_0 = vshl_s16(tmp16x4_0, tmp16x4_scaling);
- __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&kSqrtHanningReversed[i]));
- tmp32x4_0 = vmull_s16(tmp16x4_0, tmp16x4_1);
+ __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&kSqrtHanningReversed[i]));
+ tmp32x4_0 = vmull_s16(tmp16x4_0, tmp16x4_1);
- __asm__("vshrn.i32 d20, %q0, #14" : : "w"(tmp32x4_0) : "d20");
- __asm__("vst2.16 {d20, d21}, [%0, :128]" : : "r"(&fft[PART_LEN2 + j]) : "q10");
- }
+ __asm__("vshrn.i32 d20, %q0, #14" : : "w"(tmp32x4_0) : "d20");
+ __asm__("vst2.16 {d20, d21}, [%0, :128]" : : "r"(&fft[PART_LEN2 + j]) : "q10");
+ }
- WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
- WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1);
+ WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
+ WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1);
- // Take only the first PART_LEN2 samples, and switch the sign of the imaginary part.
- for(i = 0, j = 0; j < PART_LEN2; i += 8, j += 16)
- {
- __asm__("vld2.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&fft[j]) : "q10", "q11");
- __asm__("vneg.s16 d22, d22" : : : "q10");
- __asm__("vneg.s16 d23, d23" : : : "q11");
- __asm__("vst2.16 {d20, d21, d22, d23}, [%0, :256]" : :
+ // Take only the first PART_LEN2 samples, and switch the sign of the imaginary part.
+ for (i = 0, j = 0; j < PART_LEN2; i += 8, j += 16) {
+ __asm__("vld2.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&fft[j]) : "q10", "q11");
+ __asm__("vneg.s16 d22, d22" : : : "q10");
+ __asm__("vneg.s16 d23, d23" : : : "q11");
+ __asm__("vst2.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&freq_signal[i].real): "q10", "q11");
- }
+ }
}
-void WebRtcAecm_InverseFFTAndWindow(AecmCore_t* aecm,
- WebRtc_Word16* fft,
- complex16_t* efw,
- WebRtc_Word16* output,
- const WebRtc_Word16* nearendClean)
-{
- int i, j, outCFFT;
- WebRtc_Word32 tmp32no1;
+static void InverseFFTAndWindowNeon(AecmCore_t* aecm,
+ WebRtc_Word16* fft,
+ complex16_t* efw,
+ WebRtc_Word16* output,
+ const WebRtc_Word16* nearendClean) {
+ int i, j, outCFFT;
+ WebRtc_Word32 tmp32no1;
- // Synthesis
- for(i = 0, j = 0; i < PART_LEN; i += 4, j += 8)
- {
- // We overwrite two more elements in fft[], but it's ok.
- __asm__("vld2.16 {d20, d21}, [%0, :128]" : : "r"(&(efw[i].real)) : "q10");
- __asm__("vmov q11, q10" : : : "q10", "q11");
+ // Synthesis
+ for (i = 0, j = 0; i < PART_LEN; i += 4, j += 8) {
+ // We overwrite two more elements in fft[], but it's ok.
+ __asm__("vld2.16 {d20, d21}, [%0, :128]" : : "r"(&(efw[i].real)) : "q10");
+ __asm__("vmov q11, q10" : : : "q10", "q11");
- __asm__("vneg.s16 d23, d23" : : : "q11");
- __asm__("vst2.16 {d22, d23}, [%0, :128]" : : "r"(&fft[j]): "q11");
+ __asm__("vneg.s16 d23, d23" : : : "q11");
+ __asm__("vst2.16 {d22, d23}, [%0, :128]" : : "r"(&fft[j]): "q11");
- __asm__("vrev64.16 q10, q10" : : : "q10");
- __asm__("vst2.16 {d20, d21}, [%0]" : : "r"(&fft[PART_LEN4 - j - 6]): "q10");
- }
+ __asm__("vrev64.16 q10, q10" : : : "q10");
+ __asm__("vst2.16 {d20, d21}, [%0]" : : "r"(&fft[PART_LEN4 - j - 6]): "q10");
+ }
- fft[PART_LEN2] = efw[PART_LEN].real;
- fft[PART_LEN2 + 1] = -efw[PART_LEN].imag;
+ fft[PART_LEN2] = efw[PART_LEN].real;
+ fft[PART_LEN2 + 1] = -efw[PART_LEN].imag;
- // Inverse FFT, result should be scaled with outCFFT.
- WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
- outCFFT = WebRtcSpl_ComplexIFFT(fft, PART_LEN_SHIFT, 1);
+ // Inverse FFT, result should be scaled with outCFFT.
+ WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
+ outCFFT = WebRtcSpl_ComplexIFFT(fft, PART_LEN_SHIFT, 1);
- // Take only the real values and scale with outCFFT.
- for (i = 0, j = 0; i < PART_LEN2; i += 8, j+= 16)
- {
- __asm__("vld2.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&fft[j]) : "q10", "q11");
- __asm__("vst1.16 {d20, d21}, [%0, :128]" : : "r"(&fft[i]): "q10");
- }
+ // Take only the real values and scale with outCFFT.
+ for (i = 0, j = 0; i < PART_LEN2; i += 8, j += 16) {
+ __asm__("vld2.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&fft[j]) : "q10", "q11");
+ __asm__("vst1.16 {d20, d21}, [%0, :128]" : : "r"(&fft[i]): "q10");
+ }
- int32x4_t tmp32x4_2;
- __asm__("vdup.32 %q0, %1" : "=w"(tmp32x4_2) : "r"((WebRtc_Word32)
- (outCFFT - aecm->dfaCleanQDomain)));
- for (i = 0; i < PART_LEN; i += 4)
- {
- int16x4_t tmp16x4_0;
- int16x4_t tmp16x4_1;
- int32x4_t tmp32x4_0;
- int32x4_t tmp32x4_1;
+ int32x4_t tmp32x4_2;
+ __asm__("vdup.32 %q0, %1" : "=w"(tmp32x4_2) : "r"((WebRtc_Word32)
+ (outCFFT - aecm->dfaCleanQDomain)));
+ for (i = 0; i < PART_LEN; i += 4) {
+ int16x4_t tmp16x4_0;
+ int16x4_t tmp16x4_1;
+ int32x4_t tmp32x4_0;
+ int32x4_t tmp32x4_1;
- // fft[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
- // fft[i], WebRtcAecm_kSqrtHanning[i], 14);
- __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&fft[i]));
- __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&WebRtcAecm_kSqrtHanning[i]));
- __asm__("vmull.s16 %q0, %P1, %P2" : "=w"(tmp32x4_0) : "w"(tmp16x4_0), "w"(tmp16x4_1));
- __asm__("vrshr.s32 %q0, %q1, #14" : "=w"(tmp32x4_0) : "0"(tmp32x4_0));
+ // fft[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+ // fft[i], WebRtcAecm_kSqrtHanning[i], 14);
+ __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&fft[i]));
+ __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&WebRtcAecm_kSqrtHanning[i]));
+ __asm__("vmull.s16 %q0, %P1, %P2" : "=w"(tmp32x4_0) : "w"(tmp16x4_0), "w"(tmp16x4_1));
+ __asm__("vrshr.s32 %q0, %q1, #14" : "=w"(tmp32x4_0) : "0"(tmp32x4_0));
- // tmp32no1 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)fft[i],
- // outCFFT - aecm->dfaCleanQDomain);
- __asm__("vshl.s32 %q0, %q1, %q2" : "=w"(tmp32x4_0) : "0"(tmp32x4_0), "w"(tmp32x4_2));
+ // tmp32no1 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)fft[i],
+ // outCFFT - aecm->dfaCleanQDomain);
+ __asm__("vshl.s32 %q0, %q1, %q2" : "=w"(tmp32x4_0) : "0"(tmp32x4_0), "w"(tmp32x4_2));
- // fft[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
- // tmp32no1 + outBuf[i], WEBRTC_SPL_WORD16_MIN);
- // output[i] = fft[i];
- __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&aecm->outBuf[i]));
- __asm__("vmovl.s16 %q0, %P1" : "=w"(tmp32x4_1) : "w"(tmp16x4_0));
- __asm__("vadd.i32 %q0, %q1" : : "w"(tmp32x4_0), "w"(tmp32x4_1));
- __asm__("vqshrn.s32 %P0, %q1, #0" : "=w"(tmp16x4_0) : "w"(tmp32x4_0));
- __asm__("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&fft[i]));
- __asm__("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&output[i]));
+ // fft[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
+ // tmp32no1 + outBuf[i], WEBRTC_SPL_WORD16_MIN);
+ // output[i] = fft[i];
+ __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&aecm->outBuf[i]));
+ __asm__("vmovl.s16 %q0, %P1" : "=w"(tmp32x4_1) : "w"(tmp16x4_0));
+ __asm__("vadd.i32 %q0, %q1" : : "w"(tmp32x4_0), "w"(tmp32x4_1));
+ __asm__("vqshrn.s32 %P0, %q1, #0" : "=w"(tmp16x4_0) : "w"(tmp32x4_0));
+ __asm__("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&fft[i]));
+ __asm__("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&output[i]));
- // tmp32no1 = WEBRTC_SPL_MUL_16_16_RSFT(
- // fft[PART_LEN + i], WebRtcAecm_kSqrtHanning[PART_LEN - i], 14);
- __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&fft[PART_LEN + i]));
- __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&kSqrtHanningReversed[i]));
- __asm__("vmull.s16 %q0, %P1, %P2" : "=w"(tmp32x4_0) : "w"(tmp16x4_0), "w"(tmp16x4_1));
- __asm__("vshr.s32 %q0, %q1, #14" : "=w"(tmp32x4_0) : "0"(tmp32x4_0));
+ // tmp32no1 = WEBRTC_SPL_MUL_16_16_RSFT(
+ // fft[PART_LEN + i], WebRtcAecm_kSqrtHanning[PART_LEN - i], 14);
+ __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&fft[PART_LEN + i]));
+ __asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&kSqrtHanningReversed[i]));
+ __asm__("vmull.s16 %q0, %P1, %P2" : "=w"(tmp32x4_0) : "w"(tmp16x4_0), "w"(tmp16x4_1));
+ __asm__("vshr.s32 %q0, %q1, #14" : "=w"(tmp32x4_0) : "0"(tmp32x4_0));
- // tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, outCFFT - aecm->dfaCleanQDomain);
- __asm__("vshl.s32 %q0, %q1, %q2" : "=w"(tmp32x4_0) : "0"(tmp32x4_0), "w"(tmp32x4_2));
- // outBuf[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(
- // WEBRTC_SPL_WORD16_MAX, tmp32no1, WEBRTC_SPL_WORD16_MIN);
- __asm__("vqshrn.s32 %P0, %q1, #0" : "=w"(tmp16x4_0) : "w"(tmp32x4_0));
- __asm__("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&aecm->outBuf[i]));
- }
+ // tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, outCFFT - aecm->dfaCleanQDomain);
+ __asm__("vshl.s32 %q0, %q1, %q2" : "=w"(tmp32x4_0) : "0"(tmp32x4_0), "w"(tmp32x4_2));
+ // outBuf[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(
+ // WEBRTC_SPL_WORD16_MAX, tmp32no1, WEBRTC_SPL_WORD16_MIN);
+ __asm__("vqshrn.s32 %P0, %q1, #0" : "=w"(tmp16x4_0) : "w"(tmp32x4_0));
+ __asm__("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&aecm->outBuf[i]));
+ }
- // Copy the current block to the old position (outBuf is shifted elsewhere).
- for (i = 0; i < PART_LEN; i += 16)
- {
- __asm__("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
+ // Copy the current block to the old position (outBuf is shifted elsewhere).
+ for (i = 0; i < PART_LEN; i += 16) {
+ __asm__("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->xBuf[i + PART_LEN]) : "q10");
- __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&aecm->xBuf[i]): "q10");
- }
- for (i = 0; i < PART_LEN; i += 16)
- {
- __asm__("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
+ __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&aecm->xBuf[i]): "q10");
+ }
+ for (i = 0; i < PART_LEN; i += 16) {
+ __asm__("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->dBufNoisy[i + PART_LEN]) : "q10");
- __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
+ __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->dBufNoisy[i]): "q10");
+ }
+ if (nearendClean != NULL) {
+ for (i = 0; i < PART_LEN; i += 16) {
+ __asm__("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
+ "r"(&aecm->dBufClean[i + PART_LEN]) : "q10");
+ __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
+ "r"(&aecm->dBufClean[i]): "q10");
}
- if (nearendClean != NULL) {
- for (i = 0; i < PART_LEN; i += 16)
- {
- __asm__("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
- "r"(&aecm->dBufClean[i + PART_LEN]) : "q10");
- __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
- "r"(&aecm->dBufClean[i]): "q10");
- }
- }
+ }
}
-void WebRtcAecm_CalcLinearEnergies(AecmCore_t* aecm,
+static void CalcLinearEnergiesNeon(AecmCore_t* aecm,
const WebRtc_UWord16* far_spectrum,
WebRtc_Word32* echo_est,
WebRtc_UWord32* far_energy,
WebRtc_UWord32* echo_energy_adapt,
- WebRtc_UWord32* echo_energy_stored)
-{
- int i;
+ WebRtc_UWord32* echo_energy_stored) {
+ int i;
- register WebRtc_UWord32 far_energy_r;
- register WebRtc_UWord32 echo_energy_stored_r;
- register WebRtc_UWord32 echo_energy_adapt_r;
- uint32x4_t tmp32x4_0;
+ register WebRtc_UWord32 far_energy_r;
+ register WebRtc_UWord32 echo_energy_stored_r;
+ register WebRtc_UWord32 echo_energy_adapt_r;
+ uint32x4_t tmp32x4_0;
- __asm__("vmov.i32 q14, #0" : : : "q14"); // far_energy
- __asm__("vmov.i32 q8, #0" : : : "q8"); // echo_energy_stored
- __asm__("vmov.i32 q9, #0" : : : "q9"); // echo_energy_adapt
+ __asm__("vmov.i32 q14, #0" : : : "q14"); // far_energy
+ __asm__("vmov.i32 q8, #0" : : : "q8"); // echo_energy_stored
+ __asm__("vmov.i32 q9, #0" : : : "q9"); // echo_energy_adapt
- for(i = 0; i < PART_LEN -7; i += 8)
- {
- // far_energy += (WebRtc_UWord32)(far_spectrum[i]);
- __asm__("vld1.16 {d26, d27}, [%0]" : : "r"(&far_spectrum[i]) : "q13");
- __asm__("vaddw.u16 q14, q14, d26" : : : "q14", "q13");
- __asm__("vaddw.u16 q14, q14, d27" : : : "q14", "q13");
-
- // Get estimated echo energies for adaptive channel and stored channel.
- // echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
- __asm__("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelStored[i]) : "q12");
- __asm__("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
- __asm__("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
- __asm__("vst1.32 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&echo_est[i]):
- "q10", "q11");
-
- // echo_energy_stored += (WebRtc_UWord32)echoEst[i];
- __asm__("vadd.u32 q8, q10" : : : "q10", "q8");
- __asm__("vadd.u32 q8, q11" : : : "q11", "q8");
-
- // echo_energy_adapt += WEBRTC_SPL_UMUL_16_16(
- // aecm->channelAdapt16[i], far_spectrum[i]);
- __asm__("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelAdapt16[i]) : "q12");
- __asm__("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
- __asm__("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
- __asm__("vadd.u32 q9, q10" : : : "q9", "q15");
- __asm__("vadd.u32 q9, q11" : : : "q9", "q11");
- }
-
- __asm__("vadd.u32 d28, d29" : : : "q14");
- __asm__("vpadd.u32 d28, d28" : : : "q14");
- __asm__("vmov.32 %0, d28[0]" : "=r"(far_energy_r): : "q14");
-
- __asm__("vadd.u32 d18, d19" : : : "q9");
- __asm__("vpadd.u32 d18, d18" : : : "q9");
- __asm__("vmov.32 %0, d18[0]" : "=r"(echo_energy_adapt_r): : "q9");
-
- __asm__("vadd.u32 d16, d17" : : : "q8");
- __asm__("vpadd.u32 d16, d16" : : : "q8");
- __asm__("vmov.32 %0, d16[0]" : "=r"(echo_energy_stored_r): : "q8");
+ for (i = 0; i < PART_LEN - 7; i += 8) {
+ // far_energy += (WebRtc_UWord32)(far_spectrum[i]);
+ __asm__("vld1.16 {d26, d27}, [%0]" : : "r"(&far_spectrum[i]) : "q13");
+ __asm__("vaddw.u16 q14, q14, d26" : : : "q14", "q13");
+ __asm__("vaddw.u16 q14, q14, d27" : : : "q14", "q13");
// Get estimated echo energies for adaptive channel and stored channel.
- echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
- *echo_energy_stored = echo_energy_stored_r + (WebRtc_UWord32)echo_est[i];
- *far_energy = far_energy_r + (WebRtc_UWord32)(far_spectrum[i]);
- *echo_energy_adapt = echo_energy_adapt_r + WEBRTC_SPL_UMUL_16_16(
- aecm->channelAdapt16[i], far_spectrum[i]);
+ // echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
+ __asm__("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelStored[i]) : "q12");
+ __asm__("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
+ __asm__("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
+ __asm__("vst1.32 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&echo_est[i]):
+ "q10", "q11");
+
+ // echo_energy_stored += (WebRtc_UWord32)echoEst[i];
+ __asm__("vadd.u32 q8, q10" : : : "q10", "q8");
+ __asm__("vadd.u32 q8, q11" : : : "q11", "q8");
+
+ // echo_energy_adapt += WEBRTC_SPL_UMUL_16_16(
+ // aecm->channelAdapt16[i], far_spectrum[i]);
+ __asm__("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelAdapt16[i]) : "q12");
+ __asm__("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
+ __asm__("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
+ __asm__("vadd.u32 q9, q10" : : : "q9", "q15");
+ __asm__("vadd.u32 q9, q11" : : : "q9", "q11");
+ }
+
+ __asm__("vadd.u32 d28, d29" : : : "q14");
+ __asm__("vpadd.u32 d28, d28" : : : "q14");
+ __asm__("vmov.32 %0, d28[0]" : "=r"(far_energy_r): : "q14");
+
+ __asm__("vadd.u32 d18, d19" : : : "q9");
+ __asm__("vpadd.u32 d18, d18" : : : "q9");
+ __asm__("vmov.32 %0, d18[0]" : "=r"(echo_energy_adapt_r): : "q9");
+
+ __asm__("vadd.u32 d16, d17" : : : "q8");
+ __asm__("vpadd.u32 d16, d16" : : : "q8");
+ __asm__("vmov.32 %0, d16[0]" : "=r"(echo_energy_stored_r): : "q8");
+
+ // Get estimated echo energies for adaptive channel and stored channel.
+ echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
+ *echo_energy_stored = echo_energy_stored_r + (WebRtc_UWord32)echo_est[i];
+ *far_energy = far_energy_r + (WebRtc_UWord32)(far_spectrum[i]);
+ *echo_energy_adapt = echo_energy_adapt_r + WEBRTC_SPL_UMUL_16_16(
+ aecm->channelAdapt16[i], far_spectrum[i]);
}
-void WebRtcAecm_StoreAdaptiveChannel(AecmCore_t* aecm,
+static void StoreAdaptiveChannelNeon(AecmCore_t* aecm,
const WebRtc_UWord16* far_spectrum,
- WebRtc_Word32* echo_est)
-{
- int i;
+ WebRtc_Word32* echo_est) {
+ int i;
- // During startup we store the channel every block.
- // Recalculate echo estimate.
- for(i = 0; i < PART_LEN -7; i += 8)
- {
- // aecm->channelStored[i] = acem->channelAdapt16[i];
- // echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
- __asm__("vld1.16 {d26, d27}, [%0]" : : "r"(&far_spectrum[i]) : "q13");
- __asm__("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelAdapt16[i]) : "q12");
- __asm__("vst1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelStored[i]) : "q12");
- __asm__("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
- __asm__("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
- __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
- "r"(&echo_est[i]) : "q10", "q11");
- }
- aecm->channelStored[i] = aecm->channelAdapt16[i];
- echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
+ // During startup we store the channel every block.
+ // Recalculate echo estimate.
+ for (i = 0; i < PART_LEN - 7; i += 8) {
+ // aecm->channelStored[i] = acem->channelAdapt16[i];
+ // echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
+ __asm__("vld1.16 {d26, d27}, [%0]" : : "r"(&far_spectrum[i]) : "q13");
+ __asm__("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelAdapt16[i]) : "q12");
+ __asm__("vst1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelStored[i]) : "q12");
+ __asm__("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
+ __asm__("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
+ __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
+ "r"(&echo_est[i]) : "q10", "q11");
+ }
+ aecm->channelStored[i] = aecm->channelAdapt16[i];
+ echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
}
-void WebRtcAecm_ResetAdaptiveChannel(AecmCore_t* aecm)
-{
- int i;
+static void ResetAdaptiveChannelNeon(AecmCore_t* aecm) {
+ int i;
- for(i = 0; i < PART_LEN -7; i += 8)
- {
- // aecm->channelAdapt16[i] = aecm->channelStored[i];
- // aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)
- // aecm->channelStored[i], 16);
- __asm__("vld1.16 {d24, d25}, [%0, :128]" : :
- "r"(&aecm->channelStored[i]) : "q12");
- __asm__("vst1.16 {d24, d25}, [%0, :128]" : :
- "r"(&aecm->channelAdapt16[i]) : "q12");
- __asm__("vshll.s16 q10, d24, #16" : : : "q12", "q13", "q10");
- __asm__("vshll.s16 q11, d25, #16" : : : "q12", "q13", "q11");
- __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
- "r"(&aecm->channelAdapt32[i]): "q10", "q11");
- }
- aecm->channelAdapt16[i] = aecm->channelStored[i];
- aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32(
- (WebRtc_Word32)aecm->channelStored[i], 16);
+ for (i = 0; i < PART_LEN - 7; i += 8) {
+ // aecm->channelAdapt16[i] = aecm->channelStored[i];
+ // aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)
+ // aecm->channelStored[i], 16);
+ __asm__("vld1.16 {d24, d25}, [%0, :128]" : :
+ "r"(&aecm->channelStored[i]) : "q12");
+ __asm__("vst1.16 {d24, d25}, [%0, :128]" : :
+ "r"(&aecm->channelAdapt16[i]) : "q12");
+ __asm__("vshll.s16 q10, d24, #16" : : : "q12", "q13", "q10");
+ __asm__("vshll.s16 q11, d25, #16" : : : "q12", "q13", "q11");
+ __asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
+ "r"(&aecm->channelAdapt32[i]): "q10", "q11");
+ }
+ aecm->channelAdapt16[i] = aecm->channelStored[i];
+ aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32(
+ (WebRtc_Word32)aecm->channelStored[i], 16);
}
-#endif // #if defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM_NEON)
+void WebRtcAecm_InitNeon(void) {
+ WebRtcAecm_WindowAndFFT = WindowAndFFTNeon;
+ WebRtcAecm_InverseFFTAndWindow = InverseFFTAndWindowNeon;
+ WebRtcAecm_CalcLinearEnergies = CalcLinearEnergiesNeon;
+ WebRtcAecm_StoreAdaptiveChannel = StoreAdaptiveChannelNeon;
+ WebRtcAecm_ResetAdaptiveChannel = ResetAdaptiveChannelNeon;
+}
diff --git a/src/modules/audio_processing/ns/Android.mk b/src/modules/audio_processing/ns/Android.mk
index 1363a93..aba95e1 100644
--- a/src/modules/audio_processing/ns/Android.mk
+++ b/src/modules/audio_processing/ns/Android.mk
@@ -6,6 +6,8 @@
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
+#############################
+# Build the non-neon library.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
@@ -20,25 +22,20 @@
noise_suppression_x.c \
nsx_core.c
-# floating point
+# Files for floating point.
# noise_suppression.c ns_core.c
# Flags passed to both C and C++ files.
-LOCAL_CFLAGS := \
- $(MY_WEBRTC_COMMON_DEFS)
-
-ifeq ($(ARCH_ARM_HAVE_NEON),true)
-LOCAL_SRC_FILES += \
- nsx_core_neon.c
-LOCAL_CFLAGS += \
- $(MY_ARM_CFLAGS_NEON)
-endif
+LOCAL_CFLAGS := $(MY_WEBRTC_COMMON_DEFS)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/interface \
$(LOCAL_PATH)/../utility \
$(LOCAL_PATH)/../../.. \
- $(LOCAL_PATH)/../../../common_audio/signal_processing/include
+ $(LOCAL_PATH)/../../../common_audio/signal_processing/include \
+ $(LOCAL_PATH)/../../../system_wrappers/interface
+
+LOCAL_STATIC_LIBRARIES += libwebrtc_system_wrappers
LOCAL_SHARED_LIBRARIES := \
libcutils \
@@ -49,3 +46,31 @@
include external/stlport/libstlport.mk
endif
include $(BUILD_STATIC_LIBRARY)
+
+#############################
+# Build the neon library.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libwebrtc_ns_neon
+LOCAL_MODULE_TAGS := optional
+LOCAL_GENERATED_SOURCES :=
+
+LOCAL_SRC_FILES := nsx_core_neon.c
+
+# Flags passed to both C and C++ files.
+LOCAL_CFLAGS := \
+ $(MY_WEBRTC_COMMON_DEFS) \
+ -mfpu=neon \
+ -flax-vector-conversions
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/interface \
+ $(LOCAL_PATH)/../../.. \
+ $(LOCAL_PATH)/../../../common_audio/signal_processing/include
+
+ifndef NDK_ROOT
+include external/stlport/libstlport.mk
+endif
+include $(BUILD_STATIC_LIBRARY)
diff --git a/src/modules/audio_processing/ns/nsx_core.c b/src/modules/audio_processing/ns/nsx_core.c
index 66c4913..3879161 100644
--- a/src/modules/audio_processing/ns/nsx_core.c
+++ b/src/modules/audio_processing/ns/nsx_core.c
@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <stdio.h>
+#include "cpu_features_wrapper.h"
#include "nsx_core.h"
// Skip first frequency bins during estimation. (0 <= value < 64)
@@ -426,6 +427,271 @@
355, 330
};
+// Declare function pointers.
+NoiseEstimation WebRtcNsx_NoiseEstimation;
+PrepareSpectrum WebRtcNsx_PrepareSpectrum;
+SynthesisUpdate WebRtcNsx_SynthesisUpdate;
+AnalysisUpdate WebRtcNsx_AnalysisUpdate;
+Denormalize WebRtcNsx_Denormalize;
+CreateComplexBuffer WebRtcNsx_CreateComplexBuffer;
+
+// Update the noise estimation information.
+static void UpdateNoiseEstimate(NsxInst_t* inst, int offset) {
+ WebRtc_Word32 tmp32no1 = 0;
+ WebRtc_Word32 tmp32no2 = 0;
+ WebRtc_Word16 tmp16 = 0;
+ const WebRtc_Word16 kExp2Const = 11819; // Q13
+
+ int i = 0;
+
+ tmp16 = WebRtcSpl_MaxValueW16(inst->noiseEstLogQuantile + offset,
+ inst->magnLen);
+ // Guarantee a Q-domain as high as possible and still fit in int16
+ inst->qNoise = 14 - (int) WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+ kExp2Const, tmp16, 21);
+ for (i = 0; i < inst->magnLen; i++) {
+ // inst->quantile[i]=exp(inst->lquantile[offset+i]);
+ // in Q21
+ tmp32no2 = WEBRTC_SPL_MUL_16_16(kExp2Const,
+ inst->noiseEstLogQuantile[offset + i]);
+ tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); // 2^21 + frac
+ tmp16 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(tmp32no2, 21);
+ tmp16 -= 21;// shift 21 to get result in Q0
+ tmp16 += (WebRtc_Word16) inst->qNoise; //shift to get result in Q(qNoise)
+ if (tmp16 < 0) {
+ tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no1, -tmp16);
+ } else {
+ tmp32no1 = WEBRTC_SPL_LSHIFT_W32(tmp32no1, tmp16);
+ }
+ inst->noiseEstQuantile[i] = WebRtcSpl_SatW32ToW16(tmp32no1);
+ }
+}
+
+// Noise Estimation
+static void NoiseEstimationC(NsxInst_t* inst,
+ uint16_t* magn,
+ uint32_t* noise,
+ int16_t* q_noise) {
+ WebRtc_Word32 numerator = FACTOR_Q16;
+ WebRtc_Word16 lmagn[HALF_ANAL_BLOCKL], counter, countDiv;
+ WebRtc_Word16 countProd, delta, zeros, frac;
+ WebRtc_Word16 log2, tabind, logval, tmp16, tmp16no1, tmp16no2;
+ const int16_t log2_const = 22713; // Q15
+ const int16_t width_factor = 21845;
+
+ int i, s, offset;
+
+ tabind = inst->stages - inst->normData;
+ assert(tabind < 9);
+ assert(tabind > -9);
+ if (tabind < 0) {
+ logval = -WebRtcNsx_kLogTable[-tabind];
+ } else {
+ logval = WebRtcNsx_kLogTable[tabind];
+ }
+
+ // lmagn(i)=log(magn(i))=log(2)*log2(magn(i))
+ // magn is in Q(-stages), and the real lmagn values are:
+ // real_lmagn(i)=log(magn(i)*2^stages)=log(magn(i))+log(2^stages)
+ // lmagn in Q8
+ for (i = 0; i < inst->magnLen; i++) {
+ if (magn[i]) {
+ zeros = WebRtcSpl_NormU32((WebRtc_UWord32)magn[i]);
+ frac = (WebRtc_Word16)((((WebRtc_UWord32)magn[i] << zeros)
+ & 0x7FFFFFFF) >> 23);
+ // log2(magn(i))
+ assert(frac < 256);
+ log2 = (WebRtc_Word16)(((31 - zeros) << 8)
+ + WebRtcNsx_kLogTableFrac[frac]);
+ // log2(magn(i))*log(2)
+ lmagn[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(log2, log2_const, 15);
+ // + log(2^stages)
+ lmagn[i] += logval;
+ } else {
+ lmagn[i] = logval;//0;
+ }
+ }
+
+ // loop over simultaneous estimates
+ for (s = 0; s < SIMULT; s++) {
+ offset = s * inst->magnLen;
+
+ // Get counter values from state
+ counter = inst->noiseEstCounter[s];
+ assert(counter < 201);
+ countDiv = WebRtcNsx_kCounterDiv[counter];
+ countProd = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(counter, countDiv);
+
+ // quant_est(...)
+ for (i = 0; i < inst->magnLen; i++) {
+ // compute delta
+ if (inst->noiseEstDensity[offset + i] > 512) {
+ delta = WebRtcSpl_DivW32W16ResW16(numerator,
+ inst->noiseEstDensity[offset + i]);
+ } else {
+ delta = FACTOR_Q7;
+ if (inst->blockIndex < END_STARTUP_LONG) {
+ // Smaller step size during startup. This prevents from using
+ // unrealistic values causing overflow.
+ delta = FACTOR_Q7_STARTUP;
+ }
+ }
+
+ // update log quantile estimate
+ tmp16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(delta, countDiv, 14);
+ if (lmagn[i] > inst->noiseEstLogQuantile[offset + i]) {
+ // +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2
+ // CounterDiv=1/(inst->counter[s]+1) in Q15
+ tmp16 += 2;
+ tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 2);
+ inst->noiseEstLogQuantile[offset + i] += tmp16no1;
+ } else {
+ tmp16 += 1;
+ tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 1);
+ // *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2
+ tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16no1, 3, 1);
+ inst->noiseEstLogQuantile[offset + i] -= tmp16no2;
+ if (inst->noiseEstLogQuantile[offset + i] < logval) {
+ // This is the smallest fixed point representation we can
+ // have, hence we limit the output.
+ inst->noiseEstLogQuantile[offset + i] = logval;
+ }
+ }
+
+ // update density estimate
+ if (WEBRTC_SPL_ABS_W16(lmagn[i] - inst->noiseEstLogQuantile[offset + i])
+ < WIDTH_Q8) {
+ tmp16no1 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+ inst->noiseEstDensity[offset + i], countProd, 15);
+ tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+ width_factor, countDiv, 15);
+ inst->noiseEstDensity[offset + i] = tmp16no1 + tmp16no2;
+ }
+ } // end loop over magnitude spectrum
+
+ if (counter >= END_STARTUP_LONG) {
+ inst->noiseEstCounter[s] = 0;
+ if (inst->blockIndex >= END_STARTUP_LONG) {
+ UpdateNoiseEstimate(inst, offset);
+ }
+ }
+ inst->noiseEstCounter[s]++;
+
+ } // end loop over simultaneous estimates
+
+ // Sequentially update the noise during startup
+ if (inst->blockIndex < END_STARTUP_LONG) {
+ UpdateNoiseEstimate(inst, offset);
+ }
+
+ for (i = 0; i < inst->magnLen; i++) {
+ noise[i] = (WebRtc_UWord32)(inst->noiseEstQuantile[i]); // Q(qNoise)
+ }
+ (*q_noise) = (WebRtc_Word16)inst->qNoise;
+}
+
+// Filter the data in the frequency domain, and create spectrum.
+static void PrepareSpectrumC(NsxInst_t* inst, int16_t* freq_buf) {
+ int i = 0, j = 0;
+ int16_t tmp16 = 0;
+
+ for (i = 0; i < inst->magnLen; i++) {
+ inst->real[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->real[i],
+ (WebRtc_Word16)(inst->noiseSupFilter[i]), 14); // Q(normData-stages)
+ inst->imag[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->imag[i],
+ (WebRtc_Word16)(inst->noiseSupFilter[i]), 14); // Q(normData-stages)
+ }
+
+ freq_buf[0] = inst->real[0];
+ freq_buf[1] = -inst->imag[0];
+ for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) {
+ tmp16 = (inst->anaLen << 1) - j;
+ freq_buf[j] = inst->real[i];
+ freq_buf[j + 1] = -inst->imag[i];
+ freq_buf[tmp16] = inst->real[i];
+ freq_buf[tmp16 + 1] = inst->imag[i];
+ }
+ freq_buf[inst->anaLen] = inst->real[inst->anaLen2];
+ freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2];
+}
+
+// Denormalize the input buffer.
+static __inline void DenormalizeC(NsxInst_t* inst, int16_t* in, int factor) {
+ int i = 0, j = 0;
+ int32_t tmp32 = 0;
+ for (i = 0, j = 0; i < inst->anaLen; i += 1, j += 2) {
+ tmp32 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)in[j],
+ factor - inst->normData);
+ inst->real[i] = WebRtcSpl_SatW32ToW16(tmp32); // Q0
+ }
+}
+
+// For the noise supression process, synthesis, read out fully processed
+// segment, and update synthesis buffer.
+static void SynthesisUpdateC(NsxInst_t* inst,
+ int16_t* out_frame,
+ int16_t gain_factor) {
+ int i = 0;
+ int16_t tmp16a = 0;
+ int16_t tmp16b = 0;
+ int32_t tmp32 = 0;
+
+ // synthesis
+ for (i = 0; i < inst->anaLen; i++) {
+ tmp16a = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+ inst->window[i], inst->real[i], 14); // Q0, window in Q14
+ tmp32 = WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(tmp16a, gain_factor, 13); // Q0
+ // Down shift with rounding
+ tmp16b = WebRtcSpl_SatW32ToW16(tmp32); // Q0
+ inst->synthesisBuffer[i] = WEBRTC_SPL_ADD_SAT_W16(inst->synthesisBuffer[i],
+ tmp16b); // Q0
+ }
+
+ // read out fully processed segment
+ for (i = 0; i < inst->blockLen10ms; i++) {
+ out_frame[i] = inst->synthesisBuffer[i]; // Q0
+ }
+
+ // update synthesis buffer
+ WEBRTC_SPL_MEMCPY_W16(inst->synthesisBuffer,
+ inst->synthesisBuffer + inst->blockLen10ms,
+ inst->anaLen - inst->blockLen10ms);
+ WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer
+ + inst->anaLen - inst->blockLen10ms, inst->blockLen10ms);
+}
+
+// Update analysis buffer for lower band, and window data before FFT.
+static void AnalysisUpdateC(NsxInst_t* inst,
+ int16_t* out,
+ int16_t* new_speech) {
+ int i = 0;
+
+ // For lower band update analysis buffer.
+ WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer,
+ inst->analysisBuffer + inst->blockLen10ms,
+ inst->anaLen - inst->blockLen10ms);
+ WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer
+ + inst->anaLen - inst->blockLen10ms, new_speech, inst->blockLen10ms);
+
+ // Window data before FFT.
+ for (i = 0; i < inst->anaLen; i++) {
+ out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+ inst->window[i], inst->analysisBuffer[i], 14); // Q0
+ }
+}
+
+// Create a complex number buffer (out[]) as the intput (in[]) interleaved with
+// zeros, and normalize it.
+static __inline void CreateComplexBufferC(NsxInst_t* inst,
+ int16_t* in,
+ int16_t* out) {
+ int i = 0, j = 0;
+ for (i = 0, j = 0; i < inst->anaLen; i += 1, j += 2) {
+ out[j] = WEBRTC_SPL_LSHIFT_W16(in[i], inst->normData); // Q(normData)
+ out[j + 1] = 0; // Insert zeros in imaginary part
+ }
+}
+
void WebRtcNsx_CalcParametricNoiseEstimate(NsxInst_t* inst,
WebRtc_Word16 pink_noise_exp_avg,
WebRtc_Word32 pink_noise_num_avg,
@@ -600,6 +866,24 @@
inst->file5 = fopen("file5.pcm", "wb");
#endif
+ // Initialize function pointers.
+ WebRtcNsx_NoiseEstimation = NoiseEstimationC;
+ WebRtcNsx_PrepareSpectrum = PrepareSpectrumC;
+ WebRtcNsx_SynthesisUpdate = SynthesisUpdateC;
+ WebRtcNsx_AnalysisUpdate = AnalysisUpdateC;
+ WebRtcNsx_Denormalize = DenormalizeC;
+ WebRtcNsx_CreateComplexBuffer = CreateComplexBufferC;
+
+#ifdef WEBRTC_DETECT_ARM_NEON
+ uint64_t features = WebRtc_GetCPUFeaturesARM();
+ if ((features & kCPUFeatureNEON) != 0)
+ {
+ WebRtcNsx_InitNeon();
+ }
+#elif defined(WEBRTC_ARCH_ARM_NEON)
+ WebRtcNsx_InitNeon();
+#endif
+
inst->initFlag = 1;
return 0;
@@ -2157,263 +2441,4 @@
return 0;
}
-#if !(defined(WEBRTC_ARCH_ARM_NEON) && defined(WEBRTC_ANDROID))
-// Update the noise estimation information.
-static void UpdateNoiseEstimate(NsxInst_t* inst, int offset) {
- WebRtc_Word32 tmp32no1 = 0;
- WebRtc_Word32 tmp32no2 = 0;
- WebRtc_Word16 tmp16 = 0;
- const WebRtc_Word16 kExp2Const = 11819; // Q13
-
- int i = 0;
-
- tmp16 = WebRtcSpl_MaxValueW16(inst->noiseEstLogQuantile + offset,
- inst->magnLen);
- // Guarantee a Q-domain as high as possible and still fit in int16
- inst->qNoise = 14 - (int) WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
- kExp2Const, tmp16, 21);
- for (i = 0; i < inst->magnLen; i++) {
- // inst->quantile[i]=exp(inst->lquantile[offset+i]);
- // in Q21
- tmp32no2 = WEBRTC_SPL_MUL_16_16(kExp2Const,
- inst->noiseEstLogQuantile[offset + i]);
- tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); // 2^21 + frac
- tmp16 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(tmp32no2, 21);
- tmp16 -= 21;// shift 21 to get result in Q0
- tmp16 += (WebRtc_Word16) inst->qNoise; //shift to get result in Q(qNoise)
- if (tmp16 < 0) {
- tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no1, -tmp16);
- } else {
- tmp32no1 = WEBRTC_SPL_LSHIFT_W32(tmp32no1, tmp16);
- }
- inst->noiseEstQuantile[i] = WebRtcSpl_SatW32ToW16(tmp32no1);
- }
-}
-
-// Noise Estimation
-void WebRtcNsx_NoiseEstimation(NsxInst_t* inst,
- uint16_t* magn,
- uint32_t* noise,
- int16_t* q_noise) {
- WebRtc_Word32 numerator = FACTOR_Q16;
- WebRtc_Word16 lmagn[HALF_ANAL_BLOCKL], counter, countDiv;
- WebRtc_Word16 countProd, delta, zeros, frac;
- WebRtc_Word16 log2, tabind, logval, tmp16, tmp16no1, tmp16no2;
- const int16_t log2_const = 22713; // Q15
- const int16_t width_factor = 21845;
-
- int i, s, offset;
-
- tabind = inst->stages - inst->normData;
- assert(tabind < 9);
- assert(tabind > -9);
- if (tabind < 0) {
- logval = -WebRtcNsx_kLogTable[-tabind];
- } else {
- logval = WebRtcNsx_kLogTable[tabind];
- }
-
- // lmagn(i)=log(magn(i))=log(2)*log2(magn(i))
- // magn is in Q(-stages), and the real lmagn values are:
- // real_lmagn(i)=log(magn(i)*2^stages)=log(magn(i))+log(2^stages)
- // lmagn in Q8
- for (i = 0; i < inst->magnLen; i++) {
- if (magn[i]) {
- zeros = WebRtcSpl_NormU32((WebRtc_UWord32)magn[i]);
- frac = (WebRtc_Word16)((((WebRtc_UWord32)magn[i] << zeros)
- & 0x7FFFFFFF) >> 23);
- // log2(magn(i))
- assert(frac < 256);
- log2 = (WebRtc_Word16)(((31 - zeros) << 8)
- + WebRtcNsx_kLogTableFrac[frac]);
- // log2(magn(i))*log(2)
- lmagn[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(log2, log2_const, 15);
- // + log(2^stages)
- lmagn[i] += logval;
- } else {
- lmagn[i] = logval;//0;
- }
- }
-
- // loop over simultaneous estimates
- for (s = 0; s < SIMULT; s++) {
- offset = s * inst->magnLen;
-
- // Get counter values from state
- counter = inst->noiseEstCounter[s];
- assert(counter < 201);
- countDiv = WebRtcNsx_kCounterDiv[counter];
- countProd = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(counter, countDiv);
-
- // quant_est(...)
- for (i = 0; i < inst->magnLen; i++) {
- // compute delta
- if (inst->noiseEstDensity[offset + i] > 512) {
- delta = WebRtcSpl_DivW32W16ResW16(numerator,
- inst->noiseEstDensity[offset + i]);
- } else {
- delta = FACTOR_Q7;
- if (inst->blockIndex < END_STARTUP_LONG) {
- // Smaller step size during startup. This prevents from using
- // unrealistic values causing overflow.
- delta = FACTOR_Q7_STARTUP;
- }
- }
-
- // update log quantile estimate
- tmp16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(delta, countDiv, 14);
- if (lmagn[i] > inst->noiseEstLogQuantile[offset + i]) {
- // +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2
- // CounterDiv=1/(inst->counter[s]+1) in Q15
- tmp16 += 2;
- tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 2);
- inst->noiseEstLogQuantile[offset + i] += tmp16no1;
- } else {
- tmp16 += 1;
- tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 1);
- // *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2
- tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16no1, 3, 1);
- inst->noiseEstLogQuantile[offset + i] -= tmp16no2;
- if (inst->noiseEstLogQuantile[offset + i] < logval) {
- // This is the smallest fixed point representation we can
- // have, hence we limit the output.
- inst->noiseEstLogQuantile[offset + i] = logval;
- }
- }
-
- // update density estimate
- if (WEBRTC_SPL_ABS_W16(lmagn[i] - inst->noiseEstLogQuantile[offset + i])
- < WIDTH_Q8) {
- tmp16no1 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
- inst->noiseEstDensity[offset + i], countProd, 15);
- tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
- width_factor, countDiv, 15);
- inst->noiseEstDensity[offset + i] = tmp16no1 + tmp16no2;
- }
- } // end loop over magnitude spectrum
-
- if (counter >= END_STARTUP_LONG) {
- inst->noiseEstCounter[s] = 0;
- if (inst->blockIndex >= END_STARTUP_LONG) {
- UpdateNoiseEstimate(inst, offset);
- }
- }
- inst->noiseEstCounter[s]++;
-
- } // end loop over simultaneous estimates
-
- // Sequentially update the noise during startup
- if (inst->blockIndex < END_STARTUP_LONG) {
- UpdateNoiseEstimate(inst, offset);
- }
-
- for (i = 0; i < inst->magnLen; i++) {
- noise[i] = (WebRtc_UWord32)(inst->noiseEstQuantile[i]); // Q(qNoise)
- }
- (*q_noise) = (WebRtc_Word16)inst->qNoise;
-}
-
-// Filter the data in the frequency domain, and create spectrum.
-void WebRtcNsx_PrepareSpectrum(NsxInst_t* inst, int16_t* freq_buf) {
- int i = 0, j = 0;
- int16_t tmp16 = 0;
-
- for (i = 0; i < inst->magnLen; i++) {
- inst->real[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->real[i],
- (WebRtc_Word16)(inst->noiseSupFilter[i]), 14); // Q(normData-stages)
- inst->imag[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->imag[i],
- (WebRtc_Word16)(inst->noiseSupFilter[i]), 14); // Q(normData-stages)
- }
-
- freq_buf[0] = inst->real[0];
- freq_buf[1] = -inst->imag[0];
- for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) {
- tmp16 = (inst->anaLen << 1) - j;
- freq_buf[j] = inst->real[i];
- freq_buf[j + 1] = -inst->imag[i];
- freq_buf[tmp16] = inst->real[i];
- freq_buf[tmp16 + 1] = inst->imag[i];
- }
- freq_buf[inst->anaLen] = inst->real[inst->anaLen2];
- freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2];
-}
-
-// Denormalize the input buffer.
-__inline void WebRtcNsx_Denormalize(NsxInst_t* inst, int16_t* in, int factor) {
- int i = 0, j = 0;
- int32_t tmp32 = 0;
- for (i = 0, j = 0; i < inst->anaLen; i += 1, j += 2) {
- tmp32 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)in[j],
- factor - inst->normData);
- inst->real[i] = WebRtcSpl_SatW32ToW16(tmp32); // Q0
- }
-}
-
-// For the noise supression process, synthesis, read out fully processed
-// segment, and update synthesis buffer.
-void WebRtcNsx_SynthesisUpdate(NsxInst_t* inst,
- int16_t* out_frame,
- int16_t gain_factor) {
- int i = 0;
- int16_t tmp16a = 0;
- int16_t tmp16b = 0;
- int32_t tmp32 = 0;
-
- // synthesis
- for (i = 0; i < inst->anaLen; i++) {
- tmp16a = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
- inst->window[i], inst->real[i], 14); // Q0, window in Q14
- tmp32 = WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(tmp16a, gain_factor, 13); // Q0
- // Down shift with rounding
- tmp16b = WebRtcSpl_SatW32ToW16(tmp32); // Q0
- inst->synthesisBuffer[i] = WEBRTC_SPL_ADD_SAT_W16(inst->synthesisBuffer[i],
- tmp16b); // Q0
- }
-
- // read out fully processed segment
- for (i = 0; i < inst->blockLen10ms; i++) {
- out_frame[i] = inst->synthesisBuffer[i]; // Q0
- }
-
- // update synthesis buffer
- WEBRTC_SPL_MEMCPY_W16(inst->synthesisBuffer,
- inst->synthesisBuffer + inst->blockLen10ms,
- inst->anaLen - inst->blockLen10ms);
- WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer
- + inst->anaLen - inst->blockLen10ms, inst->blockLen10ms);
-}
-
-// Update analysis buffer for lower band, and window data before FFT.
-void WebRtcNsx_AnalysisUpdate(NsxInst_t* inst,
- int16_t* out,
- int16_t* new_speech) {
- int i = 0;
-
- // For lower band update analysis buffer.
- WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer,
- inst->analysisBuffer + inst->blockLen10ms,
- inst->anaLen - inst->blockLen10ms);
- WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer
- + inst->anaLen - inst->blockLen10ms, new_speech, inst->blockLen10ms);
-
- // Window data before FFT.
- for (i = 0; i < inst->anaLen; i++) {
- out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
- inst->window[i], inst->analysisBuffer[i], 14); // Q0
- }
-}
-
-// Create a complex number buffer (out[]) as the intput (in[]) interleaved with
-// zeros, and normalize it.
-__inline void WebRtcNsx_CreateComplexBuffer(NsxInst_t* inst,
- int16_t* in,
- int16_t* out) {
- int i = 0, j = 0;
- for (i = 0, j = 0; i < inst->anaLen; i += 1, j += 2) {
- out[j] = WEBRTC_SPL_LSHIFT_W16(in[i], inst->normData); // Q(normData)
- out[j + 1] = 0; // Insert zeros in imaginary part
- }
-}
-
-#endif // !(defined(WEBRTC_ARCH_ARM_NEON) && defined(WEBRTC_ANDROID))
diff --git a/src/modules/audio_processing/ns/nsx_core.h b/src/modules/audio_processing/ns/nsx_core.h
index 990dfcb..0a0faf9 100644
--- a/src/modules/audio_processing/ns/nsx_core.h
+++ b/src/modules/audio_processing/ns/nsx_core.h
@@ -165,40 +165,51 @@
short* outFrameHigh);
/****************************************************************************
- * Internal functions and variable declarations shared with optimized code.
+ * Some function pointers, for internal functions shared by ARM NEON and
+ * generic C code.
*/
-
// Noise Estimation.
-void WebRtcNsx_NoiseEstimation(NsxInst_t* inst,
- uint16_t* magn,
- uint32_t* noise,
- int16_t* q_noise);
+typedef void (*NoiseEstimation)(NsxInst_t* inst,
+ uint16_t* magn,
+ uint32_t* noise,
+ int16_t* q_noise);
+extern NoiseEstimation WebRtcNsx_NoiseEstimation;
// Filter the data in the frequency domain, and create spectrum.
-void WebRtcNsx_PrepareSpectrum(NsxInst_t* inst,
- int16_t* freq_buff);
+typedef void (*PrepareSpectrum)(NsxInst_t* inst,
+ int16_t* freq_buff);
+extern PrepareSpectrum WebRtcNsx_PrepareSpectrum;
// For the noise supression process, synthesis, read out fully processed
// segment, and update synthesis buffer.
-void WebRtcNsx_SynthesisUpdate(NsxInst_t* inst,
- int16_t* out_frame,
- int16_t gain_factor);
+typedef void (*SynthesisUpdate)(NsxInst_t* inst,
+ int16_t* out_frame,
+ int16_t gain_factor);
+extern SynthesisUpdate WebRtcNsx_SynthesisUpdate;
// Update analysis buffer for lower band, and window data before FFT.
-void WebRtcNsx_AnalysisUpdate(NsxInst_t* inst,
- int16_t* out,
- int16_t* new_speech);
+typedef void (*AnalysisUpdate)(NsxInst_t* inst,
+ int16_t* out,
+ int16_t* new_speech);
+extern AnalysisUpdate WebRtcNsx_AnalysisUpdate;
// Denormalize the input buffer.
-__inline void WebRtcNsx_Denormalize(NsxInst_t* inst,
- int16_t* in,
- int factor);
+typedef void (*Denormalize)(NsxInst_t* inst,
+ int16_t* in,
+ int factor);
+extern Denormalize WebRtcNsx_Denormalize;
// Create a complex number buffer, as the intput interleaved with zeros,
// and normalize it.
-__inline void WebRtcNsx_CreateComplexBuffer(NsxInst_t* inst,
- int16_t* in,
- int16_t* out);
+typedef void (*CreateComplexBuffer)(NsxInst_t* inst,
+ int16_t* in,
+ int16_t* out);
+extern CreateComplexBuffer WebRtcNsx_CreateComplexBuffer;
+
+/****************************************************************************
+ * Initialization of the above function pointers for ARM Neon.
+ */
+void WebRtcNsx_InitNeon(void);
extern const WebRtc_Word16 WebRtcNsx_kLogTable[9];
extern const WebRtc_Word16 WebRtcNsx_kLogTableFrac[256];
@@ -208,4 +219,4 @@
}
#endif
-#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_
+#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_
diff --git a/src/modules/audio_processing/ns/nsx_core_neon.c b/src/modules/audio_processing/ns/nsx_core_neon.c
index d01ba3b..675b652 100644
--- a/src/modules/audio_processing/ns/nsx_core_neon.c
+++ b/src/modules/audio_processing/ns/nsx_core_neon.c
@@ -8,15 +8,13 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#if defined(WEBRTC_ARCH_ARM_NEON) && defined(WEBRTC_ANDROID)
-
#include "nsx_core.h"
#include <arm_neon.h>
#include <assert.h>
// Update the noise estimation information.
-static void UpdateNoiseEstimate(NsxInst_t* inst, int offset) {
+static void UpdateNoiseEstimateNeon(NsxInst_t* inst, int offset) {
int i = 0;
const int16_t kExp2Const = 11819; // Q13
int16_t* ptr_noiseEstLogQuantile = NULL;
@@ -75,7 +73,7 @@
}
// Last iteration:
-
+
// inst->quantile[i]=exp(inst->lquantile[offset+i]);
// in Q21
int32_t tmp32no2 = WEBRTC_SPL_MUL_16_16(kExp2Const,
@@ -94,10 +92,10 @@
}
// Noise Estimation
-void WebRtcNsx_NoiseEstimation(NsxInst_t* inst,
- uint16_t* magn,
- uint32_t* noise,
- int16_t* q_noise) {
+static void NoiseEstimationNeon(NsxInst_t* inst,
+ uint16_t* magn,
+ uint32_t* noise,
+ int16_t* q_noise) {
int32_t numerator = FACTOR_Q16;
int16_t lmagn[HALF_ANAL_BLOCKL], counter, countDiv;
int16_t countProd, delta, zeros, frac;
@@ -126,11 +124,11 @@
if (magn[i]) {
zeros = WebRtcSpl_NormU32((uint32_t)magn[i]);
frac = (int16_t)((((uint32_t)magn[i] << zeros)
- & 0x7FFFFFFF) >> 23);
+ & 0x7FFFFFFF) >> 23);
assert(frac < 256);
// log2(magn(i))
log2 = (int16_t)(((31 - zeros) << 8)
- + WebRtcNsx_kLogTableFrac[frac]);
+ + WebRtcNsx_kLogTableFrac[frac]);
// log2(magn(i))*log(2)
lmagn[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(log2, log2_const, 15);
// + log(2^stages)
@@ -302,7 +300,7 @@
if (counter >= END_STARTUP_LONG) {
inst->noiseEstCounter[s] = 0;
if (inst->blockIndex >= END_STARTUP_LONG) {
- UpdateNoiseEstimate(inst, offset);
+ UpdateNoiseEstimateNeon(inst, offset);
}
}
inst->noiseEstCounter[s]++;
@@ -311,7 +309,7 @@
// Sequentially update the noise during startup
if (inst->blockIndex < END_STARTUP_LONG) {
- UpdateNoiseEstimate(inst, offset);
+ UpdateNoiseEstimateNeon(inst, offset);
}
for (i = 0; i < inst->magnLen; i++) {
@@ -321,7 +319,7 @@
}
// Filter the data in the frequency domain, and create spectrum.
-void WebRtcNsx_PrepareSpectrum(NsxInst_t* inst, int16_t* freq_buf) {
+static void PrepareSpectrumNeon(NsxInst_t* inst, int16_t* freq_buf) {
// (1) Filtering.
@@ -338,7 +336,7 @@
uint16_t* ptr_noiseSupFilter = &inst->noiseSupFilter[0];
// Filter the rest in the frequency domain.
- for (; ptr_real < &inst->real[inst->magnLen - 1]; ) {
+ for (; ptr_real < &inst->real[inst->magnLen - 1];) {
// Loop unrolled once. Both pointers are incremented by 4 twice.
__asm__ __volatile__(
"vld1.16 d20, [%[ptr_real]]\n\t"
@@ -368,7 +366,7 @@
:
:"d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25",
"q9", "q10", "q11", "q12"
- );
+ );
}
// Filter the last pair of elements in the frequency domain.
@@ -400,7 +398,7 @@
int16_t* ptr_realImag2 = ptr_realImag2 = &freq_buf[(inst->anaLen << 1) - 8];
ptr_real = &inst->real[1];
ptr_imag = &inst->imag[1];
- for (; ptr_real < &inst->real[inst->anaLen2 - 11]; ) {
+ for (; ptr_real < &inst->real[inst->anaLen2 - 11];) {
// Loop unrolled once. All pointers are incremented twice.
__asm__ __volatile__(
"vld1.16 d22, [%[ptr_real]]!\n\t"
@@ -456,13 +454,13 @@
}
// Denormalize the input buffer.
-__inline void WebRtcNsx_Denormalize(NsxInst_t* inst, int16_t* in, int factor) {
+static __inline void DenormalizeNeon(NsxInst_t* inst, int16_t* in, int factor) {
int16_t* ptr_real = &inst->real[0];
int16_t* ptr_in = &in[0];
__asm__ __volatile__("vdup.32 q10, %0" ::
"r"((int32_t)(factor - inst->normData)) : "q10");
- for (; ptr_real < &inst->real[inst->anaLen]; ) {
+ for (; ptr_real < &inst->real[inst->anaLen];) {
// Loop unrolled once. Both pointers are incremented.
__asm__ __volatile__(
@@ -495,9 +493,9 @@
// For the noise supress process, synthesis, read out fully processed segment,
// and update synthesis buffer.
-void WebRtcNsx_SynthesisUpdate(NsxInst_t* inst,
- int16_t* out_frame,
- int16_t gain_factor) {
+static void SynthesisUpdateNeon(NsxInst_t* inst,
+ int16_t* out_frame,
+ int16_t gain_factor) {
int16_t* ptr_real = &inst->real[0];
int16_t* ptr_syn = &inst->synthesisBuffer[0];
int16_t* ptr_window = &inst->window[0];
@@ -505,7 +503,7 @@
// synthesis
__asm__ __volatile__("vdup.16 d24, %0" : : "r"(gain_factor) : "d24");
// Loop unrolled once. All pointers are incremented in the assembly code.
- for (; ptr_syn < &inst->synthesisBuffer[inst->anaLen]; ) {
+ for (; ptr_syn < &inst->synthesisBuffer[inst->anaLen];) {
__asm__ __volatile__(
// Load variables.
"vld1.16 d22, [%[ptr_real]]!\n\t"
@@ -553,7 +551,7 @@
int16_t* ptr_out = &out_frame[0];
ptr_syn = &inst->synthesisBuffer[0];
// read out fully processed segment
- for (; ptr_syn < &inst->synthesisBuffer[inst->blockLen10ms]; ) {
+ for (; ptr_syn < &inst->synthesisBuffer[inst->blockLen10ms];) {
// Loop unrolled once. Both pointers are incremented in the assembly code.
__asm__ __volatile__(
// out_frame[i] = inst->synthesisBuffer[i]; // Q0
@@ -575,7 +573,7 @@
// inst->anaLen - inst->blockLen10ms);
ptr_out = &inst->synthesisBuffer[0],
ptr_syn = &inst->synthesisBuffer[inst->blockLen10ms];
- for (; ptr_syn < &inst->synthesisBuffer[inst->anaLen]; ) {
+ for (; ptr_syn < &inst->synthesisBuffer[inst->anaLen];) {
// Loop unrolled once. Both pointers are incremented in the assembly code.
__asm__ __volatile__(
"vld1.16 {d22, d23}, [%[ptr_syn]]!\n\t"
@@ -593,7 +591,7 @@
// WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer
// + inst->anaLen - inst->blockLen10ms, inst->blockLen10ms);
__asm__ __volatile__("vdup.16 q10, %0" : : "r"(0) : "q10");
- for (; ptr_out < &inst->synthesisBuffer[inst->anaLen]; ) {
+ for (; ptr_out < &inst->synthesisBuffer[inst->anaLen];) {
// Loop unrolled once. Pointer is incremented in the assembly code.
__asm__ __volatile__(
"vst1.16 {d20, d21}, [%[ptr_out]]!\n\t"
@@ -606,9 +604,9 @@
}
// Update analysis buffer for lower band, and window data before FFT.
-void WebRtcNsx_AnalysisUpdate(NsxInst_t* inst,
- int16_t* out,
- int16_t* new_speech) {
+static void AnalysisUpdateNeon(NsxInst_t* inst,
+ int16_t* out,
+ int16_t* new_speech) {
int16_t* ptr_ana = &inst->analysisBuffer[inst->blockLen10ms];
int16_t* ptr_out = &inst->analysisBuffer[0];
@@ -617,7 +615,7 @@
// WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer,
// inst->analysisBuffer + inst->blockLen10ms,
// inst->anaLen - inst->blockLen10ms);
- for (; ptr_out < &inst->analysisBuffer[inst->anaLen - inst->blockLen10ms]; ) {
+ for (; ptr_out < &inst->analysisBuffer[inst->anaLen - inst->blockLen10ms];) {
// Loop unrolled once, so both pointers are incremented by 8 twice.
__asm__ __volatile__(
"vld1.16 {d20, d21}, [%[ptr_ana]]!\n\t"
@@ -633,7 +631,7 @@
// WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer
// + inst->anaLen - inst->blockLen10ms, new_speech, inst->blockLen10ms);
- for (ptr_ana = new_speech; ptr_out < &inst->analysisBuffer[inst->anaLen]; ) {
+ for (ptr_ana = new_speech; ptr_out < &inst->analysisBuffer[inst->anaLen];) {
// Loop unrolled once, so both pointers are incremented by 8 twice.
__asm__ __volatile__(
"vld1.16 {d20, d21}, [%[ptr_ana]]!\n\t"
@@ -651,7 +649,7 @@
int16_t* ptr_window = &inst->window[0];
ptr_out = &out[0];
ptr_ana = &inst->analysisBuffer[0];
- for (; ptr_out < &out[inst->anaLen]; ) {
+ for (; ptr_out < &out[inst->anaLen];) {
// Loop unrolled once, so all pointers are incremented by 4 twice.
__asm__ __volatile__(
@@ -683,17 +681,17 @@
// Create a complex number buffer (out[]) as the intput (in[]) interleaved with
// zeros, and normalize it.
-__inline void WebRtcNsx_CreateComplexBuffer(NsxInst_t* inst,
- int16_t* in,
- int16_t* out) {
+static __inline void CreateComplexBufferNeon(NsxInst_t* inst,
+ int16_t* in,
+ int16_t* out) {
int16_t* ptr_out = &out[0];
int16_t* ptr_in = &in[0];
__asm__ __volatile__("vdup.16 d25, %0" : : "r"(0) : "d25");
__asm__ __volatile__("vdup.16 q10, %0" : : "r"(inst->normData) : "q10");
- for (; ptr_in < &in[inst->anaLen]; ) {
+ for (; ptr_in < &in[inst->anaLen];) {
- // Loop unrolled once, so ptr_in is incremented by 8 twice,
+ // Loop unrolled once, so ptr_in is incremented by 8 twice,
// and ptr_out is incremented by 8 four times.
__asm__ __volatile__(
// out[j] = WEBRTC_SPL_LSHIFT_W16(in[i], inst->normData); // Q(normData)
@@ -724,4 +722,12 @@
);
}
}
-#endif // defined(WEBRTC_ARCH_ARM_NEON) && defined(WEBRTC_ANDROID)
+
+void WebRtcNsx_InitNeon(void) {
+ WebRtcNsx_NoiseEstimation = NoiseEstimationNeon;
+ WebRtcNsx_PrepareSpectrum = PrepareSpectrumNeon;
+ WebRtcNsx_SynthesisUpdate = SynthesisUpdateNeon;
+ WebRtcNsx_AnalysisUpdate = AnalysisUpdateNeon;
+ WebRtcNsx_Denormalize = DenormalizeNeon;
+ WebRtcNsx_CreateComplexBuffer = CreateComplexBufferNeon;
+}
diff --git a/src/system_wrappers/interface/cpu_features_wrapper.h b/src/system_wrappers/interface/cpu_features_wrapper.h
index 5d8a828..d949592 100644
--- a/src/system_wrappers/interface/cpu_features_wrapper.h
+++ b/src/system_wrappers/interface/cpu_features_wrapper.h
@@ -15,18 +15,33 @@
extern "C" {
#endif
-// list of features.
+#include <typedefs.h>
+
+// List of features in x86.
typedef enum {
kSSE2,
kSSE3
} CPUFeature;
+// List of features in ARM.
+enum {
+ kCPUFeatureARMv7 = (1 << 0),
+ kCPUFeatureVFPv3 = (1 << 1),
+ kCPUFeatureNEON = (1 << 2),
+ kCPUFeatureLDREXSTREX = (1 << 3)
+};
+
typedef int (*WebRtc_CPUInfo)(CPUFeature feature);
// returns true if the CPU supports the feature.
extern WebRtc_CPUInfo WebRtc_GetCPUInfo;
// No CPU feature is available => straight C path.
extern WebRtc_CPUInfo WebRtc_GetCPUInfoNoASM;
+// Return the features in an ARM device.
+// It detects the features in the hardware platform, and returns supported
+// values in the above enum definition as a bitmask.
+extern uint64_t WebRtc_GetCPUFeaturesARM(void);
+
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
diff --git a/src/system_wrappers/source/Android.mk b/src/system_wrappers/source/Android.mk
index a5d1439..00a69ce 100644
--- a/src/system_wrappers/source/Android.mk
+++ b/src/system_wrappers/source/Android.mk
@@ -25,6 +25,7 @@
condition_variable.cc \
cpu_dummy.cc \
cpu_features.cc \
+ cpu_features_arm.c \
cpu_info.cc \
critical_section.cc \
event.cc \
diff --git a/src/system_wrappers/source/cpu_features_arm.c b/src/system_wrappers/source/cpu_features_arm.c
new file mode 100644
index 0000000..1065118
--- /dev/null
+++ b/src/system_wrappers/source/cpu_features_arm.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+
+// This file is derived from Android's NDK package r7, located at
+// <ndk>/sources/android/cpufeatures/ (downloadable from
+// http://developer.android.com/sdk/ndk/index.html).
+
+#include "cpu_features_wrapper.h"
+
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// Define CPU family.
+typedef enum {
+ CPU_FAMILY_UNKNOWN = 0,
+ CPU_FAMILY_ARM,
+ CPU_FAMILY_X86,
+ CPU_FAMILY_MAX // Do not remove.
+} CpuFamily;
+
+static pthread_once_t g_once;
+static CpuFamily g_cpuFamily;
+static uint64_t g_cpuFeatures;
+static int g_cpuCount;
+
+static const int cpufeatures_debug = 0;
+
+#ifdef __arm__
+# define DEFAULT_CPU_FAMILY CPU_FAMILY_ARM
+#elif defined __i386__
+# define DEFAULT_CPU_FAMILY CPU_FAMILY_X86
+#else
+# define DEFAULT_CPU_FAMILY CPU_FAMILY_UNKNOWN
+#endif
+
+#define D(...) \
+ do { \
+ if (cpufeatures_debug) { \
+ printf(__VA_ARGS__); fflush(stdout); \
+ } \
+ } while (0)
+
+/* Read the content of /proc/cpuinfo into a user-provided buffer.
+ * Return the length of the data, or -1 on error. Does *not*
+ * zero-terminate the content. Will not read more
+ * than 'buffsize' bytes.
+ */
+static int read_file(const char* pathname, char* buffer, size_t buffsize) {
+ int fd, len;
+
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ do {
+ len = read(fd, buffer, buffsize);
+ } while (len < 0 && errno == EINTR);
+
+ close(fd);
+
+ return len;
+}
+
+/* Extract the content of a the first occurence of a given field in
+ * the content of /proc/cpuinfo and return it as a heap-allocated
+ * string that must be freed by the caller.
+ *
+ * Return NULL if not found
+ */
+static char* extract_cpuinfo_field(char* buffer, int buflen, const char* field) {
+ int fieldlen = strlen(field);
+ char* bufend = buffer + buflen;
+ char* result = NULL;
+ int len, ignore;
+ const char* p, *q;
+
+ /* Look for first field occurence, and ensures it starts the line.
+ */
+ p = buffer;
+ bufend = buffer + buflen;
+ for (;;) {
+ p = memmem(p, bufend - p, field, fieldlen);
+ if (p == NULL)
+ goto EXIT;
+
+ if (p == buffer || p[-1] == '\n')
+ break;
+
+ p += fieldlen;
+ }
+
+ /* Skip to the first column followed by a space */
+ p += fieldlen;
+ p = memchr(p, ':', bufend - p);
+ if (p == NULL || p[1] != ' ')
+ goto EXIT;
+
+ /* Find the end of the line */
+ p += 2;
+ q = memchr(p, '\n', bufend - p);
+ if (q == NULL)
+ q = bufend;
+
+ /* Copy the line into a heap-allocated buffer */
+ len = q - p;
+ result = malloc(len + 1);
+ if (result == NULL)
+ goto EXIT;
+
+ memcpy(result, p, len);
+ result[len] = '\0';
+
+EXIT:
+ return result;
+}
+
+/* Count the number of occurences of a given field prefix in /proc/cpuinfo.
+ */
+static int count_cpuinfo_field(char* buffer, int buflen, const char* field) {
+ int fieldlen = strlen(field);
+ const char* p = buffer;
+ const char* bufend = buffer + buflen;
+ const char* q;
+ int count = 0;
+
+ for (;;) {
+ const char* q;
+
+ p = memmem(p, bufend - p, field, fieldlen);
+ if (p == NULL)
+ break;
+
+ /* Ensure that the field is at the start of a line */
+ if (p > buffer && p[-1] != '\n') {
+ p += fieldlen;
+ continue;
+ }
+
+
+ /* skip any whitespace */
+ q = p + fieldlen;
+ while (q < bufend && (*q == ' ' || *q == '\t'))
+ q++;
+
+ /* we must have a colon now */
+ if (q < bufend && *q == ':') {
+ count += 1;
+ q ++;
+ }
+ p = q;
+ }
+
+ return count;
+}
+
+/* Like strlen(), but for constant string literals */
+#define STRLEN_CONST(x) ((sizeof(x)-1)
+
+
+/* Checks that a space-separated list of items contains one given 'item'.
+ * Returns 1 if found, 0 otherwise.
+ */
+static int has_list_item(const char* list, const char* item) {
+ const char* p = list;
+ int itemlen = strlen(item);
+
+ if (list == NULL)
+ return 0;
+
+ while (*p) {
+ const char* q;
+
+ /* skip spaces */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ /* find end of current list item */
+ q = p;
+ while (*q && *q != ' ' && *q != '\t')
+ q++;
+
+ if (itemlen == q - p && !memcmp(p, item, itemlen))
+ return 1;
+
+ /* skip to next item */
+ p = q;
+ }
+ return 0;
+}
+
+
+static void cpuInit(void) {
+ char cpuinfo[4096];
+ int cpuinfo_len;
+
+ g_cpuFamily = DEFAULT_CPU_FAMILY;
+ g_cpuFeatures = 0;
+ g_cpuCount = 1;
+
+ cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
+ D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len,
+ cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo);
+
+ if (cpuinfo_len < 0) { /* should not happen */
+ return;
+ }
+
+ /* Count the CPU cores, the value may be 0 for single-core CPUs */
+ g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor");
+ if (g_cpuCount == 0) {
+ g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor");
+ if (g_cpuCount == 0) {
+ g_cpuCount = 1;
+ }
+ }
+
+ D("found cpuCount = %d\n", g_cpuCount);
+
+#ifdef __arm__
+ {
+ char* features = NULL;
+ char* architecture = NULL;
+
+ /* Extract architecture from the "CPU Architecture" field.
+ * The list is well-known, unlike the the output of
+ * the 'Processor' field which can vary greatly.
+ *
+ * See the definition of the 'proc_arch' array in
+ * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
+ * same file.
+ */
+ char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
+ "CPU architecture");
+
+ if (cpuArch != NULL) {
+ char* end;
+ long archNumber;
+ int hasARMv7 = 0;
+
+ D("found cpuArch = '%s'\n", cpuArch);
+
+ /* read the initial decimal number, ignore the rest */
+ archNumber = strtol(cpuArch, &end, 10);
+
+ /* Here we assume that ARMv8 will be upwards compatible with v7
+ * in the future. Unfortunately, there is no 'Features' field to
+ * indicate that Thumb-2 is supported.
+ */
+ if (end > cpuArch && archNumber >= 7) {
+ hasARMv7 = 1;
+ }
+
+ /* Unfortunately, it seems that certain ARMv6-based CPUs
+ * report an incorrect architecture number of 7!
+ *
+ * We try to correct this by looking at the 'elf_format'
+ * field reported by the 'Processor' field, which is of the
+ * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
+ * an ARMv6-one.
+ */
+ if (hasARMv7) {
+ char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
+ "Processor");
+ if (cpuProc != NULL) {
+ D("found cpuProc = '%s'\n", cpuProc);
+ if (has_list_item(cpuProc, "(v6l)")) {
+ D("CPU processor and architecture mismatch!!\n");
+ hasARMv7 = 0;
+ }
+ free(cpuProc);
+ }
+ }
+
+ if (hasARMv7) {
+ g_cpuFeatures |= kCPUFeatureARMv7;
+ }
+
+ /* The LDREX / STREX instructions are available from ARMv6 */
+ if (archNumber >= 6) {
+ g_cpuFeatures |= kCPUFeatureLDREXSTREX;
+ }
+
+ free(cpuArch);
+ }
+
+ /* Extract the list of CPU features from 'Features' field */
+ char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
+ "Features");
+
+ if (cpuFeatures != NULL) {
+
+ D("found cpuFeatures = '%s'\n", cpuFeatures);
+
+ if (has_list_item(cpuFeatures, "vfpv3"))
+ g_cpuFeatures |= kCPUFeatureVFPv3;
+
+ else if (has_list_item(cpuFeatures, "vfpv3d16"))
+ g_cpuFeatures |= kCPUFeatureVFPv3;
+
+ if (has_list_item(cpuFeatures, "neon")) {
+ /* Note: Certain kernels only report neon but not vfpv3
+ * in their features list. However, ARM mandates
+ * that if Neon is implemented, so must be VFPv3
+ * so always set the flag.
+ */
+ g_cpuFeatures |= kCPUFeatureNEON |
+ kCPUFeatureVFPv3;
+ }
+ free(cpuFeatures);
+ }
+ }
+#endif // __arm__
+
+#ifdef __i386__
+ g_cpuFamily = CPU_FAMILY_X86;
+#endif
+}
+
+
+uint64_t WebRtc_GetCPUFeaturesARM(void) {
+ pthread_once(&g_once, cpuInit);
+ return g_cpuFeatures;
+}