Standalone encoder and decoder for Soundstream

- Extract setup code from the existing soundstream application into a
  library target.
- Rename the existing soundstream application to soundstream_e2e.
- Encoder and decoder applications for Soundstream, which run a single
  invocation of their model, and populate the `output_header` section so
  that the results can be retrieved.
- Enable c++17 to use std::optional
- Remove the TF_LITE_STATIC_MEMORY define (as we are using the heap in
  the soundstream library now)
- Fix the definition of operator delete so the linker resolves it
  properly
- Add a definition of operator new (which calls malloc)

Change-Id: I446f135825f77744bbe561105b5e156c51b3bc0c
diff --git a/.bazelrc b/.bazelrc
index 5dd1c3b..8c3f5e9 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,6 +1,6 @@
-build --action_env=BAZEL_CXXOPTS="-std=gnu++14"
-build --cxxopt='-std=gnu++14'
-build --conlyopt='-std=gnu11'
+build --action_env=BAZEL_CXXOPTS="-std=c++17"
+build --cxxopt='-std=c++17'
+build --conlyopt='-std=c11'
 
 # Enable toolchain resolution with cc
 build --incompatible_enable_cc_toolchain_resolution
@@ -15,5 +15,4 @@
 build --copt=-DTF_LITE_USE_GLOBAL_MIN
 build --copt=-DTF_LITE_USE_GLOBAL_MAX
 build --copt=-DTF_LITE_MCU_DEBUG_LOG
-build --copt=-DTF_LITE_STATIC_MEMORY
 build:opt --copt=-DTF_LITE_STRIP_ERROR_STRINGS
diff --git a/crt/kelvin_gloss.cc b/crt/kelvin_gloss.cc
index 9d13823..07e8b8d 100644
--- a/crt/kelvin_gloss.cc
+++ b/crt/kelvin_gloss.cc
@@ -136,8 +136,8 @@
   return reinterpret_cast<void*>(prev_heap_end);
 }
 
+void* operator new(size_t n) { return malloc(n); }
+
 void operator delete(void* p) noexcept { free(p); }
 
-extern "C" void operator delete(void* p, uint32_t c) noexcept {
-  operator delete(p);
-}
+void operator delete(void* p, size_t c) noexcept { operator delete(p); }
diff --git a/examples/hello_world/hello_world.c b/examples/hello_world/hello_world.c
index e47bd5e..aa96624 100644
--- a/examples/hello_world/hello_world.c
+++ b/examples/hello_world/hello_world.c
@@ -46,7 +46,5 @@
   output = data[kDataSize - 1];
   output_header.output_ptr = (uint32_t)&output;
 
-  // invalidate all cache.
-  asm volatile("flushall");
   return 0;
 }
diff --git a/examples/tflm/soundstream/BUILD b/examples/tflm/soundstream/BUILD
index 0b79a83..b3a41f3 100644
--- a/examples/tflm/soundstream/BUILD
+++ b/examples/tflm/soundstream/BUILD
@@ -2,25 +2,70 @@
 
 package(default_visibility = ["//visibility:public"])
 
