libeffects: Added standalone testbench that calculates SNR value

Added a standalone test bench that calculates SNR values using 2 files
as input. This tool is used to test the multichannel changes.

Test: build_and_run_all_unit_tests.sh
Bug: 121353611
Change-Id: I5e67d8113bace7872133f2e02d9ae7b9d90e61ff
diff --git a/media/libeffects/lvm/tests/Android.bp b/media/libeffects/lvm/tests/Android.bp
index 8ee807c..003ce9e 100644
--- a/media/libeffects/lvm/tests/Android.bp
+++ b/media/libeffects/lvm/tests/Android.bp
@@ -44,3 +44,16 @@
         "-Wextra",
     ],
 }
+
+cc_test {
+    name: "snr",
+    host_supported: false,
+
+    srcs: ["snr.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
index 861ee64..41a4f04 100755
--- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
+++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
@@ -25,16 +25,17 @@
 adb shell mkdir -p $testdir
 adb push $ANDROID_BUILD_TOP/cts/tests/tests/media/res/raw/sinesweepraw.raw $testdir
 adb push $OUT/testcases/lvmtest/arm64/lvmtest $testdir
+adb push $OUT/testcases/snr/arm64/snr $testdir
 
 flags_arr=(
     "-csE"
     "-eqE"
     "-tE"
     "-csE -tE -eqE"
-    "-bE"
+    "-bE -M"
     "-csE -tE"
     "-csE -eqE" "-tE -eqE"
-    "-csE -tE -bE -eqE"
+    "-csE -tE -bE -M -eqE"
 )
 
 fs_arr=(
@@ -79,6 +80,10 @@
             then
                 adb shell cmp $testdir/sinesweep_2_$((fs)).raw \
                     $testdir/sinesweep_$((ch))_$((fs)).raw
+            elif [[ $flags == *"-bE"* ]] && [ "$ch" -gt 2 ]
+            then
+                adb shell $testdir/snr $testdir/sinesweep_2_$((fs)).raw \
+                    $testdir/sinesweep_$((ch))_$((fs)).raw -thr:90.308998
             fi
 
         done
diff --git a/media/libeffects/lvm/tests/snr.cpp b/media/libeffects/lvm/tests/snr.cpp
new file mode 100644
index 0000000..88110c0
--- /dev/null
+++ b/media/libeffects/lvm/tests/snr.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <assert.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vector>
+
+template <typename T, typename A = float>
+std::pair<A, A> getSignalNoise(FILE *finp, FILE *fref) {
+  constexpr size_t framesize = 256;
+  std::vector<T> in(framesize);
+  std::vector<T> ref(framesize);
+  A signal{};
+  A noise{};
+
+  for (;;) {
+    size_t read_samples_in = fread(&in[0], sizeof(T), framesize, finp);
+    const size_t read_samples_ref = fread(&ref[0], sizeof(T), framesize, fref);
+    if (read_samples_in != read_samples_ref) {
+      printf("file sizes do not match (last %zu %zu)", read_samples_in, read_samples_ref);
+      read_samples_in = std::min(read_samples_in, read_samples_ref);
+    }
+    if (read_samples_in == 0) {
+        return { signal, noise };
+    }
+    for (size_t i = 0; i < read_samples_in; ++i) {
+       const A value(ref[i]);
+       const A diff(A(in[i]) - value);
+       signal += value * value;
+       noise += diff * diff;
+    }
+  }
+}
+
+void printUsage() {
+  printf("\nUsage: ");
+  printf("\n     snr <ref_file> <test_file> [options]\n");
+  printf("\nwhere, \n     <ref_file>  is the reference file name");
+  printf("\n                  on which will be taken as pure signal");
+  printf("\n     <test_file> is test file for snr calculation");
+  printf("\n     and options are mentioned below");
+  printf("\n");
+  printf("\n     -pcm_format:<pcm format of input files>");
+  printf("\n           0 - 16 bit pcm");
+  printf("\n           1 - 32 bit float");
+  printf("\n           default 0");
+  printf("\n     -thr:<threshold value>");
+  printf("\n           default - negative infinity\n\n");
+}
+
+int main(int argc, const char *argv[]) {
+  if (argc < 3) {
+    printUsage();
+    return -1;
+  }
+  int pcm_format = 0;
+  float thr = - std::numeric_limits<float>::infinity();
+  FILE *fref = fopen(argv[1], "rb");
+  FILE *finp = fopen(argv[2], "rb");
+  for (int i = 3; i < argc; i++) {
+    if (!strncmp(argv[i], "-pcm_format:", 12)) {
+      pcm_format = atoi(argv[i] + 12);
+    } else if (!strncmp(argv[i], "-thr:", 5)) {
+      thr = atof(argv[i] + 5);
+    }
+  }
+  if (finp == nullptr || fref == nullptr) {
+    printf("\nError: missing input/reference files\n");
+    return -1;
+  }
+  auto sn = pcm_format == 0
+      ? getSignalNoise<short>(finp, fref)
+      : getSignalNoise<float>(finp, fref);
+  if (sn.first > 0.f && sn.second > 0.f) {
+    float snr = 10.f * log(sn.first / sn.second);
+    // compare the measured snr value with threshold
+    if (snr < thr) {
+      printf("%.6f less than threshold %.6f\n", snr, thr);
+    } else {
+      printf("%.6f\n", snr);
+    }
+  }
+  fclose(finp);
+  fclose(fref);
+
+  return 0;
+}