1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "common_audio/vad/vad_sp.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
14*d9f75844SAndroid Build Coastguard Worker #include "common_audio/signal_processing/include/signal_processing_library.h"
15*d9f75844SAndroid Build Coastguard Worker #include "common_audio/vad/vad_core.h"
16*d9f75844SAndroid Build Coastguard Worker
17*d9f75844SAndroid Build Coastguard Worker // Allpass filter coefficients, upper and lower, in Q13.
18*d9f75844SAndroid Build Coastguard Worker // Upper: 0.64, Lower: 0.17.
19*d9f75844SAndroid Build Coastguard Worker static const int16_t kAllPassCoefsQ13[2] = { 5243, 1392 }; // Q13.
20*d9f75844SAndroid Build Coastguard Worker static const int16_t kSmoothingDown = 6553; // 0.2 in Q15.
21*d9f75844SAndroid Build Coastguard Worker static const int16_t kSmoothingUp = 32439; // 0.99 in Q15.
22*d9f75844SAndroid Build Coastguard Worker
23*d9f75844SAndroid Build Coastguard Worker // TODO(bjornv): Move this function to vad_filterbank.c.
24*d9f75844SAndroid Build Coastguard Worker // Downsampling filter based on splitting filter and allpass functions.
WebRtcVad_Downsampling(const int16_t * signal_in,int16_t * signal_out,int32_t * filter_state,size_t in_length)25*d9f75844SAndroid Build Coastguard Worker void WebRtcVad_Downsampling(const int16_t* signal_in,
26*d9f75844SAndroid Build Coastguard Worker int16_t* signal_out,
27*d9f75844SAndroid Build Coastguard Worker int32_t* filter_state,
28*d9f75844SAndroid Build Coastguard Worker size_t in_length) {
29*d9f75844SAndroid Build Coastguard Worker int16_t tmp16_1 = 0, tmp16_2 = 0;
30*d9f75844SAndroid Build Coastguard Worker int32_t tmp32_1 = filter_state[0];
31*d9f75844SAndroid Build Coastguard Worker int32_t tmp32_2 = filter_state[1];
32*d9f75844SAndroid Build Coastguard Worker size_t n = 0;
33*d9f75844SAndroid Build Coastguard Worker // Downsampling by 2 gives half length.
34*d9f75844SAndroid Build Coastguard Worker size_t half_length = (in_length >> 1);
35*d9f75844SAndroid Build Coastguard Worker
36*d9f75844SAndroid Build Coastguard Worker // Filter coefficients in Q13, filter state in Q0.
37*d9f75844SAndroid Build Coastguard Worker for (n = 0; n < half_length; n++) {
38*d9f75844SAndroid Build Coastguard Worker // All-pass filtering upper branch.
39*d9f75844SAndroid Build Coastguard Worker tmp16_1 = (int16_t) ((tmp32_1 >> 1) +
40*d9f75844SAndroid Build Coastguard Worker ((kAllPassCoefsQ13[0] * *signal_in) >> 14));
41*d9f75844SAndroid Build Coastguard Worker *signal_out = tmp16_1;
42*d9f75844SAndroid Build Coastguard Worker tmp32_1 = (int32_t)(*signal_in++) - ((kAllPassCoefsQ13[0] * tmp16_1) >> 12);
43*d9f75844SAndroid Build Coastguard Worker
44*d9f75844SAndroid Build Coastguard Worker // All-pass filtering lower branch.
45*d9f75844SAndroid Build Coastguard Worker tmp16_2 = (int16_t) ((tmp32_2 >> 1) +
46*d9f75844SAndroid Build Coastguard Worker ((kAllPassCoefsQ13[1] * *signal_in) >> 14));
47*d9f75844SAndroid Build Coastguard Worker *signal_out++ += tmp16_2;
48*d9f75844SAndroid Build Coastguard Worker tmp32_2 = (int32_t)(*signal_in++) - ((kAllPassCoefsQ13[1] * tmp16_2) >> 12);
49*d9f75844SAndroid Build Coastguard Worker }
50*d9f75844SAndroid Build Coastguard Worker // Store the filter states.
51*d9f75844SAndroid Build Coastguard Worker filter_state[0] = tmp32_1;
52*d9f75844SAndroid Build Coastguard Worker filter_state[1] = tmp32_2;
53*d9f75844SAndroid Build Coastguard Worker }
54*d9f75844SAndroid Build Coastguard Worker
55*d9f75844SAndroid Build Coastguard Worker // Inserts `feature_value` into `low_value_vector`, if it is one of the 16
56*d9f75844SAndroid Build Coastguard Worker // smallest values the last 100 frames. Then calculates and returns the median
57*d9f75844SAndroid Build Coastguard Worker // of the five smallest values.
WebRtcVad_FindMinimum(VadInstT * self,int16_t feature_value,int channel)58*d9f75844SAndroid Build Coastguard Worker int16_t WebRtcVad_FindMinimum(VadInstT* self,
59*d9f75844SAndroid Build Coastguard Worker int16_t feature_value,
60*d9f75844SAndroid Build Coastguard Worker int channel) {
61*d9f75844SAndroid Build Coastguard Worker int i = 0, j = 0;
62*d9f75844SAndroid Build Coastguard Worker int position = -1;
63*d9f75844SAndroid Build Coastguard Worker // Offset to beginning of the 16 minimum values in memory.
64*d9f75844SAndroid Build Coastguard Worker const int offset = (channel << 4);
65*d9f75844SAndroid Build Coastguard Worker int16_t current_median = 1600;
66*d9f75844SAndroid Build Coastguard Worker int16_t alpha = 0;
67*d9f75844SAndroid Build Coastguard Worker int32_t tmp32 = 0;
68*d9f75844SAndroid Build Coastguard Worker // Pointer to memory for the 16 minimum values and the age of each value of
69*d9f75844SAndroid Build Coastguard Worker // the `channel`.
70*d9f75844SAndroid Build Coastguard Worker int16_t* age = &self->index_vector[offset];
71*d9f75844SAndroid Build Coastguard Worker int16_t* smallest_values = &self->low_value_vector[offset];
72*d9f75844SAndroid Build Coastguard Worker
73*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(channel, kNumChannels);
74*d9f75844SAndroid Build Coastguard Worker
75*d9f75844SAndroid Build Coastguard Worker // Each value in `smallest_values` is getting 1 loop older. Update `age`, and
76*d9f75844SAndroid Build Coastguard Worker // remove old values.
77*d9f75844SAndroid Build Coastguard Worker for (i = 0; i < 16; i++) {
78*d9f75844SAndroid Build Coastguard Worker if (age[i] != 100) {
79*d9f75844SAndroid Build Coastguard Worker age[i]++;
80*d9f75844SAndroid Build Coastguard Worker } else {
81*d9f75844SAndroid Build Coastguard Worker // Too old value. Remove from memory and shift larger values downwards.
82*d9f75844SAndroid Build Coastguard Worker for (j = i; j < 15; j++) {
83*d9f75844SAndroid Build Coastguard Worker smallest_values[j] = smallest_values[j + 1];
84*d9f75844SAndroid Build Coastguard Worker age[j] = age[j + 1];
85*d9f75844SAndroid Build Coastguard Worker }
86*d9f75844SAndroid Build Coastguard Worker age[15] = 101;
87*d9f75844SAndroid Build Coastguard Worker smallest_values[15] = 10000;
88*d9f75844SAndroid Build Coastguard Worker }
89*d9f75844SAndroid Build Coastguard Worker }
90*d9f75844SAndroid Build Coastguard Worker
91*d9f75844SAndroid Build Coastguard Worker // Check if `feature_value` is smaller than any of the values in
92*d9f75844SAndroid Build Coastguard Worker // `smallest_values`. If so, find the `position` where to insert the new value
93*d9f75844SAndroid Build Coastguard Worker // (`feature_value`).
94*d9f75844SAndroid Build Coastguard Worker if (feature_value < smallest_values[7]) {
95*d9f75844SAndroid Build Coastguard Worker if (feature_value < smallest_values[3]) {
96*d9f75844SAndroid Build Coastguard Worker if (feature_value < smallest_values[1]) {
97*d9f75844SAndroid Build Coastguard Worker if (feature_value < smallest_values[0]) {
98*d9f75844SAndroid Build Coastguard Worker position = 0;
99*d9f75844SAndroid Build Coastguard Worker } else {
100*d9f75844SAndroid Build Coastguard Worker position = 1;
101*d9f75844SAndroid Build Coastguard Worker }
102*d9f75844SAndroid Build Coastguard Worker } else if (feature_value < smallest_values[2]) {
103*d9f75844SAndroid Build Coastguard Worker position = 2;
104*d9f75844SAndroid Build Coastguard Worker } else {
105*d9f75844SAndroid Build Coastguard Worker position = 3;
106*d9f75844SAndroid Build Coastguard Worker }
107*d9f75844SAndroid Build Coastguard Worker } else if (feature_value < smallest_values[5]) {
108*d9f75844SAndroid Build Coastguard Worker if (feature_value < smallest_values[4]) {
109*d9f75844SAndroid Build Coastguard Worker position = 4;
110*d9f75844SAndroid Build Coastguard Worker } else {
111*d9f75844SAndroid Build Coastguard Worker position = 5;
112*d9f75844SAndroid Build Coastguard Worker }
113*d9f75844SAndroid Build Coastguard Worker } else if (feature_value < smallest_values[6]) {
114*d9f75844SAndroid Build Coastguard Worker position = 6;
115*d9f75844SAndroid Build Coastguard Worker } else {
116*d9f75844SAndroid Build Coastguard Worker position = 7;
117*d9f75844SAndroid Build Coastguard Worker }
118*d9f75844SAndroid Build Coastguard Worker } else if (feature_value < smallest_values[15]) {
119*d9f75844SAndroid Build Coastguard Worker if (feature_value < smallest_values[11]) {
120*d9f75844SAndroid Build Coastguard Worker if (feature_value < smallest_values[9]) {
121*d9f75844SAndroid Build Coastguard Worker if (feature_value < smallest_values[8]) {
122*d9f75844SAndroid Build Coastguard Worker position = 8;
123*d9f75844SAndroid Build Coastguard Worker } else {
124*d9f75844SAndroid Build Coastguard Worker position = 9;
125*d9f75844SAndroid Build Coastguard Worker }
126*d9f75844SAndroid Build Coastguard Worker } else if (feature_value < smallest_values[10]) {
127*d9f75844SAndroid Build Coastguard Worker position = 10;
128*d9f75844SAndroid Build Coastguard Worker } else {
129*d9f75844SAndroid Build Coastguard Worker position = 11;
130*d9f75844SAndroid Build Coastguard Worker }
131*d9f75844SAndroid Build Coastguard Worker } else if (feature_value < smallest_values[13]) {
132*d9f75844SAndroid Build Coastguard Worker if (feature_value < smallest_values[12]) {
133*d9f75844SAndroid Build Coastguard Worker position = 12;
134*d9f75844SAndroid Build Coastguard Worker } else {
135*d9f75844SAndroid Build Coastguard Worker position = 13;
136*d9f75844SAndroid Build Coastguard Worker }
137*d9f75844SAndroid Build Coastguard Worker } else if (feature_value < smallest_values[14]) {
138*d9f75844SAndroid Build Coastguard Worker position = 14;
139*d9f75844SAndroid Build Coastguard Worker } else {
140*d9f75844SAndroid Build Coastguard Worker position = 15;
141*d9f75844SAndroid Build Coastguard Worker }
142*d9f75844SAndroid Build Coastguard Worker }
143*d9f75844SAndroid Build Coastguard Worker
144*d9f75844SAndroid Build Coastguard Worker // If we have detected a new small value, insert it at the correct position
145*d9f75844SAndroid Build Coastguard Worker // and shift larger values up.
146*d9f75844SAndroid Build Coastguard Worker if (position > -1) {
147*d9f75844SAndroid Build Coastguard Worker for (i = 15; i > position; i--) {
148*d9f75844SAndroid Build Coastguard Worker smallest_values[i] = smallest_values[i - 1];
149*d9f75844SAndroid Build Coastguard Worker age[i] = age[i - 1];
150*d9f75844SAndroid Build Coastguard Worker }
151*d9f75844SAndroid Build Coastguard Worker smallest_values[position] = feature_value;
152*d9f75844SAndroid Build Coastguard Worker age[position] = 1;
153*d9f75844SAndroid Build Coastguard Worker }
154*d9f75844SAndroid Build Coastguard Worker
155*d9f75844SAndroid Build Coastguard Worker // Get `current_median`.
156*d9f75844SAndroid Build Coastguard Worker if (self->frame_counter > 2) {
157*d9f75844SAndroid Build Coastguard Worker current_median = smallest_values[2];
158*d9f75844SAndroid Build Coastguard Worker } else if (self->frame_counter > 0) {
159*d9f75844SAndroid Build Coastguard Worker current_median = smallest_values[0];
160*d9f75844SAndroid Build Coastguard Worker }
161*d9f75844SAndroid Build Coastguard Worker
162*d9f75844SAndroid Build Coastguard Worker // Smooth the median value.
163*d9f75844SAndroid Build Coastguard Worker if (self->frame_counter > 0) {
164*d9f75844SAndroid Build Coastguard Worker if (current_median < self->mean_value[channel]) {
165*d9f75844SAndroid Build Coastguard Worker alpha = kSmoothingDown; // 0.2 in Q15.
166*d9f75844SAndroid Build Coastguard Worker } else {
167*d9f75844SAndroid Build Coastguard Worker alpha = kSmoothingUp; // 0.99 in Q15.
168*d9f75844SAndroid Build Coastguard Worker }
169*d9f75844SAndroid Build Coastguard Worker }
170*d9f75844SAndroid Build Coastguard Worker tmp32 = (alpha + 1) * self->mean_value[channel];
171*d9f75844SAndroid Build Coastguard Worker tmp32 += (WEBRTC_SPL_WORD16_MAX - alpha) * current_median;
172*d9f75844SAndroid Build Coastguard Worker tmp32 += 16384;
173*d9f75844SAndroid Build Coastguard Worker self->mean_value[channel] = (int16_t) (tmp32 >> 15);
174*d9f75844SAndroid Build Coastguard Worker
175*d9f75844SAndroid Build Coastguard Worker return self->mean_value[channel];
176*d9f75844SAndroid Build Coastguard Worker }
177