janitor

janitor
git clone https://git.prime8.dev/janitor.git
Log | Files | Refs | README | LICENSE

feature-mfcc.cc (5692B)


      1 /**
      2  * Copyright (c)  2022  Xiaomi Corporation (authors: Fangjun Kuang)
      3  *
      4  * See LICENSE for clarification regarding multiple authors
      5  *
      6  * Licensed under the Apache License, Version 2.0 (the "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 
     19 // This file is copied/modified from kaldi/src/feat/feature-mfcc.cc
     20 //
     21 #include "kaldi-native-fbank/csrc/feature-mfcc.h"
     22 
     23 #include <algorithm>
     24 #include <cmath>
     25 #include <limits>
     26 #include <utility>
     27 #include <vector>
     28 
     29 #include "kaldi-native-fbank/csrc/feature-functions.h"
     30 #include "kaldi-native-fbank/csrc/feature-window.h"
     31 #include "kaldi-native-fbank/csrc/kaldi-math.h"
     32 #include "kaldi-native-fbank/csrc/log.h"
     33 
     34 namespace knf {
     35 
     36 static std::vector<float> ComputeDctMatrix(int32_t num_rows, int32_t num_cols) {
     37   // this function is copied from
     38   // https://github.com/kaldi-asr/kaldi/blob/master/src/matrix/matrix-functions.cc#L592
     39 
     40   std::vector<float> ans(num_rows * num_cols);
     41   float *p = ans.data();
     42 
     43   float normalizer = std::sqrt(1.0 / num_cols);  // normalizer for X_0
     44 
     45   for (int32_t i = 0; i != num_cols; ++i) {
     46     p[i] = normalizer;
     47   }
     48 
     49   normalizer = std::sqrt(2.0 / num_cols);  // normalizer for other elements
     50 
     51   for (int32_t k = 1; k != num_rows; ++k) {
     52     for (int32_t n = 0; n != num_cols; ++n) {
     53       *(p + k * num_cols + n) =
     54           normalizer *
     55           std::cos(static_cast<double>(M_PI) / num_cols * (n + 0.5) * k);
     56     }
     57   }
     58 
     59   return ans;
     60 }
     61 
     62 std::ostream &operator<<(std::ostream &os, const MfccOptions &opts) {
     63   os << opts.ToString();
     64   return os;
     65 }
     66 
     67 MfccComputer::MfccComputer(const MfccOptions &opts)
     68     : opts_(opts),
     69       rfft_(opts.frame_opts.PaddedWindowSize()),
     70       mel_energies_(opts.mel_opts.num_bins) {
     71   if (opts.energy_floor > 0.0f) {
     72     log_energy_floor_ = logf(opts.energy_floor);
     73   }
     74 
     75   // We'll definitely need the filterbanks info for VTLN warping factor 1.0.
     76   // [note: this call caches it.]
     77   GetMelBanks(1.0f);
     78 
     79   int32_t num_bins = opts.mel_opts.num_bins;
     80 
     81   KNF_CHECK_LE(opts.num_ceps, num_bins)
     82       << "num-ceps cannot be larger than num-mel-bins."
     83       << " It should be smaller or equal. You provided num-ceps: "
     84       << opts.num_ceps << "  and num-mel-bins: " << num_bins;
     85 
     86   dct_matrix_ = ComputeDctMatrix(opts.num_ceps, num_bins);
     87 
     88   if (opts.cepstral_lifter != 0.0) {
     89     lifter_coeffs_ = std::vector<float>(opts.num_ceps);
     90     // torch::empty({1, opts.num_ceps}, torch::kFloat32);
     91     ComputeLifterCoeffs(opts.cepstral_lifter, &lifter_coeffs_);
     92   }
     93 }
     94 
     95 MfccComputer::~MfccComputer() {
     96   for (auto iter = mel_banks_.begin(); iter != mel_banks_.end(); ++iter)
     97     delete iter->second;
     98 }
     99 
    100 const MelBanks *MfccComputer::GetMelBanks(float vtln_warp) {
    101   MelBanks *this_mel_banks = nullptr;
    102 
    103   // std::map<float, MelBanks *>::iterator iter = mel_banks_.find(vtln_warp);
    104   auto iter = mel_banks_.find(vtln_warp);
    105   if (iter == mel_banks_.end()) {
    106     this_mel_banks = new MelBanks(opts_.mel_opts, opts_.frame_opts, vtln_warp);
    107     mel_banks_[vtln_warp] = this_mel_banks;
    108   } else {
    109     this_mel_banks = iter->second;
    110   }
    111   return this_mel_banks;
    112 }
    113 
    114 void MfccComputer::Compute(float signal_raw_log_energy, float vtln_warp,
    115                            std::vector<float> *signal_frame, float *feature) {
    116   const MelBanks &mel_banks = *(GetMelBanks(vtln_warp));
    117 
    118   KNF_CHECK_EQ(signal_frame->size(), opts_.frame_opts.PaddedWindowSize());
    119 
    120   // Compute energy after window function (not the raw one).
    121   if (opts_.use_energy && !opts_.raw_energy) {
    122     signal_raw_log_energy = std::log(
    123         std::max<float>(InnerProduct(signal_frame->data(), signal_frame->data(),
    124                                      signal_frame->size()),
    125                         std::numeric_limits<float>::epsilon()));
    126   }
    127   rfft_.Compute(signal_frame->data());  // signal_frame is modified in-place
    128   ComputePowerSpectrum(signal_frame);
    129 
    130   // Sum with mel filter banks over the power spectrum
    131   mel_banks.Compute(signal_frame->data(), mel_energies_.data());
    132 
    133   // Avoid log of zero (which should be prevented anyway by dithering).
    134   for (int32_t i = 0; i != opts_.mel_opts.num_bins; ++i) {
    135     auto t = std::max(mel_energies_[i], std::numeric_limits<float>::epsilon());
    136     mel_energies_[i] = std::log(t);
    137   }
    138 
    139   // feature = dct_matrix_ * mel_energies [which now have log]
    140   for (int32_t i = 0; i != opts_.num_ceps; ++i) {
    141     feature[i] = InnerProduct(dct_matrix_.data() + i * opts_.mel_opts.num_bins,
    142                               mel_energies_.data(), opts_.mel_opts.num_bins);
    143   }
    144 
    145   if (opts_.cepstral_lifter != 0.0) {
    146     for (int32_t i = 0; i != opts_.num_ceps; ++i) {
    147       feature[i] *= lifter_coeffs_[i];
    148     }
    149   }
    150 
    151   if (opts_.use_energy) {
    152     if (opts_.energy_floor > 0.0 && signal_raw_log_energy < log_energy_floor_) {
    153       signal_raw_log_energy = log_energy_floor_;
    154     }
    155     feature[0] = signal_raw_log_energy;
    156   }
    157 
    158   if (opts_.htk_compat) {
    159     float energy = feature[0];
    160 
    161     for (int32_t i = 0; i < opts_.num_ceps - 1; ++i) {
    162       feature[i] = feature[i + 1];
    163     }
    164 
    165     if (!opts_.use_energy) {
    166       energy *= M_SQRT2;  // scale on C0 (actually removing a scale
    167     }
    168     // we previously added that's part of one common definition of
    169     // the cosine transform.)
    170     feature[opts_.num_ceps - 1] = energy;
    171   }
    172 }
    173 
    174 }  // namespace knf