blob: c2b7ffbf642786acd2798f73b8a2ed26a07ffbaf [file] [log] [blame]
Richard Burton00553462021-11-10 16:27:14 +00001/*
2 * Copyright (c) 2021 Arm Limited. All rights reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17#include "RNNoiseModel.hpp"
18#include "UseCaseHandler.hpp"
19#include "InputFiles.hpp"
20#include "RNNUCTestCaseData.hpp"
21#include "UseCaseCommonUtils.hpp"
22
23#include <catch.hpp>
24#include <hal.h>
25#include <Profiler.hpp>
Richard Burton033c9152021-12-07 14:04:44 +000026
Richard Burton00553462021-11-10 16:27:14 +000027#define PLATFORM \
28hal_platform platform; \
29data_acq_module data_acq; \
30data_psn_module data_psn; \
31platform_timer timer; \
32hal_init(&platform, &data_acq, &data_psn, &timer); \
33hal_platform_init(&platform);
34
35#define CONTEXT \
36arm::app::ApplicationContext caseContext; \
37arm::app::Profiler profiler{&platform, "noise_reduction"}; \
38caseContext.Set<arm::app::Profiler&>("profiler", profiler); \
39caseContext.Set<hal_platform&>("platform", platform); \
40caseContext.Set<arm::app::RNNoiseModel&>("model", model);
41
42TEST_CASE("Verify output tensor memory dump")
43{
44 constexpr size_t maxMemDumpSz = 0x100000; /* 1 MiB worth of space */
45 std::vector<uint8_t> memPool(maxMemDumpSz); /* Memory pool */
46 arm::app::RNNoiseModel model{};
47
48 REQUIRE(model.Init());
49 REQUIRE(model.IsInited());
50
51 /* Populate the output tensors */
52 const size_t numOutputs = model.GetNumOutputs();
53 size_t sizeToWrite = 0;
54 size_t lastTensorSize = model.GetOutputTensor(numOutputs - 1)->bytes;
55
56 for (size_t i = 0; i < numOutputs; ++i) {
57 TfLiteTensor* tensor = model.GetOutputTensor(i);
58 auto* tData = tflite::GetTensorData<uint8_t>(tensor);
59
60 if (tensor->bytes > 0) {
61 memset(tData, static_cast<uint8_t>(i), tensor->bytes);
62 sizeToWrite += tensor->bytes;
63 }
64 }
65
66
67 SECTION("Positive use case")
68 {
69 /* Run the memory dump */
70 auto bytesWritten = DumpOutputTensorsToMemory(model, memPool.data(), memPool.size());
71 REQUIRE(sizeToWrite == bytesWritten);
72
73 /* Verify the dump */
74 size_t k = 0;
75 for (size_t i = 0; i < numOutputs && k < memPool.size(); ++i) {
76 TfLiteTensor* tensor = model.GetOutputTensor(i);
77 auto* tData = tflite::GetTensorData<uint8_t>(tensor);
78
79 for (size_t j = 0; j < tensor->bytes && k < memPool.size(); ++j) {
80 REQUIRE(tData[j] == memPool[k++]);
81 }
82 }
83 }
84
85 SECTION("Limited memory - skipping last tensor")
86 {
87 /* Run the memory dump */
88 auto bytesWritten = DumpOutputTensorsToMemory(model, memPool.data(), sizeToWrite - 1);
89 REQUIRE(lastTensorSize > 0);
90 REQUIRE(bytesWritten == sizeToWrite - lastTensorSize);
91 }
92
93 SECTION("Zero memory")
94 {
95 /* Run the memory dump */
96 auto bytesWritten = DumpOutputTensorsToMemory(model, memPool.data(), 0);
97 REQUIRE(bytesWritten == 0);
98 }
99}
100
101TEST_CASE("Inference run all clips", "[RNNoise]")
102{
103 PLATFORM
104
105 arm::app::RNNoiseModel model;
106
107 CONTEXT
108
109 caseContext.Set<uint32_t>("clipIndex", 0);
110 caseContext.Set<uint32_t>("numInputFeatures", g_NumInputFeatures);
111 caseContext.Set<uint32_t>("frameLength", g_FrameLength);
112 caseContext.Set<uint32_t>("frameStride", g_FrameStride);
113
114 /* Load the model. */
115 REQUIRE(model.Init());
116
117 REQUIRE(arm::app::NoiseReductionHandler(caseContext, true));
118}
119
120std::function<uint32_t(const uint32_t)> get_golden_input_p232_208_array_size(const uint32_t numberOfFeatures) {
121
122 return [numberOfFeatures](const uint32_t) -> uint32_t{
123 return numberOfFeatures;
124 };
125}
126
127const char* get_test_filename(const uint32_t idx) {
128 auto name = get_filename(idx);
129 REQUIRE(std::string("p232_208.wav") == name);
130 return "p232_208.wav";
131}
132
133void testInfByIndex(std::vector<uint32_t>& numberOfInferences) {
134 PLATFORM
135
136 arm::app::RNNoiseModel model;
137
138 CONTEXT
139
140 caseContext.Set<std::function<const int16_t*(const uint32_t)>>("features", get_audio_array);
141 caseContext.Set<std::function<const char* (const uint32_t)>>("featureFileNames", get_test_filename);
142 caseContext.Set<uint32_t>("frameLength", g_FrameLength);
143 caseContext.Set<uint32_t>("frameStride", g_FrameStride);
144 caseContext.Set<uint32_t>("numInputFeatures", g_NumInputFeatures);
145 /* Load the model. */
146 REQUIRE(model.Init());
147
148 size_t oneInferenceOutSizeBytes = g_FrameLength * sizeof(int16_t);
149
150 auto infIndex = 0;
151 for (auto numInf: numberOfInferences) {
152 DYNAMIC_SECTION("Number of features: "<< numInf) {
153 caseContext.Set<uint32_t>("clipIndex", 1); /* Only getting p232_208.wav for tests. */
154 uint32_t audioSizeInput = numInf*g_FrameLength;
155 caseContext.Set<std::function<uint32_t(const uint32_t)>>("featureSizes",
156 get_golden_input_p232_208_array_size(audioSizeInput));
157
158 size_t headerNumBytes = 4 + 12 + 4; /* Filename length, filename (12 for p232_208.wav), dump size. */
159 size_t footerNumBytes = 4; /* Eof value. */
160 size_t memDumpMaxLenBytes = headerNumBytes + footerNumBytes + oneInferenceOutSizeBytes * numInf;
161
162 std::vector<uint8_t > memDump(memDumpMaxLenBytes);
163 size_t undefMemDumpBytesWritten = 0;
164 caseContext.Set<size_t>("MEM_DUMP_LEN", memDumpMaxLenBytes);
165 caseContext.Set<uint8_t*>("MEM_DUMP_BASE_ADDR", memDump.data());
166 caseContext.Set<size_t*>("MEM_DUMP_BYTE_WRITTEN", &undefMemDumpBytesWritten);
167
168 /* Inference. */
169 REQUIRE(arm::app::NoiseReductionHandler(caseContext, false));
170
171 /* The expected output after post-processing. */
172 std::vector<int16_t> golden(&ofms[infIndex][0], &ofms[infIndex][0] + g_FrameLength);
173
174 size_t startOfLastInfOut = undefMemDumpBytesWritten - oneInferenceOutSizeBytes;
175
176 /* The actual result from the usecase handler. */
177 std::vector<int16_t> runtime(g_FrameLength);
178 std::memcpy(runtime.data(), &memDump[startOfLastInfOut], oneInferenceOutSizeBytes);
179
Richard Burton033c9152021-12-07 14:04:44 +0000180 /* Margin of 43 is 0.07% error. */
181 REQUIRE_THAT(golden, Catch::Matchers::Approx(runtime).margin(43));
Richard Burton00553462021-11-10 16:27:14 +0000182 }
183 ++infIndex;
184 }
185}
186
187TEST_CASE("Inference by index - one inference", "[RNNoise]")
188{
189 auto totalAudioSize = get_audio_array_size(1);
190 REQUIRE(64757 == totalAudioSize); /* Checking that the input file is as expected and has not changed. */
191
192 /* Run 1 inference */
193 std::vector<uint32_t> numberOfInferences = {1};
194 testInfByIndex(numberOfInferences);
195}
196
197TEST_CASE("Inference by index - several inferences", "[RNNoise]")
198{
199 auto totalAudioSize = get_audio_array_size(1);
200 REQUIRE(64757 == totalAudioSize); /* Checking that the input file is as expected and has not changed. */
201
202 /* 3 different inference amounts: 1, 2 and all inferences required to cover total feature set */
203 uint32_t totalInferences = totalAudioSize / g_FrameLength;
204 std::vector<uint32_t> numberOfInferences = {1, 2, totalInferences};
205 testInfByIndex(numberOfInferences);
206}