blob: 8437bd06c9a175f1054b3592fd72d5dd08b5508b [file] [log] [blame]
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <stdint.h>
#include "signal/src/circular_buffer.h"
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
#include "tensorflow/lite/kernels/kernel_util.h"
#include "tensorflow/lite/micro/flatbuffer_utils.h"
#include "tensorflow/lite/micro/kernels/kernel_util.h"
#include "tensorflow/lite/micro/memory_helpers.h"
#include "tensorflow/lite/micro/micro_utils.h"
namespace tflite {
namespace {
constexpr int kInputTensor = 0;
constexpr int kOutputTensor = 0;
constexpr int kOutputValidTensor = 1;
// Indices into the init flexbuffer's vector.
// The parameter's name is in the comment that follows.
// Elements in the vectors are ordered alphabetically by parameter name.
constexpr int kFrameSizeIndex = 0; // 'frame_size'
constexpr int kFrameStepIndex = 1; // 'frame_step'
constexpr int kPrefillIndex = 2; // 'prefill'
struct TFLMSignalFramerParams {
int32_t frame_size;
int32_t frame_step;
int32_t outer_dims;
int32_t n_frames;
bool prefill;
int8_t** state_buffers;
tflite::tflm_signal::CircularBuffer** circular_buffers;
};
void ResetState(TFLMSignalFramerParams* params) {
for (int i = 0; i < params->outer_dims; ++i) {
tflite::tflm_signal::CircularBufferReset(params->circular_buffers[i]);
if (params->prefill) {
tflite::tflm_signal::CircularBufferWriteZeros(
params->circular_buffers[i], params->frame_size - params->frame_step);
}
}
}
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
const uint8_t* buffer_t = reinterpret_cast<const uint8_t*>(buffer);
auto* params =
static_cast<TFLMSignalFramerParams*>(context->AllocatePersistentBuffer(
context, sizeof(TFLMSignalFramerParams)));
if (params == nullptr) {
return nullptr;
}
tflite::FlexbufferWrapper fbw(buffer_t, length);
params->frame_size = fbw.ElementAsInt32(kFrameSizeIndex);
params->frame_step = fbw.ElementAsInt32(kFrameStepIndex);
params->prefill = fbw.ElementAsBool(kPrefillIndex);
return params;
}
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 2);
MicroContext* micro_context = GetMicroContext(context);
TfLiteTensor* input =
micro_context->AllocateTempInputTensor(node, kInputTensor);
TF_LITE_ENSURE(context, input != nullptr);
TfLiteTensor* output =
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
TF_LITE_ENSURE(context, output != nullptr);
TfLiteTensor* output_valid =
micro_context->AllocateTempOutputTensor(node, kOutputValidTensor);
TF_LITE_ENSURE(context, output_valid != nullptr);
TF_LITE_ENSURE_EQ(context, NumDimensions(input) + 1, NumDimensions(output));
TF_LITE_ENSURE_EQ(context, NumDimensions(output_valid), 0);
TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteInt16);
TF_LITE_ENSURE_TYPES_EQ(context, output->type, kTfLiteInt16);
TF_LITE_ENSURE_TYPES_EQ(context, output_valid->type, kTfLiteBool);
auto* params = reinterpret_cast<TFLMSignalFramerParams*>(node->user_data);
RuntimeShape input_shape = GetTensorShape(input);
int innermost_dim = input_shape.Dims(input_shape.DimensionsCount() - 1);
TF_LITE_ENSURE(context, innermost_dim >= params->frame_step);
TF_LITE_ENSURE_EQ(context, innermost_dim % params->frame_step, 0);
params->outer_dims = input_shape.FlatSize() / innermost_dim;
params->n_frames = innermost_dim / params->frame_step;
params->state_buffers =
static_cast<int8_t**>(context->AllocatePersistentBuffer(
context, params->outer_dims * sizeof(int8_t*)));
params->circular_buffers = static_cast<tflite::tflm_signal::CircularBuffer**>(
context->AllocatePersistentBuffer(
context,
params->outer_dims * sizeof(tflite::tflm_signal::CircularBuffer*)));
for (int i = 0; i < params->outer_dims; i++) {
// Calculate the capacity of the circular buffer. Round up the frame size to
// a multiple of frame step. Saves memory relative to the simpler frame_size
// + frame_step. For example: step_size = 160, frame_size = 400 capacity =
// 480 vs. step_size + frame_size = 560
size_t capacity = (params->frame_size + params->frame_step - 1) /
params->frame_step * params->frame_step;
size_t state_size =
tflite::tflm_signal::CircularBufferGetNeededMemory(capacity);
params->state_buffers[i] =
static_cast<int8_t*>(context->AllocatePersistentBuffer(
context, state_size * sizeof(int8_t)));
params->circular_buffers[i] = tflite::tflm_signal::CircularBufferInit(
capacity, params->state_buffers[i], state_size);
}
ResetState(params);
micro_context->DeallocateTempTfLiteTensor(input);
micro_context->DeallocateTempTfLiteTensor(output);
micro_context->DeallocateTempTfLiteTensor(output_valid);
return kTfLiteOk;
}
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
auto* params = reinterpret_cast<TFLMSignalFramerParams*>(node->user_data);
const TfLiteEvalTensor* input =
tflite::micro::GetEvalInput(context, node, kInputTensor);
TfLiteEvalTensor* output =
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
TfLiteEvalTensor* output_valid =
tflite::micro::GetEvalOutput(context, node, kOutputValidTensor);
const int16_t* input_data = tflite::micro::GetTensorData<int16_t>(input);
int16_t* output_data = tflite::micro::GetTensorData<int16_t>(output);
bool* output_valid_data = tflite::micro::GetTensorData<bool>(output_valid);
*output_valid_data = true;
for (int i = 0; i < params->outer_dims; i++) {
for (int frame = 0; frame < params->n_frames; frame++) {
int input_idx = (i * params->n_frames + frame) * params->frame_step;
int output_idx = (i * params->n_frames + frame) * params->frame_size;
tflite::tflm_signal::CircularBufferWrite(params->circular_buffers[i],
&input_data[input_idx],
params->frame_step);
if (tflite::tflm_signal::CircularBufferAvailable(
params->circular_buffers[i]) >=
static_cast<size_t>(params->frame_size)) {
tflite::tflm_signal::CircularBufferGet(params->circular_buffers[i],
params->frame_size,
&output_data[output_idx]);
tflite::tflm_signal::CircularBufferDiscard(params->circular_buffers[i],
params->frame_step);
} else {
*output_valid_data = false;
}
}
}
return kTfLiteOk;
}
void Reset(TfLiteContext* context, void* buffer) {
ResetState(static_cast<TFLMSignalFramerParams*>(buffer));
}
} // namespace
namespace tflm_signal {
// TODO(b/286250473): remove namespace once de-duped libraries above
TFLMRegistration* Register_FRAMER() {
static TFLMRegistration r =
tflite::micro::RegisterOp(Init, Prepare, Eval, nullptr, Reset);
return &r;
}
} // namespace tflm_signal
} // namespace tflite