iSAC unit test: test encode/decode via API wrapper

Unit test to test the iSAC webrtc API wrapper, plus a minor
change in the c iSAC wrapper.

Bug: webrtc:10584
Change-Id: Iecbf6f3e7db5b3bdba41f8428254ae6a6a73e24a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168492
Commit-Queue: Alessio Bazzica <alessiob@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30514}
diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn
index 669deeb..8efc221 100644
--- a/modules/audio_coding/BUILD.gn
+++ b/modules/audio_coding/BUILD.gn
@@ -744,7 +744,8 @@
     "//third_party/abseil-cpp/absl/strings",
     "//third_party/abseil-cpp/absl/types:optional",
   ]
-  public_deps = [ ":webrtc_opus_wrapper" ]  # no-presubmit-check TODO(webrtc:8603)
+  public_deps =  # no-presubmit-check TODO(webrtc:8603)
+      [ ":webrtc_opus_wrapper" ]
 
   defines = audio_codec_defines
 
@@ -780,7 +781,8 @@
     "//third_party/abseil-cpp/absl/strings",
     "//third_party/abseil-cpp/absl/types:optional",
   ]
-  public_deps = [ ":webrtc_opus_wrapper" ]  # no-presubmit-check TODO(webrtc:8603)
+  public_deps =  # no-presubmit-check TODO(webrtc:8603)
+      [ ":webrtc_opus_wrapper" ]
 
   defines = audio_codec_defines
 
@@ -865,7 +867,8 @@
     "audio_network_adaptor/util/threshold_curve.h",
   ]
 
-  public_deps = [ ":audio_network_adaptor_config" ]  # no-presubmit-check TODO(webrtc:8603)
+  public_deps =  # no-presubmit-check TODO(webrtc:8603)
+      [ ":audio_network_adaptor_config" ]
 
   deps = [
     "../../api/audio_codecs:audio_codecs_api",
@@ -1160,7 +1163,8 @@
       "../rtp_rtcp:rtp_rtcp_format",
       "//third_party/abseil-cpp/absl/types:optional",
     ]
-    public_deps = [ "../../logging:rtc_event_log_proto" ]  # no-presubmit-check TODO(webrtc:8603)
+    public_deps =  # no-presubmit-check TODO(webrtc:8603)
+        [ "../../logging:rtc_event_log_proto" ]
   }
 
   # Only used for test purpose. Since we want to use it from chromium
@@ -1911,6 +1915,7 @@
       "codecs/isac/fix/source/filters_unittest.cc",
       "codecs/isac/fix/source/lpc_masking_model_unittest.cc",
       "codecs/isac/fix/source/transform_unittest.cc",
+      "codecs/isac/isac_webrtc_api_test.cc",
       "codecs/isac/main/source/audio_encoder_isac_unittest.cc",
       "codecs/isac/main/source/isac_unittest.cc",
       "codecs/legacy_encoded_audio_frame_unittest.cc",
@@ -1976,6 +1981,7 @@
       ":ilbc",
       ":isac",
       ":isac_c",
+      ":isac_common",
       ":isac_fix",
       ":legacy_encoded_audio_frame",
       ":mocks",
@@ -1988,10 +1994,15 @@
       ":webrtc_opus",
       "..:module_api",
       "..:module_api_public",
+      "../../api:array_view",
       "../../api/audio:audio_frame_api",
       "../../api/audio_codecs:audio_codecs_api",
       "../../api/audio_codecs:builtin_audio_decoder_factory",
       "../../api/audio_codecs:builtin_audio_encoder_factory",
+      "../../api/audio_codecs/isac:audio_decoder_isac_fix",
+      "../../api/audio_codecs/isac:audio_decoder_isac_float",
+      "../../api/audio_codecs/isac:audio_encoder_isac_fix",
+      "../../api/audio_codecs/isac:audio_encoder_isac_float",
       "../../api/audio_codecs/opus:audio_decoder_multiopus",
       "../../api/audio_codecs/opus:audio_decoder_opus",
       "../../api/audio_codecs/opus:audio_encoder_multiopus",
