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