-kelvin_binary(
+cc_library(
     name = "soundstream",
     srcs = [
+        "decoder.cc",
+        "encoder.cc",
+        "decoder_non_stream_q16x8_b64_io_int16_tflite.cc",
+        "encoder_non_stream_q16x8_b64_io_int16_tflite.cc",
+        "decoder_non_stream_q16x8_b64_io_int16_tflite.h",
+        "encoder_non_stream_q16x8_b64_io_int16_tflite.h",
+    ],
+    hdrs = [
+        "decoder.h",
+        "encoder.h",
+    ],
+    tags = ["manual"],
+    deps = [
+        "@tflite-micro//tensorflow/lite/micro:micro_framework",
+    ],
+)
+
+kelvin_binary(
+    name = "soundstream_decoder",
+    srcs = [
+        "soundstream_decoder.cc",
+    ],
+    tags = ["manual"],
+    deps = [
+        ":soundstream",
+        "//crt:crt_header",
+        "@tflite-micro//tensorflow/lite/micro:micro_framework",
+        "@tflite-micro//tensorflow/lite/micro:system_setup",
+    ],
+)
+
+kelvin_binary(
+    name = "soundstream_encoder",
+    srcs = [
+        "soundstream_encoder.cc",
+    ],
+    tags = ["manual"],
+    deps = [
+        ":soundstream",
+        "//crt:crt_header",
+        "@tflite-micro//tensorflow/lite/micro:micro_framework",
+        "@tflite-micro//tensorflow/lite/micro:system_setup",
+    ],
+)
+
+kelvin_binary(
+    name = "soundstream_e2e",
+    srcs = [
         "best_of_times_s16_decoded.cc",
         "best_of_times_s16_encoded.cc",
         "best_of_times_s16_wav.cc",
-        "decoder_non_stream_q16x8_b64_io_int16_tflite.cc",
-        "encoder_non_stream_q16x8_b64_io_int16_tflite.cc",
-        "soundstream.cc",
+        "soundstream_e2e.cc",
     ],
     hdrs = [
         "best_of_times_s16_decoded.h",
         "best_of_times_s16_encoded.h",
         "best_of_times_s16_wav.h",
-        "decoder_non_stream_q16x8_b64_io_int16_tflite.h",
-        "encoder_non_stream_q16x8_b64_io_int16_tflite.h",
     ],
     tags = ["manual"],
     deps = [
+        ":soundstream",
         "//crt:crt_header",
         "@tflite-micro//tensorflow/lite/micro:micro_framework",
         "@tflite-micro//tensorflow/lite/micro:system_setup",
diff --git a/examples/tflm/soundstream/decoder.cc b/examples/tflm/soundstream/decoder.cc
new file mode 100644
index 0000000..2a0fc4c
--- /dev/null
+++ b/examples/tflm/soundstream/decoder.cc
@@ -0,0 +1,37 @@
+#include "examples/tflm/soundstream/decoder.h"
+
+#include "examples/tflm/soundstream/decoder_non_stream_q16x8_b64_io_int16_tflite.h"
+
+namespace kelvin::soundstream::decoder {
+std::optional<Decoder> Setup(uint8_t* tensor_arena) {
+  auto* model =
+      tflite::GetModel(g__decoder_non_stream_q16x8_b64_io_int16_model_data);
+  if (model->version() != TFLITE_SCHEMA_VERSION) {
+    return {};
+  }
+
+  Decoder d;
+  d.resolver = std::make_unique<tflite::MicroMutableOpResolver<11>>();
+  d.resolver->AddReshape();
+  d.resolver->AddPad();
+  d.resolver->AddConv2D();
+  d.resolver->AddLeakyRelu();
+  d.resolver->AddSplit();
+  d.resolver->AddTransposeConv();
+  d.resolver->AddStridedSlice();
+  d.resolver->AddConcatenation();
+  d.resolver->AddDepthwiseConv2D();
+  d.resolver->AddAdd();
+  d.resolver->AddQuantize();
+
+  d.interpreter = std::make_unique<tflite::MicroInterpreter>(
+      model, *d.resolver, tensor_arena, kTensorArenaSizeBytes);
+
+  TfLiteStatus allocate_status = d.interpreter->AllocateTensors();
+  if (allocate_status != kTfLiteOk) {
+    MicroPrintf("Failed to allocate decoder's tensors");
+    return {};
+  }
+  return d;
+}
+}  // namespace kelvin::soundstream::decoder
diff --git a/examples/tflm/soundstream/decoder.h b/examples/tflm/soundstream/decoder.h
new file mode 100644
index 0000000..b21c93d
--- /dev/null
+++ b/examples/tflm/soundstream/decoder.h
@@ -0,0 +1,20 @@
+#ifndef EXAMPLES_TFLM_SOUNDSTREAM_DECODER_H_
+#define EXAMPLES_TFLM_SOUNDSTREAM_DECODER_H_
+
+#include <cstddef>
+#include <memory>
+#include <optional>
+
+#include "tensorflow/lite/micro/micro_interpreter.h"
+#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
+
+namespace kelvin::soundstream::decoder {
+constexpr size_t kTensorArenaSizeBytes = 96 * 1024;
+struct Decoder {
+  std::unique_ptr<tflite::MicroInterpreter> interpreter;
+  std::unique_ptr<tflite::MicroMutableOpResolver<11>> resolver;
+};
+std::optional<Decoder> Setup(uint8_t* tensor_arena);
+}  // namespace kelvin::soundstream::decoder
+
+#endif  // EXAMPLES_TFLM_SOUNDSTREAM_DECODER_H_
diff --git a/examples/tflm/soundstream/encoder.cc b/examples/tflm/soundstream/encoder.cc
new file mode 100644
index 0000000..d31d517
--- /dev/null
+++ b/examples/tflm/soundstream/encoder.cc
@@ -0,0 +1,32 @@
+#include "examples/tflm/soundstream/encoder.h"
+
+#include "examples/tflm/soundstream/encoder_non_stream_q16x8_b64_io_int16_tflite.h"
+
+namespace kelvin::soundstream::encoder {
+std::optional<Encoder> Setup(uint8_t* tensor_arena) {
+  auto* model =
+      tflite::GetModel(g__encoder_non_stream_q16x8_b64_io_int16_model_data);
+  if (model->version() != TFLITE_SCHEMA_VERSION) {
+    return {};
+  }
+
+  Encoder e;
+  e.resolver = std::make_unique<tflite::MicroMutableOpResolver<6>>();
+  e.resolver->AddReshape();
+  e.resolver->AddPad();
+  e.resolver->AddConv2D();
+  e.resolver->AddLeakyRelu();
+  e.resolver->AddDepthwiseConv2D();
+  e.resolver->AddAdd();
+
+  e.interpreter = std::make_unique<tflite::MicroInterpreter>(
+      model, *e.resolver, tensor_arena, kTensorArenaSizeBytes);
+
+  TfLiteStatus allocate_status = e.interpreter->AllocateTensors();
+  if (allocate_status != kTfLiteOk) {
+    MicroPrintf("Failed to allocate encoder's tensors");
+    return {};
+  }
+  return e;
+}
+}  // namespace kelvin::soundstream::encoder
diff --git a/examples/tflm/soundstream/encoder.h b/examples/tflm/soundstream/encoder.h
new file mode 100644
index 0000000..3cb74f9
--- /dev/null
+++ b/examples/tflm/soundstream/encoder.h
@@ -0,0 +1,20 @@
+#ifndef EXAMPLES_TFLM_SOUNDSTREAM_ENCODER_H_
+#define EXAMPLES_TFLM_SOUNDSTREAM_ENCODER_H_
+
+#include <cstddef>
+#include <memory>
+#include <optional>
+
+#include "tensorflow/lite/micro/micro_interpreter.h"
+#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
+
+namespace kelvin::soundstream::encoder {
+constexpr size_t kTensorArenaSizeBytes = 96 * 1024;
+struct Encoder {
+  std::unique_ptr<tflite::MicroInterpreter> interpreter;
+  std::unique_ptr<tflite::MicroMutableOpResolver<6>> resolver;
+};
+std::optional<Encoder> Setup(uint8_t* tensor_arena);
+}  // namespace kelvin::soundstream::encoder
+
+#endif  // EXAMPLES_TFLM_SOUNDSTREAM_ENCODER_H_
diff --git a/examples/tflm/soundstream/soundstream.cc b/examples/tflm/soundstream/soundstream.cc
deleted file mode 100644
index 7f500a9..0000000
--- a/examples/tflm/soundstream/soundstream.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2023 Google LLC
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-
-#include "examples/tflm/soundstream/best_of_times_s16_decoded.h"
-#include "examples/tflm/soundstream/best_of_times_s16_encoded.h"
-#include "examples/tflm/soundstream/best_of_times_s16_wav.h"
-#include "examples/tflm/soundstream/decoder_non_stream_q16x8_b64_io_int16_tflite.h"
-#include "examples/tflm/soundstream/encoder_non_stream_q16x8_b64_io_int16_tflite.h"
-#include "tensorflow/lite/micro/micro_interpreter.h"
-#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
-
-namespace {
-const tflite::Model *encoder_model = nullptr;
-const tflite::Model *decoder_model = nullptr;
-tflite::MicroInterpreter *encoder_interpreter = nullptr;
-tflite::MicroInterpreter *decoder_interpreter = nullptr;
-constexpr int kTensorArenaSize =
-    96 * 1024;
-uint8_t encoder_tensor_arena[kTensorArenaSize] __attribute__((aligned(16)));
-uint8_t decoder_tensor_arena[kTensorArenaSize] __attribute__((aligned(16)));
-}  // namespace
-
-int main(int argc, char **argv) {
-  encoder_model =
-      tflite::GetModel(g__encoder_non_stream_q16x8_b64_io_int16_model_data);
-  if (encoder_model->version() != TFLITE_SCHEMA_VERSION) {
-    return 1;
-  }
-  decoder_model =
-      tflite::GetModel(g__decoder_non_stream_q16x8_b64_io_int16_model_data);
-  if (decoder_model->version() != TFLITE_SCHEMA_VERSION) {
-    return 1;
-  }
-
-  static tflite::MicroMutableOpResolver<6> encoder_resolver{};
-  encoder_resolver.AddReshape();
-  encoder_resolver.AddPad();
-  encoder_resolver.AddConv2D();
-  encoder_resolver.AddLeakyRelu();
-  encoder_resolver.AddDepthwiseConv2D();
-  encoder_resolver.AddAdd();
-
-  static tflite::MicroMutableOpResolver<11> decoder_resolver{};
-  decoder_resolver.AddReshape();
-  decoder_resolver.AddPad();
-  decoder_resolver.AddConv2D();
-  decoder_resolver.AddLeakyRelu();
-  decoder_resolver.AddSplit();
-  decoder_resolver.AddTransposeConv();
-  decoder_resolver.AddStridedSlice();
-  decoder_resolver.AddConcatenation();
-  decoder_resolver.AddDepthwiseConv2D();
-  decoder_resolver.AddAdd();
-  decoder_resolver.AddQuantize();
-
-  static tflite::MicroInterpreter encoder_static_interpreter(
-      encoder_model, encoder_resolver, encoder_tensor_arena, kTensorArenaSize);
-  encoder_interpreter = &encoder_static_interpreter;
-
-  static tflite::MicroInterpreter decoder_static_interpreter(
-      decoder_model, decoder_resolver, decoder_tensor_arena, kTensorArenaSize);
-  decoder_interpreter = &decoder_static_interpreter;
-
-  TfLiteStatus allocate_status = encoder_interpreter->AllocateTensors();
-  if (allocate_status != kTfLiteOk) {
-    MicroPrintf("Failed to allocate encoder's tensors");
-    return -1;
-  }
-  allocate_status = decoder_interpreter->AllocateTensors();
-  if (allocate_status != kTfLiteOk) {
-    MicroPrintf("Failed to allocate decoder's tensors");
-    return -1;
-  }
-
-  TfLiteTensor *encoder_input = encoder_interpreter->input(0);
-  TfLiteTensor *encoder_output = encoder_interpreter->output(0);
-  TfLiteTensor *decoder_input = decoder_interpreter->input(0);
-  TfLiteTensor *decoder_output = decoder_interpreter->output(0);
-
-  int invocation_count =
-      (g_best_of_times_s16_audio_data_size * sizeof(int16_t)) /
-      encoder_input->bytes;
-  for (int i = 0; i < invocation_count; ++i) {
-    MicroPrintf("Invocation %d of %d", i, invocation_count);
-    memcpy(encoder_input->data.uint8,
-           g_best_of_times_s16_audio_data +
-               ((i * encoder_input->bytes) / sizeof(int16_t)),
-           encoder_input->bytes);
-    TfLiteStatus invoke_status = encoder_interpreter->Invoke();
-    if (invoke_status != kTfLiteOk) {
-      MicroPrintf("Failed to invoke encoder");
-      return -1;
-    }
-    if (memcmp(encoder_output->data.uint8,
-               g_best_of_times_s16_encoded + (i * encoder_output->bytes),
-               encoder_output->bytes)) {
-      MicroPrintf("Encoder output mismatches reference");
-      return -1;
-    }
-
-    memcpy(decoder_input->data.uint8, encoder_output->data.uint8,
-           decoder_input->bytes);
-    invoke_status = decoder_interpreter->Invoke();
-    if (invoke_status != kTfLiteOk) {
-      MicroPrintf("Failed to invoke decoder");
-      return -1;
-    }
-    if (memcmp(decoder_output->data.uint8,
-               g_best_of_times_s16_decoded + (i * decoder_output->bytes),
-               decoder_output->bytes)) {
-      MicroPrintf("Decoder output mismatches reference");
-      return -1;
-    }
-  }
-
-  return 0;
-}
diff --git a/examples/tflm/soundstream/soundstream_decoder.cc b/examples/tflm/soundstream/soundstream_decoder.cc
new file mode 100644
index 0000000..77f0f94
--- /dev/null
+++ b/examples/tflm/soundstream/soundstream_decoder.cc
@@ -0,0 +1,45 @@
+// Copyright 2023 Google LLC
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "examples/tflm/soundstream/decoder.h"
+
+typedef struct {
+  uint32_t return_code;  // Populated in kelvin_start
+  uint32_t output_ptr;
+  uint32_t length;
+} OutputHeader;
+
+__attribute__((section(".model_output_header"))) OutputHeader output_header = {
+    .output_ptr = 0,
+    .length = 0,
+};
+
+namespace {
+uint8_t
+    decoder_tensor_arena[kelvin::soundstream::decoder::kTensorArenaSizeBytes]
+    __attribute__((aligned(64)));
+}  // namespace
+
+int main(int argc, char **argv) {
+  auto decoder = kelvin::soundstream::decoder::Setup(decoder_tensor_arena);
+  if (!decoder) {
+    MicroPrintf("Unable to construct decoder");
+    return -1;
+  }
+
+  TfLiteTensor *decoder_input = decoder->interpreter->input(0);
+  TfLiteTensor *decoder_output = decoder->interpreter->output(0);
+
+  memset(decoder_input->data.uint8, 0, decoder_input->bytes);
+  TfLiteStatus invoke_status = decoder->interpreter->Invoke();
+  if (invoke_status != kTfLiteOk) {
+    MicroPrintf("Failed to invoke decoder");
+    return -1;
+  }
+
+  output_header.length = decoder_output->bytes;
+  output_header.output_ptr =
+      reinterpret_cast<uint32_t>(decoder_output->data.uint8);
+  return 0;
+}
diff --git a/examples/tflm/soundstream/soundstream_e2e.cc b/examples/tflm/soundstream/soundstream_e2e.cc
new file mode 100644
index 0000000..4c43060
--- /dev/null
+++ b/examples/tflm/soundstream/soundstream_e2e.cc
@@ -0,0 +1,75 @@
+// Copyright 2023 Google LLC
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "examples/tflm/soundstream/best_of_times_s16_decoded.h"
+#include "examples/tflm/soundstream/best_of_times_s16_encoded.h"
+#include "examples/tflm/soundstream/best_of_times_s16_wav.h"
+#include "examples/tflm/soundstream/decoder.h"
+#include "examples/tflm/soundstream/encoder.h"
+
+namespace {
+uint8_t
+    encoder_tensor_arena[kelvin::soundstream::encoder::kTensorArenaSizeBytes]
+    __attribute__((aligned(64)));
+uint8_t
+    decoder_tensor_arena[kelvin::soundstream::decoder::kTensorArenaSizeBytes]
+    __attribute__((aligned(64)));
+}  // namespace
+
+int main(int argc, char **argv) {
+  auto encoder = kelvin::soundstream::encoder::Setup(encoder_tensor_arena);
+  if (!encoder) {
+    MicroPrintf("Unable to construct encoder");
+    return -1;
+  }
+
+  auto decoder = kelvin::soundstream::decoder::Setup(decoder_tensor_arena);
+  if (!decoder) {
+    MicroPrintf("Unable to construct decoder");
+    return -1;
+  }
+
+  TfLiteTensor *encoder_input = encoder->interpreter->input(0);
+  TfLiteTensor *encoder_output = encoder->interpreter->output(0);
+  TfLiteTensor *decoder_input = decoder->interpreter->input(0);
+  TfLiteTensor *decoder_output = decoder->interpreter->output(0);
+
+  int invocation_count =
+      (g_best_of_times_s16_audio_data_size * sizeof(int16_t)) /
+      encoder_input->bytes;
+  for (int i = 0; i < invocation_count; ++i) {
+    MicroPrintf("Invocation %d of %d", i, invocation_count);
+    memcpy(encoder_input->data.uint8,
+           g_best_of_times_s16_audio_data +
+               ((i * encoder_input->bytes) / sizeof(int16_t)),
+           encoder_input->bytes);
+    TfLiteStatus invoke_status = encoder->interpreter->Invoke();
+    if (invoke_status != kTfLiteOk) {
+      MicroPrintf("Failed to invoke encoder");
+      return -1;
+    }
+    if (memcmp(encoder_output->data.uint8,
+               g_best_of_times_s16_encoded + (i * encoder_output->bytes),
+               encoder_output->bytes)) {
+      MicroPrintf("Encoder output mismatches reference");
+      return -1;
+    }
+
+    memcpy(decoder_input->data.uint8, encoder_output->data.uint8,
+           decoder_input->bytes);
+    invoke_status = decoder->interpreter->Invoke();
+    if (invoke_status != kTfLiteOk) {
+      MicroPrintf("Failed to invoke decoder");
+      return -1;
+    }
+    if (memcmp(decoder_output->data.uint8,
+               g_best_of_times_s16_decoded + (i * decoder_output->bytes),
+               decoder_output->bytes)) {
+      MicroPrintf("Decoder output mismatches reference");
+      return -1;
+    }
+  }
+
+  return 0;
+}
diff --git a/examples/tflm/soundstream/soundstream_encoder.cc b/examples/tflm/soundstream/soundstream_encoder.cc
new file mode 100644
index 0000000..bc7fc25
--- /dev/null
+++ b/examples/tflm/soundstream/soundstream_encoder.cc
@@ -0,0 +1,45 @@
+// Copyright 2023 Google LLC
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "examples/tflm/soundstream/encoder.h"
+
+typedef struct {
+  uint32_t return_code;  // Populated in kelvin_start
+  uint32_t output_ptr;
+  uint32_t length;
+} OutputHeader;
+
+__attribute__((section(".model_output_header"))) OutputHeader output_header = {
+    .output_ptr = 0,
+    .length = 0,
+};
+
+namespace {
+uint8_t
+    encoder_tensor_arena[kelvin::soundstream::encoder::kTensorArenaSizeBytes]
+    __attribute__((aligned(64)));
+}  // namespace
+
+int main(int argc, char **argv) {
+  auto encoder = kelvin::soundstream::encoder::Setup(encoder_tensor_arena);
+  if (!encoder) {
+    MicroPrintf("Unable to construct encoder");
+    return -1;
+  }
+
+  TfLiteTensor *encoder_input = encoder->interpreter->input(0);
+  TfLiteTensor *encoder_output = encoder->interpreter->output(0);
+
+  memset(encoder_input->data.uint8, 0, encoder_input->bytes);
+  TfLiteStatus invoke_status = encoder->interpreter->Invoke();
+  if (invoke_status != kTfLiteOk) {
+    MicroPrintf("Failed to invoke encoder");
+    return -1;
+  }
+
+  output_header.length = encoder_output->bytes;
+  output_header.output_ptr =
+      reinterpret_cast<uint32_t>(encoder_output->data.uint8);
+  return 0;
+}