| /* |
| * Copyright (c) 2021 Arm Limited. All rights reserved. |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include "RNNoiseModel.hpp" |
| #include "TensorFlowLiteMicro.hpp" |
| #include "TestData_noise_reduction.hpp" |
| |
| #include <catch.hpp> |
| #include <random> |
| |
| bool RunInference(arm::app::Model& model, std::vector<int8_t> vec, |
| const size_t sizeRequired, const size_t dataInputIndex) |
| { |
| TfLiteTensor* inputTensor = model.GetInputTensor(dataInputIndex); |
| REQUIRE(inputTensor); |
| size_t copySz = inputTensor->bytes < sizeRequired ? inputTensor->bytes : sizeRequired; |
| const int8_t* vecData = vec.data(); |
| memcpy(inputTensor->data.data, vecData, copySz); |
| return model.RunInference(); |
| } |
| |
| void genRandom(size_t bytes, std::vector<int8_t>& randomAudio) |
| { |
| randomAudio.resize(bytes); |
| std::random_device rndDevice; |
| std::mt19937 mersenneGen{rndDevice()}; |
| std::uniform_int_distribution<short> dist {-128, 127}; |
| auto gen = [&dist, &mersenneGen](){ |
| return dist(mersenneGen); |
| }; |
| std::generate(std::begin(randomAudio), std::end(randomAudio), gen); |
| } |
| |
| bool RunInferenceRandom(arm::app::Model& model, const size_t dataInputIndex) |
| { |
| std::array<size_t, 4> inputSizes = {IFM_0_DATA_SIZE, IFM_1_DATA_SIZE, IFM_2_DATA_SIZE, IFM_3_DATA_SIZE}; |
| std::vector<int8_t> randomAudio; |
| TfLiteTensor* inputTensor = model.GetInputTensor(dataInputIndex); |
| REQUIRE(inputTensor); |
| genRandom(inputTensor->bytes, randomAudio); |
| |
| REQUIRE(RunInference(model, randomAudio, inputSizes[dataInputIndex], dataInputIndex)); |
| return true; |
| } |
| |
| TEST_CASE("Running random inference with TensorFlow Lite Micro and RNNoiseModel Int8", "[RNNoise]") |
| { |
| arm::app::RNNoiseModel model{}; |
| |
| REQUIRE_FALSE(model.IsInited()); |
| REQUIRE(model.Init()); |
| REQUIRE(model.IsInited()); |
| |
| model.ResetGruState(); |
| |
| for (int i = 1; i < 4; i++ ) { |
| TfLiteTensor* inputGruStateTensor = model.GetInputTensor(i); |
| auto* inputGruState = tflite::GetTensorData<int8_t>(inputGruStateTensor); |
| for (size_t tIndex = 0; tIndex < inputGruStateTensor->bytes; tIndex++) { |
| REQUIRE(inputGruState[tIndex] == arm::app::GetTensorQuantParams(inputGruStateTensor).offset); |
| } |
| } |
| |
| REQUIRE(RunInferenceRandom(model, 0)); |
| } |
| |
| class TestRNNoiseModel : public arm::app::RNNoiseModel |
| { |
| public: |
| bool CopyGruStatesTest() { |
| return RNNoiseModel::CopyGruStates(); |
| } |
| |
| std::vector<std::pair<size_t, size_t>> GetStateMap() { |
| return m_gruStateMap; |
| } |
| |
| }; |
| |
| template <class T> |
| void printArray(size_t dataSz, T data){ |
| char strhex[8]; |
| std::string strdump; |
| |
| for (size_t i = 0; i < dataSz; ++i) { |
| if (0 == i % 8) { |
| printf("%s\n\t", strdump.c_str()); |
| strdump.clear(); |
| } |
| snprintf(strhex, sizeof(strhex) - 1, |
| "0x%02x, ", data[i]); |
| strdump += std::string(strhex); |
| } |
| |
| if (!strdump.empty()) { |
| printf("%s\n", strdump.c_str()); |
| } |
| } |
| |
| /* This is true for gcc x86 platform, not guaranteed for other compilers and platforms. */ |
| TEST_CASE("Test initial GRU out state is 0", "[RNNoise]") |
| { |
| TestRNNoiseModel model{}; |
| model.Init(); |
| |
| auto map = model.GetStateMap(); |
| |
| for(auto& mapping: map) { |
| TfLiteTensor* gruOut = model.GetOutputTensor(mapping.first); |
| auto* outGruState = tflite::GetTensorData<uint8_t>(gruOut); |
| |
| printf("gru out state:"); |
| printArray(gruOut->bytes, outGruState); |
| |
| for (size_t tIndex = 0; tIndex < gruOut->bytes; tIndex++) { |
| REQUIRE(outGruState[tIndex] == 0); |
| } |
| } |
| |
| } |
| |
| TEST_CASE("Test GRU state copy", "[RNNoise]") |
| { |
| TestRNNoiseModel model{}; |
| model.Init(); |
| REQUIRE(RunInferenceRandom(model, 0)); |
| |
| auto map = model.GetStateMap(); |
| |
| std::vector<std::vector<uint8_t>> oldStates; |
| for(auto& mapping: map) { |
| |
| TfLiteTensor* gruOut = model.GetOutputTensor(mapping.first); |
| auto* outGruState = tflite::GetTensorData<uint8_t>(gruOut); |
| /* Save old output state. */ |
| std::vector<uint8_t> oldState(gruOut->bytes); |
| memcpy(oldState.data(), outGruState, gruOut->bytes); |
| oldStates.push_back(oldState); |
| } |
| |
| model.CopyGruStatesTest(); |
| auto statesIter = oldStates.begin(); |
| for(auto& mapping: map) { |
| TfLiteTensor* gruInput = model.GetInputTensor(mapping.second); |
| auto* inGruState = tflite::GetTensorData<uint8_t>(gruInput); |
| for (size_t tIndex = 0; tIndex < gruInput->bytes; tIndex++) { |
| REQUIRE((*statesIter)[tIndex] == inGruState[tIndex]); |
| } |
| statesIter++; |
| } |
| |
| } |