MLECO-2079 Adding the C++ KWS example

Signed-off-by: Eanna O Cathain <eanna.ocathain@arm.com>
Change-Id: I81899bbfaada32f478c2e2fc6441eabb94d8d0fc
diff --git a/samples/KeywordSpotting/src/Main.cpp b/samples/KeywordSpotting/src/Main.cpp
new file mode 100644
index 0000000..10efcd8
--- /dev/null
+++ b/samples/KeywordSpotting/src/Main.cpp
@@ -0,0 +1,128 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#include <iostream>
+#include <map>
+#include <vector>
+#include <algorithm>
+#include <cmath>
+#include "KeywordSpottingPipeline.hpp"
+#include "CmdArgsParser.hpp"
+#include "ArmnnNetworkExecutor.hpp"
+#include "AudioCapture.hpp"
+
+const std::string AUDIO_FILE_PATH = "--audio-file-path";
+const std::string MODEL_FILE_PATH = "--model-file-path";
+const std::string LABEL_PATH = "--label-path";
+const std::string PREFERRED_BACKENDS = "--preferred-backends";
+const std::string HELP = "--help";
+
+/*
+ * The accepted options for this Speech Recognition executable
+ */
+static std::map<std::string, std::string> CMD_OPTIONS = 
+{
+        {AUDIO_FILE_PATH,    "[REQUIRED] Path to the Audio file to run speech recognition on"},
+        {MODEL_FILE_PATH,    "[REQUIRED] Path to the Speech Recognition model to use"},
+        {PREFERRED_BACKENDS, "[OPTIONAL] Takes the preferred backends in preference order, separated by comma."
+                             " For example: CpuAcc,GpuAcc,CpuRef. Accepted options: [CpuAcc, CpuRef, GpuAcc]."
+                             " Defaults to CpuAcc,CpuRef"}
+};
+
+/*
+ * Reads the user supplied backend preference, splits it by comma, and returns an ordered vector
+ */
+std::vector<armnn::BackendId> GetPreferredBackendList(const std::string& preferredBackends) 
+{
+    std::vector<armnn::BackendId> backends;
+    std::stringstream ss(preferredBackends);
+
+    while (ss.good()) 
+    {
+        std::string backend;
+        std::getline(ss, backend, ',');
+        backends.emplace_back(backend);
+    }
+    return backends;
+}
+
+//Labels for this model
+std::map<int, std::string> labels = 
+{
+        {0,  "silence"},
+        {1,  "unknown"},
+        {2,  "yes"},
+        {3,  "no"},
+        {4,  "up"},
+        {5,  "down"},
+        {6,  "left"},
+        {7,  "right"},
+        {8,  "on"},
+        {9,  "off"},
+        {10, "stop"},
+        {11, "go"}
+};
+
+
+int main(int argc, char* argv[]) 
+{
+    printf("ArmNN major version: %d\n", ARMNN_MAJOR_VERSION);
+    std::map<std::string, std::string> options;
+
+    //Read command line args
+    int result = ParseOptions(options, CMD_OPTIONS, argv, argc);
+    if (result != 0) 
+    {
+        return result;
+    }
+
+    // Create the ArmNN inference runner
+    common::PipelineOptions pipelineOptions;
+    pipelineOptions.m_ModelName = "DS_CNN_CLUSTERED_INT8";
+    pipelineOptions.m_ModelFilePath = GetSpecifiedOption(options, MODEL_FILE_PATH);
+    if (CheckOptionSpecified(options, PREFERRED_BACKENDS)) 
+    {
+        pipelineOptions.m_backends = GetPreferredBackendList(
+            (GetSpecifiedOption(options, PREFERRED_BACKENDS)));
+    } 
+    else 
+    {
+        pipelineOptions.m_backends = {"CpuAcc", "CpuRef"};
+    }
+
+    kws::IPipelinePtr kwsPipeline = kws::CreatePipeline(pipelineOptions);
+
+    //Extract audio data from sound file
+    auto filePath = GetSpecifiedOption(options, AUDIO_FILE_PATH);
+    std::vector<float> audioData = audio::AudioCapture::LoadAudioFile(filePath);
+
+    audio::AudioCapture capture;
+    //todo: read samples and stride from pipeline
+    capture.InitSlidingWindow(audioData.data(), 
+                              audioData.size(), 
+                              kwsPipeline->getInputSamplesSize(), 
+                              kwsPipeline->getInputSamplesSize()/2);
+
+    //Loop through audio data buffer
+    while (capture.HasNext()) 
+    {
+        std::vector<float> audioBlock = capture.Next();
+        common::InferenceResults<int8_t> results;
+
+        //Prepare input tensors
+        std::vector<int8_t> preprocessedData = kwsPipeline->PreProcessing(audioBlock);
+        //Run inference
+        kwsPipeline->Inference(preprocessedData, results);
+        //Decode output
+        kwsPipeline->PostProcessing(results, labels,
+                                    [](int index, std::string& label, float prob) -> void {
+                                        printf("Keyword \"%s\", index %d:, probability %f\n",
+                                               label.c_str(),
+                                               index,
+                                               prob);
+                                    });
+    }
+
+    return 0;
+}
\ No newline at end of file