#include "feature-mfcc.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include <utility>
#include <vector>
#include "feature-functions.h"
#include "feature-window.h"
#include "kaldi-math.h"
#include "log.h"
namespace knf {
static std::vector<float> ComputeDctMatrix(int32_t num_rows, int32_t num_cols) {
std::vector<float> ans(num_rows * num_cols);
float *p = ans.data();
float normalizer = std::sqrt(1.0 / num_cols);
for (int32_t i = 0; i != num_cols; ++i) {
p[i] = normalizer;
}
normalizer = std::sqrt(2.0 / num_cols);
for (int32_t k = 1; k != num_rows; ++k) {
for (int32_t n = 0; n != num_cols; ++n) {
*(p + k * num_cols + n) =
normalizer *
std::cos(static_cast<double>(M_PI) / num_cols * (n + 0.5) * k);
}
}
return ans;
}
std::ostream &operator<<(std::ostream &os, const MfccOptions &opts) {
os << opts.ToString();
return os;
}
MfccComputer::MfccComputer(const MfccOptions &opts)
: opts_(opts),
rfft_(opts.frame_opts.PaddedWindowSize()),
mel_energies_(opts.mel_opts.num_bins) {
if (opts.energy_floor > 0.0f) {
log_energy_floor_ = logf(opts.energy_floor);
}
GetMelBanks(1.0f);
int32_t num_bins = opts.mel_opts.num_bins;
KNF_CHECK_LE(opts.num_ceps, num_bins)
<< "num-ceps cannot be larger than num-mel-bins."
<< " It should be smaller or equal. You provided num-ceps: "
<< opts.num_ceps << " and num-mel-bins: " << num_bins;
dct_matrix_ = ComputeDctMatrix(opts.num_ceps, num_bins);
if (opts.cepstral_lifter != 0.0) {
lifter_coeffs_ = std::vector<float>(opts.num_ceps);
ComputeLifterCoeffs(opts.cepstral_lifter, &lifter_coeffs_);
}
}
MfccComputer::~MfccComputer() {
for (auto iter = mel_banks_.begin(); iter != mel_banks_.end(); ++iter)
delete iter->second;
}
const MelBanks *MfccComputer::GetMelBanks(float vtln_warp) {
MelBanks *this_mel_banks = nullptr;
auto iter = mel_banks_.find(vtln_warp);
if (iter == mel_banks_.end()) {
this_mel_banks = new MelBanks(opts_.mel_opts, opts_.frame_opts, vtln_warp);
mel_banks_[vtln_warp] = this_mel_banks;
} else {
this_mel_banks = iter->second;
}
return this_mel_banks;
}
void MfccComputer::Compute(float signal_raw_log_energy, float vtln_warp,
std::vector<float> *signal_frame, float *feature) {
const MelBanks &mel_banks = *(GetMelBanks(vtln_warp));
KNF_CHECK_EQ(signal_frame->size(), opts_.frame_opts.PaddedWindowSize());
if (opts_.use_energy && !opts_.raw_energy) {
signal_raw_log_energy = std::log(
std::max<float>(InnerProduct(signal_frame->data(), signal_frame->data(),
signal_frame->size()),
std::numeric_limits<float>::epsilon()));
}
rfft_.Compute(signal_frame->data()); ComputePowerSpectrum(signal_frame);
mel_banks.Compute(signal_frame->data(), mel_energies_.data());
for (int32_t i = 0; i != opts_.mel_opts.num_bins; ++i) {
auto t = std::max(mel_energies_[i], std::numeric_limits<float>::epsilon());
mel_energies_[i] = std::log(t);
}
for (int32_t i = 0; i != opts_.num_ceps; ++i) {
feature[i] = InnerProduct(dct_matrix_.data() + i * opts_.mel_opts.num_bins,
mel_energies_.data(), opts_.mel_opts.num_bins);
}
if (opts_.cepstral_lifter != 0.0) {
for (int32_t i = 0; i != opts_.num_ceps; ++i) {
feature[i] *= lifter_coeffs_[i];
}
}
if (opts_.use_energy) {
if (opts_.energy_floor > 0.0 && signal_raw_log_energy < log_energy_floor_) {
signal_raw_log_energy = log_energy_floor_;
}
feature[0] = signal_raw_log_energy;
}
if (opts_.htk_compat) {
double energy = feature[0];
for (int32_t i = 0; i < opts_.num_ceps - 1; ++i) {
feature[i] = feature[i + 1];
}
if (!opts_.use_energy) {
energy *= M_SQRT2; }
feature[opts_.num_ceps - 1] = static_cast<float>(energy);
}
}
}