audiofuntest: Add option to evaluate power level

BUG=b:178681150
TEST=emerge-zork audiofuntest;
     manually test on DUT;

Change-Id: If753c82b5dbc2dbd7852b4e55c7d8b2c37130880
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/audiotest/+/2928885
Commit-Queue: Gene Chang <genechang@google.com>
Tested-by: Gene Chang <genechang@google.com>
Auto-Submit: Gene Chang <genechang@google.com>
Reviewed-by: Pin-yen Lin <treapking@chromium.org>
Reviewed-by: Yu-Hsuan Hsu <yuhsuan@chromium.org>
diff --git a/include/common.h b/include/common.h
index 2fdb772..b5cb8b7 100644
--- a/include/common.h
+++ b/include/common.h
@@ -47,6 +47,7 @@
       : allowed_delay_sec(1.5),
         fft_size(2048u),
         match_window_size(7),
+        power_threshold(0.01),
         confidence_threshold(3),
         sample_rate(64000),
         sample_format(SampleFormat::kPcmS16),
@@ -64,6 +65,7 @@
   double allowed_delay_sec;
   int fft_size;
   int match_window_size;
+  double power_threshold;
   double confidence_threshold;
   std::string player_command;
   std::string player_fifo;
diff --git a/include/evaluator.h b/include/evaluator.h
index b6b5322..159cbfc 100644
--- a/include/evaluator.h
+++ b/include/evaluator.h
@@ -40,6 +40,7 @@
   std::unique_ptr<uint8_t[]> buffer_;
   size_t buf_size_;
 
+  double power_threshold_;
   double confidence_threshold_;
   int max_trial_;
 
diff --git a/src/audiofuntest.cc b/src/audiofuntest.cc
index 0bda5fd..f1b434e 100644
--- a/src/audiofuntest.cc
+++ b/src/audiofuntest.cc
@@ -16,33 +16,34 @@
 #include "include/sample_format.h"
 #include "include/tone_generators.h"
 
-constexpr static const char *short_options =
-    "a:m:d:n:o:w:P:f:R:F:r:t:c:C:T:l:g:i:x:hv";
+constexpr static const char* short_options =
+    "a:m:d:n:o:w:p:P:f:R:F:r:t:c:C:T:l:g:i:x:hv";
 
 constexpr static const struct option long_options[] = {
-  {"active-speaker-channels", 1, NULL, 'a'},
-  {"active-mic-channels", 1, NULL, 'm'},
-  {"allowed-delay", 1, NULL, 'd'},
-  {"fft-size", 1, NULL, 'n'},
-  {"confidence-threshold", 1, NULL, 'o'},
-  {"match-window-size", 1, NULL, 'w'},
-  {"player-command", 1, NULL, 'P'},
-  {"player-fifo", 1, NULL, 'f'},
-  {"recorder-command", 1, NULL, 'R'},
-  {"recorder-fifo", 1, NULL, 'F'},
-  {"sample-rate", 1, NULL, 'r'},
-  {"sample-format", 1, NULL, 't'},
-  {"num-mic-channels", 1, NULL, 'c'},
-  {"num-speaker-channels", 1, NULL, 'C'},
-  {"test-rounds", 1, NULL, 'T'},
-  {"tone-length", 1, NULL, 'l'},
-  {"volume-gain", 1, NULL, 'g'},
-  {"min-frequency", 1, NULL, 'i'},
-  {"max-frequency", 1, NULL, 'x'},
+    {"active-speaker-channels", 1, NULL, 'a'},
+    {"active-mic-channels", 1, NULL, 'm'},
+    {"allowed-delay", 1, NULL, 'd'},
+    {"fft-size", 1, NULL, 'n'},
+    {"power-threshold", 1, NULL, 'p'},
+    {"confidence-threshold", 1, NULL, 'o'},
+    {"match-window-size", 1, NULL, 'w'},
+    {"player-command", 1, NULL, 'P'},
+    {"player-fifo", 1, NULL, 'f'},
+    {"recorder-command", 1, NULL, 'R'},
+    {"recorder-fifo", 1, NULL, 'F'},
+    {"sample-rate", 1, NULL, 'r'},
+    {"sample-format", 1, NULL, 't'},
+    {"num-mic-channels", 1, NULL, 'c'},
+    {"num-speaker-channels", 1, NULL, 'C'},
+    {"test-rounds", 1, NULL, 'T'},
+    {"tone-length", 1, NULL, 'l'},
+    {"volume-gain", 1, NULL, 'g'},
+    {"min-frequency", 1, NULL, 'i'},
+    {"max-frequency", 1, NULL, 'x'},
 
-  // Other helper args.
-  {"help", 0, NULL, 'h'},
-  {"verbose", 0, NULL, 'v'},
+    // Other helper args.
+    {"help", 0, NULL, 'h'},
+    {"verbose", 0, NULL, 'v'},
 };
 
 // Parse the sample format. The input should be one of the string in
@@ -96,6 +97,9 @@
           return false;
         }
         break;
+      case 'p':
+        config->power_threshold = atof(optarg);
+        break;
       case 'P':
         config->player_command = std::string(optarg);
         break;
@@ -214,6 +218,11 @@
           " Also, fftsize needs to be power of 2"
           "(def %d)\n", default_config.fft_size);
   fprintf(fd,
+          "\t-p, --power-threshold:\n"
+          "\t\tThreshold of RMS value to pass evaluation "
+          "(def %.4f)\n",
+          default_config.power_threshold);
+  fprintf(fd,
           "\t-o, --confidence-threshold:\n"
           "\t\tThreshold of accumulated confidence to pass evaluation "
           "(def %.4f)\n", default_config.confidence_threshold);
diff --git a/src/evaluator.cc b/src/evaluator.cc
index 6c424b6..ec7d6ed 100644
--- a/src/evaluator.cc
+++ b/src/evaluator.cc
@@ -78,7 +78,7 @@
 
 }  // namespace
 
-Evaluator::Evaluator(const AudioFunTestConfig &config)
+Evaluator::Evaluator(const AudioFunTestConfig& config)
     : filter_(config.match_window_size),
       half_window_size_(config.match_window_size / 2),
       num_channels_(config.num_mic_channels),
@@ -86,6 +86,7 @@
       format_(config.sample_format),
       sample_rate_(config.sample_rate),
       bin_(config.match_window_size),
+      power_threshold_(config.power_threshold),
       confidence_threshold_(config.confidence_threshold),
       verbose_(config.verbose) {
   // Initializes original expected filter.
@@ -146,6 +147,24 @@
 
 double Evaluator::EstimateChannel(
     std::vector<double> *data, int center_bin) {
+  // Calculate RMS before FFT
+  // The |data| here is already formatted as double sized to store the result
+  // (read, imaginary) from FFT.
+  int data_size = data->size() / 2;
+  double rms = 0.0;
+  for (int t = 0; t < data_size; ++t) {
+    const double amplitude = (*data)[2 * t];
+    rms += amplitude * amplitude;
+  }
+  rms = sqrt(rms / data_size);
+  if (verbose_)
+    printf("rms: %0.4f\n", rms);
+
+  if (rms < power_threshold_) {
+    perror("The RMS level is too low.");
+    return 0.0;
+  }
+
   FFT(data);
 
   double confidence = 0.0, mean = 0.0, sigma = 0.0;