| /* |
| * 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 "Wav2LetterPreprocess.hpp" |
| |
| #include <limits> |
| #include <catch.hpp> |
| |
| constexpr uint32_t numMfccFeatures = 13; |
| constexpr uint32_t numMfccVectors = 10; |
| |
| /* Test vector output: generated using test-asr-preprocessing.py. */ |
| int8_t expectedResult[numMfccVectors][numMfccFeatures * 3] = { |
| /* Feature vec 0. */ |
| -32, 4, -9, -8, -10, -10, -11, -11, -11, -11, -12, -11, -11, /* MFCCs. */ |
| -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, /* Delta 1. */ |
| -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, /* Delta 2. */ |
| |
| /* Feature vec 1. */ |
| -31, 4, -9, -8, -10, -10, -11, -11, -11, -11, -12, -11, -11, |
| -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, |
| -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, |
| |
| /* Feature vec 2. */ |
| -31, 4, -9, -9, -10, -10, -11, -11, -11, -11, -12, -12, -12, |
| -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, |
| -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, |
| |
| /* Feature vec 3. */ |
| -31, 4, -9, -9, -10, -10, -11, -11, -11, -11, -11, -12, -12, |
| -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, |
| -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, |
| |
| /* Feature vec 4 : this should have valid delta 1 and delta 2. */ |
| -31, 4, -9, -9, -10, -10, -11, -11, -11, -11, -11, -12, -12, |
| -38, -29, -9, 1, -2, -7, -8, -8, -12, -16, -14, -5, 5, |
| -68, -50, -13, 5, 0, -9, -9, -8, -13, -20, -19, -3, 15, |
| |
| /* Feature vec 5 : this should have valid delta 1 and delta 2. */ |
| -31, 4, -9, -8, -10, -10, -11, -11, -11, -11, -11, -12, -12, |
| -62, -45, -11, 5, 0, -8, -9, -8, -12, -19, -17, -3, 13, |
| -27, -22, -13, -9, -11, -12, -12, -11, -11, -13, -13, -10, -6, |
| |
| /* Feature vec 6. */ |
| -31, 4, -9, -8, -10, -10, -11, -11, -11, -11, -12, -11, -11, |
| -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, |
| -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, |
| |
| /* Feature vec 7. */ |
| -32, 4, -9, -8, -10, -10, -11, -11, -11, -12, -12, -11, -11, |
| -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, |
| -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, |
| |
| /* Feature vec 8. */ |
| -32, 4, -9, -8, -10, -10, -11, -11, -11, -12, -12, -11, -11, |
| -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, |
| -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, |
| |
| /* Feature vec 9. */ |
| -31, 4, -9, -8, -10, -10, -11, -11, -11, -11, -12, -11, -11, |
| -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, |
| -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10 |
| }; |
| |
| void PopulateTestWavVector(std::vector<int16_t>& vec) |
| { |
| constexpr int int16max = std::numeric_limits<int16_t>::max(); |
| int val = 0; |
| for (size_t i = 0; i < vec.size(); ++i, ++val) { |
| |
| /* We want a differential filter response from both - order 1 |
| * and 2 => Don't have a linear signal here - we use a signal |
| * using squares for example. Alternate sign flips might work |
| * just as well and will be computationally less work! */ |
| int valsq = val * val; |
| if (valsq > int16max) { |
| val = 0; |
| valsq = 0; |
| } |
| vec[i] = valsq; |
| } |
| } |
| |
| TEST_CASE("Preprocessing calculation INT8") |
| { |
| /* Constants. */ |
| const uint32_t windowLen = 512; |
| const uint32_t windowStride = 160; |
| int dimArray[] = {3, 1, numMfccFeatures * 3, numMfccVectors}; |
| const float quantScale = 0.1410219967365265; |
| const int quantOffset = -11; |
| |
| /* Test wav memory. */ |
| std::vector <int16_t> testWav((windowStride * numMfccVectors) + |
| (windowLen - windowStride)); |
| |
| /* Populate with dummy input. */ |
| PopulateTestWavVector(testWav); |
| |
| /* Allocate mem for tensor. */ |
| std::vector<int8_t> tensorVec(dimArray[1]*dimArray[2]*dimArray[3]); |
| |
| /* Initialise dimensions and the test tensor. */ |
| TfLiteIntArray* dims= tflite::testing::IntArrayFromInts(dimArray); |
| TfLiteTensor tensor = tflite::testing::CreateQuantizedTensor( |
| tensorVec.data(), dims, quantScale, quantOffset, "preprocessedInput"); |
| |
| /* Initialise pre-processing module. */ |
| arm::app::audio::asr::Preprocess prep{ |
| numMfccFeatures, windowLen, windowStride, numMfccVectors}; |
| |
| /* Invoke pre-processing. */ |
| REQUIRE(prep.Invoke(testWav.data(), testWav.size(), &tensor)); |
| |
| /* Wrap the tensor with a std::vector for ease. */ |
| auto* tensorData = tflite::GetTensorData<int8_t>(&tensor); |
| std::vector <int8_t> vecResults = |
| std::vector<int8_t>(tensorData, tensorData + tensor.bytes); |
| |
| /* Check sizes. */ |
| REQUIRE(vecResults.size() == sizeof(expectedResult)); |
| |
| /* Check that the elements have been calculated correctly. */ |
| for (uint32_t j = 0; j < numMfccVectors; ++j) { |
| for (uint32_t i = 0; i < numMfccFeatures * 3; ++i) { |
| size_t tensorIdx = (j * numMfccFeatures * 3) + i; |
| CHECK(vecResults[tensorIdx] == expectedResult[j][i]); |
| } |
| } |
| } |