diff --git a/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc b/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc
new file mode 100644
index 0000000..ac83861
--- /dev/null
+++ b/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc
@@ -0,0 +1,145 @@
+/*
+ *  Copyright (c) 2020 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.
+ */
+
+#include <array>
+#include <limits>
+#include <vector>
+
+#include "api/array_view.h"
+#include "api/audio_codecs/isac/audio_decoder_isac_fix.h"
+#include "api/audio_codecs/isac/audio_decoder_isac_float.h"
+#include "api/audio_codecs/isac/audio_encoder_isac_fix.h"
+#include "api/audio_codecs/isac/audio_encoder_isac_float.h"
+#include "rtc_base/random.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+constexpr int kPayloadType = 42;
+constexpr int kBitrateBps = 20000;
+
+enum class IsacImpl { kFixed, kFloat };
+
+std::vector<int16_t> GetRandomSamplesVector(size_t size) {
+  constexpr int32_t kMin = std::numeric_limits<int16_t>::min();
+  constexpr int32_t kMax = std::numeric_limits<int16_t>::max();
+  std::vector<int16_t> v(size);
+  Random gen(/*seed=*/42);
+  for (auto& x : v) {
+    x = static_cast<int16_t>(gen.Rand(kMin, kMax));
+  }
+  return v;
+}
+
+class IsacApiTest
+    : public testing::TestWithParam<std::tuple<int, int, IsacImpl, IsacImpl>> {
+ protected:
+  IsacApiTest() : input_frame_(GetRandomSamplesVector(GetInputFrameLength())) {}
+  rtc::ArrayView<const int16_t> GetInputFrame() { return input_frame_; }
+  int GetSampleRateHz() const { return std::get<0>(GetParam()); }
+  int GetEncoderFrameLenght() const {
+    return GetEncoderFrameLenghtMs() * GetSampleRateHz() / 1000;
+  }
+  std::unique_ptr<AudioEncoder> CreateEncoder() const {
+    switch (GetEncoderIsacImpl()) {
+      case IsacImpl::kFixed: {
+        AudioEncoderIsacFix::Config config;
+        config.frame_size_ms = GetEncoderFrameLenghtMs();
+        RTC_CHECK_EQ(16000, GetSampleRateHz());
+        return AudioEncoderIsacFix::MakeAudioEncoder(config, kPayloadType);
+      }
+      case IsacImpl::kFloat: {
+        AudioEncoderIsacFloat::Config config;
+        config.bit_rate = kBitrateBps;
+        config.frame_size_ms = GetEncoderFrameLenghtMs();
+        config.sample_rate_hz = GetSampleRateHz();
+        return AudioEncoderIsacFloat::MakeAudioEncoder(config, kPayloadType);
+      }
+    }
+  }
+  std::unique_ptr<AudioDecoder> CreateDecoder() const {
+    switch (GetDecoderIsacImpl()) {
+      case IsacImpl::kFixed: {
+        webrtc::AudioDecoderIsacFix::Config config;
+        RTC_CHECK_EQ(16000, GetSampleRateHz());
+        return webrtc::AudioDecoderIsacFix::MakeAudioDecoder(config);
+      }
+      case IsacImpl::kFloat: {
+        webrtc::AudioDecoderIsacFloat::Config config;
+        config.sample_rate_hz = GetSampleRateHz();
+        return webrtc::AudioDecoderIsacFloat::MakeAudioDecoder(config);
+      }
+    }
+  }
+
+ private:
+  const std::vector<int16_t> input_frame_;
+  int GetInputFrameLength() const {
+    return rtc::CheckedDivExact(std::get<0>(GetParam()), 100);  // 10 ms.
+  }
+  int GetEncoderFrameLenghtMs() const {
+    int frame_size_ms = std::get<1>(GetParam());
+    RTC_CHECK(frame_size_ms == 30 || frame_size_ms == 60);
+    return frame_size_ms;
+  }
+  IsacImpl GetEncoderIsacImpl() const { return std::get<2>(GetParam()); }
+  IsacImpl GetDecoderIsacImpl() const { return std::get<3>(GetParam()); }
+};
+
+// Checks that the number of encoded and decoded samples match.
+TEST_P(IsacApiTest, EncodeDecode) {
+  auto encoder = CreateEncoder();
+  auto decoder = CreateDecoder();
+  const int encoder_frame_length = GetEncoderFrameLenght();
+  std::vector<int16_t> out(encoder_frame_length);
+  size_t num_encoded_samples = 0;
+  size_t num_decoded_samples = 0;
+  constexpr int kNumFrames = 12;
+  for (int i = 0; i < kNumFrames; ++i) {
+    rtc::Buffer encoded;
+    auto in = GetInputFrame();
+    encoder->Encode(/*rtp_timestamp=*/0, in, &encoded);
+    num_encoded_samples += in.size();
+    if (encoded.empty()) {
+      continue;
+    }
+    // Decode.
+    const std::vector<AudioDecoder::ParseResult> parse_result =
+        decoder->ParsePayload(std::move(encoded), /*timestamp=*/0);
+    EXPECT_EQ(parse_result.size(), size_t{1});
+    auto decode_result = parse_result[0].frame->Decode(out);
+    EXPECT_TRUE(decode_result.has_value());
+    EXPECT_EQ(out.size(), decode_result->num_decoded_samples);
+    num_decoded_samples += decode_result->num_decoded_samples;
+  }
+  EXPECT_EQ(num_encoded_samples, num_decoded_samples);
+}
+
+// Creates tests for different encoder frame lengths and different
+// encoder/decoder implementations.
+INSTANTIATE_TEST_SUITE_P(
+    AllTest,
+    IsacApiTest,
+    ::testing::ValuesIn([] {
+      std::vector<std::tuple<int, int, IsacImpl, IsacImpl>> cases;
+      for (int frame_length_ms : {30, 60}) {
+        for (IsacImpl enc : {IsacImpl::kFloat, IsacImpl::kFixed}) {
+          for (IsacImpl dec : {IsacImpl::kFloat, IsacImpl::kFixed}) {
+            cases.push_back({16000, frame_length_ms, enc, dec});
+          }
+        }
+      }
+      cases.push_back({32000, 30, IsacImpl::kFloat, IsacImpl::kFloat});
+      return cases;
+    }()));
+
+}  // namespace
+}  // namespace webrtc
diff --git a/modules/audio_coding/codecs/isac/main/include/isac.h b/modules/audio_coding/codecs/isac/main/include/isac.h
index 6bbbf8a..3d2caef 100644
--- a/modules/audio_coding/codecs/isac/main/include/isac.h
+++ b/modules/audio_coding/codecs/isac/main/include/isac.h
@@ -252,7 +252,7 @@
  *
  */
 
-int16_t WebRtcIsac_ReadFrameLen(ISACStruct* ISAC_main_inst,
+int16_t WebRtcIsac_ReadFrameLen(const ISACStruct* ISAC_main_inst,
                                 const uint8_t* encoded,
                                 int16_t* frameLength);
 
diff --git a/modules/audio_coding/codecs/isac/main/source/isac.c b/modules/audio_coding/codecs/isac/main/source/isac.c
index 552bab8..73f132c 100644
--- a/modules/audio_coding/codecs/isac/main/source/isac.c
+++ b/modules/audio_coding/codecs/isac/main/source/isac.c
@@ -1719,7 +1719,7 @@
  *        - frameLength       : Length of frame in packet (in samples)
  *
  */
-int16_t WebRtcIsac_ReadFrameLen(ISACStruct* ISAC_main_inst,
+int16_t WebRtcIsac_ReadFrameLen(const ISACStruct* ISAC_main_inst,
                                 const uint8_t* encoded,
                                 int16_t* frameLength) {
   Bitstr streamdata;