blob: 792b4603b21dae3a7af1218cf18b5c1a458ae5ab [file] [log] [blame]
Richard Burton00553462021-11-10 16:27:14 +00001/*
Richard Burtoned35a6f2022-02-14 11:55:35 +00002 * Copyright (c) 2021-2022 Arm Limited. All rights reserved.
Richard Burton00553462021-11-10 16:27:14 +00003 * 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 */
Richard Burton00553462021-11-10 16:27:14 +000017#include "hal.h"
alexander31ae9f02022-02-10 16:15:54 +000018#include "UseCaseHandler.hpp"
Richard Burton00553462021-11-10 16:27:14 +000019#include "UseCaseCommonUtils.hpp"
20#include "AudioUtils.hpp"
Richard Burtoned35a6f2022-02-14 11:55:35 +000021#include "ImageUtils.hpp"
Richard Burton00553462021-11-10 16:27:14 +000022#include "InputFiles.hpp"
23#include "RNNoiseModel.hpp"
24#include "RNNoiseProcess.hpp"
alexander31ae9f02022-02-10 16:15:54 +000025#include "log_macros.h"
26
27#include <cmath>
28#include <algorithm>
Richard Burton00553462021-11-10 16:27:14 +000029
30namespace arm {
31namespace app {
32
33 /**
34 * @brief Helper function to increment current audio clip features index.
35 * @param[in,out] ctx Pointer to the application context object.
36 **/
37 static void IncrementAppCtxClipIdx(ApplicationContext& ctx);
38
39 /**
40 * @brief Quantize the given features and populate the input Tensor.
41 * @param[in] inputFeatures Vector of floating point features to quantize.
42 * @param[in] quantScale Quantization scale for the inputTensor.
43 * @param[in] quantOffset Quantization offset for the inputTensor.
44 * @param[in,out] inputTensor TFLite micro tensor to populate.
45 **/
46 static void QuantizeAndPopulateInput(rnn::vec1D32F& inputFeatures,
47 float quantScale, int quantOffset,
48 TfLiteTensor* inputTensor);
49
50 /* Noise reduction inference handler. */
51 bool NoiseReductionHandler(ApplicationContext& ctx, bool runAll)
52 {
53 constexpr uint32_t dataPsnTxtInfStartX = 20;
54 constexpr uint32_t dataPsnTxtInfStartY = 40;
55
56 /* Variables used for memory dumping. */
57 size_t memDumpMaxLen = 0;
58 uint8_t* memDumpBaseAddr = nullptr;
59 size_t undefMemDumpBytesWritten = 0;
60 size_t *pMemDumpBytesWritten = &undefMemDumpBytesWritten;
61 if (ctx.Has("MEM_DUMP_LEN") && ctx.Has("MEM_DUMP_BASE_ADDR") && ctx.Has("MEM_DUMP_BYTE_WRITTEN")) {
62 memDumpMaxLen = ctx.Get<size_t>("MEM_DUMP_LEN");
63 memDumpBaseAddr = ctx.Get<uint8_t*>("MEM_DUMP_BASE_ADDR");
64 pMemDumpBytesWritten = ctx.Get<size_t*>("MEM_DUMP_BYTE_WRITTEN");
65 }
66 std::reference_wrapper<size_t> memDumpBytesWritten = std::ref(*pMemDumpBytesWritten);
67
68 auto& platform = ctx.Get<hal_platform&>("platform");
Richard Burton00553462021-11-10 16:27:14 +000069 auto& profiler = ctx.Get<Profiler&>("profiler");
70
71 /* Get model reference. */
72 auto& model = ctx.Get<RNNoiseModel&>("model");
73 if (!model.IsInited()) {
74 printf_err("Model is not initialised! Terminating processing.\n");
75 return false;
76 }
77
78 /* Populate Pre-Processing related parameters. */
79 auto audioParamsWinLen = ctx.Get<uint32_t>("frameLength");
80 auto audioParamsWinStride = ctx.Get<uint32_t>("frameStride");
81 auto nrNumInputFeatures = ctx.Get<uint32_t>("numInputFeatures");
82
83 TfLiteTensor* inputTensor = model.GetInputTensor(0);
84 if (nrNumInputFeatures != inputTensor->bytes) {
85 printf_err("Input features size must be equal to input tensor size."
86 " Feature size = %" PRIu32 ", Tensor size = %zu.\n",
87 nrNumInputFeatures, inputTensor->bytes);
88 return false;
89 }
90
91 TfLiteTensor* outputTensor = model.GetOutputTensor(model.m_indexForModelOutput);
92
93 /* Initial choice of index for WAV file. */
94 auto startClipIdx = ctx.Get<uint32_t>("clipIndex");
95
96 std::function<const int16_t* (const uint32_t)> audioAccessorFunc = get_audio_array;
97 if (ctx.Has("features")) {
98 audioAccessorFunc = ctx.Get<std::function<const int16_t* (const uint32_t)>>("features");
99 }
100 std::function<uint32_t (const uint32_t)> audioSizeAccessorFunc = get_audio_array_size;
101 if (ctx.Has("featureSizes")) {
102 audioSizeAccessorFunc = ctx.Get<std::function<uint32_t (const uint32_t)>>("featureSizes");
103 }
104 std::function<const char*(const uint32_t)> audioFileAccessorFunc = get_filename;
105 if (ctx.Has("featureFileNames")) {
106 audioFileAccessorFunc = ctx.Get<std::function<const char*(const uint32_t)>>("featureFileNames");
107 }
108 do{
Richard Burton9b8d67a2021-12-10 12:32:51 +0000109 platform.data_psn->clear(COLOR_BLACK);
110
Richard Burton00553462021-11-10 16:27:14 +0000111 auto startDumpAddress = memDumpBaseAddr + memDumpBytesWritten;
112 auto currentIndex = ctx.Get<uint32_t>("clipIndex");
113
114 /* Creating a sliding window through the audio. */
115 auto audioDataSlider = audio::SlidingWindow<const int16_t>(
116 audioAccessorFunc(currentIndex),
117 audioSizeAccessorFunc(currentIndex), audioParamsWinLen,
118 audioParamsWinStride);
119
120 info("Running inference on input feature map %" PRIu32 " => %s\n", currentIndex,
121 audioFileAccessorFunc(currentIndex));
122
123 memDumpBytesWritten += DumpDenoisedAudioHeader(audioFileAccessorFunc(currentIndex),
124 (audioDataSlider.TotalStrides() + 1) * audioParamsWinLen,
125 memDumpBaseAddr + memDumpBytesWritten,
126 memDumpMaxLen - memDumpBytesWritten);
127
128 rnn::RNNoiseProcess featureProcessor = rnn::RNNoiseProcess();
129 rnn::vec1D32F audioFrame(audioParamsWinLen);
130 rnn::vec1D32F inputFeatures(nrNumInputFeatures);
131 rnn::vec1D32F denoisedAudioFrameFloat(audioParamsWinLen);
132 std::vector<int16_t> denoisedAudioFrame(audioParamsWinLen);
133
134 std::vector<float> modelOutputFloat(outputTensor->bytes);
135 rnn::FrameFeatures frameFeatures;
136 bool resetGRU = true;
137
138 while (audioDataSlider.HasNext()) {
139 const int16_t* inferenceWindow = audioDataSlider.Next();
140 audioFrame = rnn::vec1D32F(inferenceWindow, inferenceWindow+audioParamsWinLen);
141
142 featureProcessor.PreprocessFrame(audioFrame.data(), audioParamsWinLen, frameFeatures);
143
144 /* Reset or copy over GRU states first to avoid TFLu memory overlap issues. */
145 if (resetGRU){
146 model.ResetGruState();
147 } else {
148 /* Copying gru state outputs to gru state inputs.
149 * Call ResetGruState in between the sequence of inferences on unrelated input data. */
150 model.CopyGruStates();
151 }
152
153 QuantizeAndPopulateInput(frameFeatures.m_featuresVec,
154 inputTensor->params.scale, inputTensor->params.zero_point,
155 inputTensor);
156
157 /* Strings for presentation/logging. */
158 std::string str_inf{"Running inference... "};
159
160 /* Display message on the LCD - inference running. */
161 platform.data_psn->present_data_text(
162 str_inf.c_str(), str_inf.size(),
163 dataPsnTxtInfStartX, dataPsnTxtInfStartY, false);
164
165 info("Inference %zu/%zu\n", audioDataSlider.Index() + 1, audioDataSlider.TotalStrides() + 1);
166
167 /* Run inference over this feature sliding window. */
168 profiler.StartProfiling("Inference");
169 bool success = model.RunInference();
170 profiler.StopProfiling();
171 resetGRU = false;
172
173 if (!success) {
174 return false;
175 }
176
177 /* De-quantize main model output ready for post-processing. */
178 const auto* outputData = tflite::GetTensorData<int8_t>(outputTensor);
179 auto outputQuantParams = arm::app::GetTensorQuantParams(outputTensor);
180
181 for (size_t i = 0; i < outputTensor->bytes; ++i) {
182 modelOutputFloat[i] = (static_cast<float>(outputData[i]) - outputQuantParams.offset)
183 * outputQuantParams.scale;
184 }
185
186 /* Round and cast the post-processed results for dumping to wav. */
187 featureProcessor.PostProcessFrame(modelOutputFloat, frameFeatures, denoisedAudioFrameFloat);
188 for (size_t i = 0; i < audioParamsWinLen; ++i) {
189 denoisedAudioFrame[i] = static_cast<int16_t>(std::roundf(denoisedAudioFrameFloat[i]));
190 }
191
192 /* Erase. */
193 str_inf = std::string(str_inf.size(), ' ');
194 platform.data_psn->present_data_text(
195 str_inf.c_str(), str_inf.size(),
196 dataPsnTxtInfStartX, dataPsnTxtInfStartY, false);
197
198 if (memDumpMaxLen > 0) {
199 /* Dump output tensors to memory. */
200 memDumpBytesWritten += DumpOutputDenoisedAudioFrame(
201 denoisedAudioFrame,
202 memDumpBaseAddr + memDumpBytesWritten,
203 memDumpMaxLen - memDumpBytesWritten);
204 }
205 }
206
207 if (memDumpMaxLen > 0) {
208 /* Needed to not let the compiler complain about type mismatch. */
209 size_t valMemDumpBytesWritten = memDumpBytesWritten;
210 info("Output memory dump of %zu bytes written at address 0x%p\n",
211 valMemDumpBytesWritten, startDumpAddress);
212 }
213
214 DumpDenoisedAudioFooter(memDumpBaseAddr + memDumpBytesWritten, memDumpMaxLen - memDumpBytesWritten);
215
Richard Burton9b8d67a2021-12-10 12:32:51 +0000216 info("All inferences for audio clip complete.\n");
Richard Burton00553462021-11-10 16:27:14 +0000217 profiler.PrintProfilingResult();
218 IncrementAppCtxClipIdx(ctx);
219
Ayaan Masood233cec02021-12-09 17:22:22 +0000220 std::string clearString{' '};
221 platform.data_psn->present_data_text(
222 clearString.c_str(), clearString.size(),
223 dataPsnTxtInfStartX, dataPsnTxtInfStartY, false);
224
225 std::string completeMsg{"Inference complete!"};
226
227 /* Display message on the LCD - inference complete. */
228 platform.data_psn->present_data_text(
229 completeMsg.c_str(), completeMsg.size(),
230 dataPsnTxtInfStartX, dataPsnTxtInfStartY, false);
231
Richard Burton00553462021-11-10 16:27:14 +0000232 } while (runAll && ctx.Get<uint32_t>("clipIndex") != startClipIdx);
233
234 return true;
235 }
236
237 size_t DumpDenoisedAudioHeader(const char* filename, size_t dumpSize,
238 uint8_t *memAddress, size_t memSize){
239
240 if (memAddress == nullptr){
241 return 0;
242 }
243
244 int32_t filenameLength = strlen(filename);
245 size_t numBytesWritten = 0;
246 size_t numBytesToWrite = 0;
247 int32_t dumpSizeByte = dumpSize * sizeof(int16_t);
248 bool overflow = false;
249
250 /* Write the filename length */
251 numBytesToWrite = sizeof(filenameLength);
252 if (memSize - numBytesToWrite > 0) {
253 std::memcpy(memAddress, &filenameLength, numBytesToWrite);
254 numBytesWritten += numBytesToWrite;
255 memSize -= numBytesWritten;
256 } else {
257 overflow = true;
258 }
259
260 /* Write file name */
261 numBytesToWrite = filenameLength;
262 if(memSize - numBytesToWrite > 0) {
263 std::memcpy(memAddress + numBytesWritten, filename, numBytesToWrite);
264 numBytesWritten += numBytesToWrite;
265 memSize -= numBytesWritten;
266 } else {
267 overflow = true;
268 }
269
270 /* Write dumpSize in byte */
271 numBytesToWrite = sizeof(dumpSizeByte);
272 if(memSize - numBytesToWrite > 0) {
273 std::memcpy(memAddress + numBytesWritten, &(dumpSizeByte), numBytesToWrite);
274 numBytesWritten += numBytesToWrite;
275 memSize -= numBytesWritten;
276 } else {
277 overflow = true;
278 }
279
280 if(false == overflow) {
281 info("Audio Clip dump header info (%zu bytes) written to %p\n", numBytesWritten, memAddress);
282 } else {
283 printf_err("Not enough memory to dump Audio Clip header.\n");
284 }
285
286 return numBytesWritten;
287 }
288
289 size_t DumpDenoisedAudioFooter(uint8_t *memAddress, size_t memSize){
290 if ((memAddress == nullptr) || (memSize < 4)) {
291 return 0;
292 }
293 const int32_t eofMarker = -1;
294 std::memcpy(memAddress, &eofMarker, sizeof(int32_t));
295
296 return sizeof(int32_t);
297 }
298
299 size_t DumpOutputDenoisedAudioFrame(const std::vector<int16_t> &audioFrame,
300 uint8_t *memAddress, size_t memSize)
301 {
302 if (memAddress == nullptr) {
303 return 0;
304 }
305
306 size_t numByteToBeWritten = audioFrame.size() * sizeof(int16_t);
307 if( numByteToBeWritten > memSize) {
George Gekova2b0fc22021-11-08 16:30:43 +0000308 printf_err("Overflow error: Writing %zu of %zu bytes to memory @ 0x%p.\n", memSize, numByteToBeWritten, memAddress);
Richard Burton00553462021-11-10 16:27:14 +0000309 numByteToBeWritten = memSize;
310 }
311
312 std::memcpy(memAddress, audioFrame.data(), numByteToBeWritten);
313 info("Copied %zu bytes to %p\n", numByteToBeWritten, memAddress);
314
315 return numByteToBeWritten;
316 }
317
318 size_t DumpOutputTensorsToMemory(Model& model, uint8_t* memAddress, const size_t memSize)
319 {
320 const size_t numOutputs = model.GetNumOutputs();
321 size_t numBytesWritten = 0;
322 uint8_t* ptr = memAddress;
323
324 /* Iterate over all output tensors. */
325 for (size_t i = 0; i < numOutputs; ++i) {
326 const TfLiteTensor* tensor = model.GetOutputTensor(i);
327 const auto* tData = tflite::GetTensorData<uint8_t>(tensor);
328#if VERIFY_TEST_OUTPUT
329 arm::app::DumpTensor(tensor);
330#endif /* VERIFY_TEST_OUTPUT */
331 /* Ensure that we don't overflow the allowed limit. */
332 if (numBytesWritten + tensor->bytes <= memSize) {
333 if (tensor->bytes > 0) {
334 std::memcpy(ptr, tData, tensor->bytes);
335
336 info("Copied %zu bytes for tensor %zu to 0x%p\n",
337 tensor->bytes, i, ptr);
338
339 numBytesWritten += tensor->bytes;
340 ptr += tensor->bytes;
341 }
342 } else {
343 printf_err("Error writing tensor %zu to memory @ 0x%p\n",
344 i, memAddress);
345 break;
346 }
347 }
348
349 info("%zu bytes written to memory @ 0x%p\n", numBytesWritten, memAddress);
350
351 return numBytesWritten;
352 }
353
354 static void IncrementAppCtxClipIdx(ApplicationContext& ctx)
355 {
356 auto curClipIdx = ctx.Get<uint32_t>("clipIndex");
357 if (curClipIdx + 1 >= NUMBER_OF_FILES) {
358 ctx.Set<uint32_t>("clipIndex", 0);
359 return;
360 }
361 ++curClipIdx;
362 ctx.Set<uint32_t>("clipIndex", curClipIdx);
363 }
364
365 void QuantizeAndPopulateInput(rnn::vec1D32F& inputFeatures,
366 const float quantScale, const int quantOffset, TfLiteTensor* inputTensor)
367 {
368 const float minVal = std::numeric_limits<int8_t>::min();
369 const float maxVal = std::numeric_limits<int8_t>::max();
370
371 auto* inputTensorData = tflite::GetTensorData<int8_t>(inputTensor);
372
373 for (size_t i=0; i < inputFeatures.size(); ++i) {
374 float quantValue = ((inputFeatures[i] / quantScale) + quantOffset);
375 inputTensorData[i] = static_cast<int8_t>(std::min<float>(std::max<float>(quantValue, minVal), maxVal));
376 }
377 }
378
379
380} /* namespace app */
381} /* namespace arm */