You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
721 lines
25 KiB
721 lines
25 KiB
// Copyright (c) 2022 PaddlePaddle 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.
|
|
|
|
// feat/feature-mfcc-test.cc
|
|
|
|
// Copyright 2009-2011 Karel Vesely; Petr Motlicek
|
|
|
|
// See ../../COPYING for clarification regarding multiple authors
|
|
//
|
|
// 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
|
|
//
|
|
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
|
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
|
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
// See the Apache 2 License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include "base/kaldi-math.h"
|
|
#include "feat/feature-mfcc.h"
|
|
#include "feat/wave-reader.h"
|
|
#include "matrix/kaldi-matrix-inl.h"
|
|
|
|
using namespace kaldi;
|
|
|
|
|
|
static void UnitTestReadWave() {
|
|
std::cout << "=== UnitTestReadWave() ===\n";
|
|
|
|
Vector<BaseFloat> v, v2;
|
|
|
|
std::cout << "<<<=== Reading waveform\n";
|
|
|
|
{
|
|
std::ifstream is("test_data/test.wav", std::ios_base::binary);
|
|
WaveData wave;
|
|
wave.Read(is);
|
|
const Matrix<BaseFloat> data(wave.Data());
|
|
KALDI_ASSERT(data.NumRows() == 1);
|
|
v.Resize(data.NumCols());
|
|
v.CopyFromVec(data.Row(0));
|
|
}
|
|
|
|
std::cout
|
|
<< "<<<=== Reading Vector<BaseFloat> waveform, prepared by matlab\n";
|
|
std::ifstream input("test_data/test_matlab.ascii");
|
|
KALDI_ASSERT(input.good());
|
|
v2.Read(input, false);
|
|
input.close();
|
|
|
|
std::cout
|
|
<< "<<<=== Comparing freshly read waveform to 'libsndfile' waveform\n";
|
|
KALDI_ASSERT(v.Dim() == v2.Dim());
|
|
for (int32 i = 0; i < v.Dim(); i++) {
|
|
KALDI_ASSERT(v(i) == v2(i));
|
|
}
|
|
std::cout << "<<<=== Comparing done\n";
|
|
|
|
// std::cout << "== The Waveform Samples == \n";
|
|
// std::cout << v;
|
|
|
|
std::cout << "Test passed :)\n\n";
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
static void UnitTestSimple() {
|
|
std::cout << "=== UnitTestSimple() ===\n";
|
|
|
|
Vector<BaseFloat> v(100000);
|
|
Matrix<BaseFloat> m;
|
|
|
|
// init with noise
|
|
for (int32 i = 0; i < v.Dim(); i++) {
|
|
v(i) = (abs(i * 433024253) % 65535) - (65535 / 2);
|
|
}
|
|
|
|
std::cout << "<<<=== Just make sure it runs... Nothing is compared\n";
|
|
// the parametrization object
|
|
MfccOptions op;
|
|
// trying to have same opts as baseline.
|
|
op.frame_opts.dither = 0.0;
|
|
op.frame_opts.preemph_coeff = 0.0;
|
|
op.frame_opts.window_type = "rectangular";
|
|
op.frame_opts.remove_dc_offset = false;
|
|
op.frame_opts.round_to_power_of_two = true;
|
|
op.mel_opts.low_freq = 0.0;
|
|
op.mel_opts.htk_mode = true;
|
|
op.htk_compat = true;
|
|
|
|
Mfcc mfcc(op);
|
|
// use default parameters
|
|
|
|
// compute mfccs.
|
|
mfcc.Compute(v, 1.0, &m);
|
|
|
|
// possibly dump
|
|
// std::cout << "== Output features == \n" << m;
|
|
std::cout << "Test passed :)\n\n";
|
|
}
|
|
|
|
|
|
static void UnitTestHTKCompare1() {
|
|
std::cout << "=== UnitTestHTKCompare1() ===\n";
|
|
|
|
std::ifstream is("test_data/test.wav", std::ios_base::binary);
|
|
WaveData wave;
|
|
wave.Read(is);
|
|
KALDI_ASSERT(wave.Data().NumRows() == 1);
|
|
SubVector<BaseFloat> waveform(wave.Data(), 0);
|
|
|
|
// read the HTK features
|
|
Matrix<BaseFloat> htk_features;
|
|
{
|
|
std::ifstream is("test_data/test.wav.fea_htk.1",
|
|
std::ios::in | std::ios_base::binary);
|
|
bool ans = ReadHtk(is, &htk_features, 0);
|
|
KALDI_ASSERT(ans);
|
|
}
|
|
|
|
// use mfcc with default configuration...
|
|
MfccOptions op;
|
|
op.frame_opts.dither = 0.0;
|
|
op.frame_opts.preemph_coeff = 0.0;
|
|
op.frame_opts.window_type = "hamming";
|
|
op.frame_opts.remove_dc_offset = false;
|
|
op.frame_opts.round_to_power_of_two = true;
|
|
op.mel_opts.low_freq = 0.0;
|
|
op.mel_opts.htk_mode = true;
|
|
op.htk_compat = true;
|
|
op.use_energy = false; // C0 not energy.
|
|
|
|
Mfcc mfcc(op);
|
|
|
|
// calculate kaldi features
|
|
Matrix<BaseFloat> kaldi_raw_features;
|
|
mfcc.Compute(waveform, 1.0, &kaldi_raw_features);
|
|
|
|
DeltaFeaturesOptions delta_opts;
|
|
Matrix<BaseFloat> kaldi_features;
|
|
ComputeDeltas(delta_opts, kaldi_raw_features, &kaldi_features);
|
|
|
|
// compare the results
|
|
bool passed = true;
|
|
int32 i_old = -1;
|
|
KALDI_ASSERT(kaldi_features.NumRows() == htk_features.NumRows());
|
|
KALDI_ASSERT(kaldi_features.NumCols() == htk_features.NumCols());
|
|
// Ignore ends-- we make slightly different choices than
|
|
// HTK about how to treat the deltas at the ends.
|
|
for (int32 i = 10; i + 10 < kaldi_features.NumRows(); i++) {
|
|
for (int32 j = 0; j < kaldi_features.NumCols(); j++) {
|
|
BaseFloat a = kaldi_features(i, j), b = htk_features(i, j);
|
|
if ((std::abs(b - a)) > 1.0) { //<< TOLERANCE TO DIFFERENCES!!!!!
|
|
// print the non-matching data only once per-line
|
|
if (i_old != i) {
|
|
std::cout << "\n\n\n[HTK-row: " << i << "] "
|
|
<< htk_features.Row(i) << "\n";
|
|
std::cout << "[Kaldi-row: " << i << "] "
|
|
<< kaldi_features.Row(i) << "\n\n\n";
|
|
i_old = i;
|
|
}
|
|
// print indices of non-matching cells
|
|
std::cout << "[" << i << ", " << j << "]";
|
|
passed = false;
|
|
}
|
|
}
|
|
}
|
|
if (!passed) KALDI_ERR << "Test failed";
|
|
|
|
// write the htk features for later inspection
|
|
HtkHeader header = {
|
|
kaldi_features.NumRows(),
|
|
100000, // 10ms
|
|
static_cast<int16>(sizeof(float) * kaldi_features.NumCols()),
|
|
021406 // MFCC_D_A_0
|
|
};
|
|
{
|
|
std::ofstream os("tmp.test.wav.fea_kaldi.1",
|
|
std::ios::out | std::ios::binary);
|
|
WriteHtk(os, kaldi_features, header);
|
|
}
|
|
|
|
std::cout << "Test passed :)\n\n";
|
|
|
|
unlink("tmp.test.wav.fea_kaldi.1");
|
|
}
|
|
|
|
|
|
static void UnitTestHTKCompare2() {
|
|
std::cout << "=== UnitTestHTKCompare2() ===\n";
|
|
|
|
std::ifstream is("test_data/test.wav", std::ios_base::binary);
|
|
WaveData wave;
|
|
wave.Read(is);
|
|
KALDI_ASSERT(wave.Data().NumRows() == 1);
|
|
SubVector<BaseFloat> waveform(wave.Data(), 0);
|
|
|
|
// read the HTK features
|
|
Matrix<BaseFloat> htk_features;
|
|
{
|
|
std::ifstream is("test_data/test.wav.fea_htk.2",
|
|
std::ios::in | std::ios_base::binary);
|
|
bool ans = ReadHtk(is, &htk_features, 0);
|
|
KALDI_ASSERT(ans);
|
|
}
|
|
|
|
// use mfcc with default configuration...
|
|
MfccOptions op;
|
|
op.frame_opts.dither = 0.0;
|
|
op.frame_opts.preemph_coeff = 0.0;
|
|
op.frame_opts.window_type = "hamming";
|
|
op.frame_opts.remove_dc_offset = false;
|
|
op.frame_opts.round_to_power_of_two = true;
|
|
op.mel_opts.low_freq = 0.0;
|
|
op.mel_opts.htk_mode = true;
|
|
op.htk_compat = true;
|
|
op.use_energy = true; // Use energy.
|
|
|
|
Mfcc mfcc(op);
|
|
|
|
// calculate kaldi features
|
|
Matrix<BaseFloat> kaldi_raw_features;
|
|
mfcc.Compute(waveform, 1.0, &kaldi_raw_features);
|
|
|
|
DeltaFeaturesOptions delta_opts;
|
|
Matrix<BaseFloat> kaldi_features;
|
|
ComputeDeltas(delta_opts, kaldi_raw_features, &kaldi_features);
|
|
|
|
// compare the results
|
|
bool passed = true;
|
|
int32 i_old = -1;
|
|
KALDI_ASSERT(kaldi_features.NumRows() == htk_features.NumRows());
|
|
KALDI_ASSERT(kaldi_features.NumCols() == htk_features.NumCols());
|
|
// Ignore ends-- we make slightly different choices than
|
|
// HTK about how to treat the deltas at the ends.
|
|
for (int32 i = 10; i + 10 < kaldi_features.NumRows(); i++) {
|
|
for (int32 j = 0; j < kaldi_features.NumCols(); j++) {
|
|
BaseFloat a = kaldi_features(i, j), b = htk_features(i, j);
|
|
if ((std::abs(b - a)) > 1.0) { //<< TOLERANCE TO DIFFERENCES!!!!!
|
|
// print the non-matching data only once per-line
|
|
if (i_old != i) {
|
|
std::cout << "\n\n\n[HTK-row: " << i << "] "
|
|
<< htk_features.Row(i) << "\n";
|
|
std::cout << "[Kaldi-row: " << i << "] "
|
|
<< kaldi_features.Row(i) << "\n\n\n";
|
|
i_old = i;
|
|
}
|
|
// print indices of non-matching cells
|
|
std::cout << "[" << i << ", " << j << "]";
|
|
passed = false;
|
|
}
|
|
}
|
|
}
|
|
if (!passed) KALDI_ERR << "Test failed";
|
|
|
|
// write the htk features for later inspection
|
|
HtkHeader header = {
|
|
kaldi_features.NumRows(),
|
|
100000, // 10ms
|
|
static_cast<int16>(sizeof(float) * kaldi_features.NumCols()),
|
|
021406 // MFCC_D_A_0
|
|
};
|
|
{
|
|
std::ofstream os("tmp.test.wav.fea_kaldi.2",
|
|
std::ios::out | std::ios::binary);
|
|
WriteHtk(os, kaldi_features, header);
|
|
}
|
|
|
|
std::cout << "Test passed :)\n\n";
|
|
|
|
unlink("tmp.test.wav.fea_kaldi.2");
|
|
}
|
|
|
|
|
|
static void UnitTestHTKCompare3() {
|
|
std::cout << "=== UnitTestHTKCompare3() ===\n";
|
|
|
|
std::ifstream is("test_data/test.wav", std::ios_base::binary);
|
|
WaveData wave;
|
|
wave.Read(is);
|
|
KALDI_ASSERT(wave.Data().NumRows() == 1);
|
|
SubVector<BaseFloat> waveform(wave.Data(), 0);
|
|
|
|
// read the HTK features
|
|
Matrix<BaseFloat> htk_features;
|
|
{
|
|
std::ifstream is("test_data/test.wav.fea_htk.3",
|
|
std::ios::in | std::ios_base::binary);
|
|
bool ans = ReadHtk(is, &htk_features, 0);
|
|
KALDI_ASSERT(ans);
|
|
}
|
|
|
|
// use mfcc with default configuration...
|
|
MfccOptions op;
|
|
op.frame_opts.dither = 0.0;
|
|
op.frame_opts.preemph_coeff = 0.0;
|
|
op.frame_opts.window_type = "hamming";
|
|
op.frame_opts.remove_dc_offset = false;
|
|
op.frame_opts.round_to_power_of_two = true;
|
|
op.htk_compat = true;
|
|
op.use_energy = true; // Use energy.
|
|
op.mel_opts.low_freq = 20.0;
|
|
// op.mel_opts.debug_mel = true;
|
|
op.mel_opts.htk_mode = true;
|
|
|
|
Mfcc mfcc(op);
|
|
|
|
// calculate kaldi features
|
|
Matrix<BaseFloat> kaldi_raw_features;
|
|
mfcc.Compute(waveform, 1.0, &kaldi_raw_features);
|
|
|
|
DeltaFeaturesOptions delta_opts;
|
|
Matrix<BaseFloat> kaldi_features;
|
|
ComputeDeltas(delta_opts, kaldi_raw_features, &kaldi_features);
|
|
|
|
// compare the results
|
|
bool passed = true;
|
|
int32 i_old = -1;
|
|
KALDI_ASSERT(kaldi_features.NumRows() == htk_features.NumRows());
|
|
KALDI_ASSERT(kaldi_features.NumCols() == htk_features.NumCols());
|
|
// Ignore ends-- we make slightly different choices than
|
|
// HTK about how to treat the deltas at the ends.
|
|
for (int32 i = 10; i + 10 < kaldi_features.NumRows(); i++) {
|
|
for (int32 j = 0; j < kaldi_features.NumCols(); j++) {
|
|
BaseFloat a = kaldi_features(i, j), b = htk_features(i, j);
|
|
if ((std::abs(b - a)) > 1.0) { //<< TOLERANCE TO DIFFERENCES!!!!!
|
|
// print the non-matching data only once per-line
|
|
if (static_cast<int32>(i_old) != i) {
|
|
std::cout << "\n\n\n[HTK-row: " << i << "] "
|
|
<< htk_features.Row(i) << "\n";
|
|
std::cout << "[Kaldi-row: " << i << "] "
|
|
<< kaldi_features.Row(i) << "\n\n\n";
|
|
i_old = i;
|
|
}
|
|
// print indices of non-matching cells
|
|
std::cout << "[" << i << ", " << j << "]";
|
|
passed = false;
|
|
}
|
|
}
|
|
}
|
|
if (!passed) KALDI_ERR << "Test failed";
|
|
|
|
// write the htk features for later inspection
|
|
HtkHeader header = {
|
|
kaldi_features.NumRows(),
|
|
100000, // 10ms
|
|
static_cast<int16>(sizeof(float) * kaldi_features.NumCols()),
|
|
021406 // MFCC_D_A_0
|
|
};
|
|
{
|
|
std::ofstream os("tmp.test.wav.fea_kaldi.3",
|
|
std::ios::out | std::ios::binary);
|
|
WriteHtk(os, kaldi_features, header);
|
|
}
|
|
|
|
std::cout << "Test passed :)\n\n";
|
|
|
|
unlink("tmp.test.wav.fea_kaldi.3");
|
|
}
|
|
|
|
|
|
static void UnitTestHTKCompare4() {
|
|
std::cout << "=== UnitTestHTKCompare4() ===\n";
|
|
|
|
std::ifstream is("test_data/test.wav", std::ios_base::binary);
|
|
WaveData wave;
|
|
wave.Read(is);
|
|
KALDI_ASSERT(wave.Data().NumRows() == 1);
|
|
SubVector<BaseFloat> waveform(wave.Data(), 0);
|
|
|
|
// read the HTK features
|
|
Matrix<BaseFloat> htk_features;
|
|
{
|
|
std::ifstream is("test_data/test.wav.fea_htk.4",
|
|
std::ios::in | std::ios_base::binary);
|
|
bool ans = ReadHtk(is, &htk_features, 0);
|
|
KALDI_ASSERT(ans);
|
|
}
|
|
|
|
// use mfcc with default configuration...
|
|
MfccOptions op;
|
|
op.frame_opts.dither = 0.0;
|
|
op.frame_opts.window_type = "hamming";
|
|
op.frame_opts.remove_dc_offset = false;
|
|
op.frame_opts.round_to_power_of_two = true;
|
|
op.mel_opts.low_freq = 0.0;
|
|
op.htk_compat = true;
|
|
op.use_energy = true; // Use energy.
|
|
op.mel_opts.htk_mode = true;
|
|
|
|
Mfcc mfcc(op);
|
|
|
|
// calculate kaldi features
|
|
Matrix<BaseFloat> kaldi_raw_features;
|
|
mfcc.Compute(waveform, 1.0, &kaldi_raw_features);
|
|
|
|
DeltaFeaturesOptions delta_opts;
|
|
Matrix<BaseFloat> kaldi_features;
|
|
ComputeDeltas(delta_opts, kaldi_raw_features, &kaldi_features);
|
|
|
|
// compare the results
|
|
bool passed = true;
|
|
int32 i_old = -1;
|
|
KALDI_ASSERT(kaldi_features.NumRows() == htk_features.NumRows());
|
|
KALDI_ASSERT(kaldi_features.NumCols() == htk_features.NumCols());
|
|
// Ignore ends-- we make slightly different choices than
|
|
// HTK about how to treat the deltas at the ends.
|
|
for (int32 i = 10; i + 10 < kaldi_features.NumRows(); i++) {
|
|
for (int32 j = 0; j < kaldi_features.NumCols(); j++) {
|
|
BaseFloat a = kaldi_features(i, j), b = htk_features(i, j);
|
|
if ((std::abs(b - a)) > 1.0) { //<< TOLERANCE TO DIFFERENCES!!!!!
|
|
// print the non-matching data only once per-line
|
|
if (static_cast<int32>(i_old) != i) {
|
|
std::cout << "\n\n\n[HTK-row: " << i << "] "
|
|
<< htk_features.Row(i) << "\n";
|
|
std::cout << "[Kaldi-row: " << i << "] "
|
|
<< kaldi_features.Row(i) << "\n\n\n";
|
|
i_old = i;
|
|
}
|
|
// print indices of non-matching cells
|
|
std::cout << "[" << i << ", " << j << "]";
|
|
passed = false;
|
|
}
|
|
}
|
|
}
|
|
if (!passed) KALDI_ERR << "Test failed";
|
|
|
|
// write the htk features for later inspection
|
|
HtkHeader header = {
|
|
kaldi_features.NumRows(),
|
|
100000, // 10ms
|
|
static_cast<int16>(sizeof(float) * kaldi_features.NumCols()),
|
|
021406 // MFCC_D_A_0
|
|
};
|
|
{
|
|
std::ofstream os("tmp.test.wav.fea_kaldi.4",
|
|
std::ios::out | std::ios::binary);
|
|
WriteHtk(os, kaldi_features, header);
|
|
}
|
|
|
|
std::cout << "Test passed :)\n\n";
|
|
|
|
unlink("tmp.test.wav.fea_kaldi.4");
|
|
}
|
|
|
|
|
|
static void UnitTestHTKCompare5() {
|
|
std::cout << "=== UnitTestHTKCompare5() ===\n";
|
|
|
|
std::ifstream is("test_data/test.wav", std::ios_base::binary);
|
|
WaveData wave;
|
|
wave.Read(is);
|
|
KALDI_ASSERT(wave.Data().NumRows() == 1);
|
|
SubVector<BaseFloat> waveform(wave.Data(), 0);
|
|
|
|
// read the HTK features
|
|
Matrix<BaseFloat> htk_features;
|
|
{
|
|
std::ifstream is("test_data/test.wav.fea_htk.5",
|
|
std::ios::in | std::ios_base::binary);
|
|
bool ans = ReadHtk(is, &htk_features, 0);
|
|
KALDI_ASSERT(ans);
|
|
}
|
|
|
|
// use mfcc with default configuration...
|
|
MfccOptions op;
|
|
op.frame_opts.dither = 0.0;
|
|
op.frame_opts.window_type = "hamming";
|
|
op.frame_opts.remove_dc_offset = false;
|
|
op.frame_opts.round_to_power_of_two = true;
|
|
op.htk_compat = true;
|
|
op.use_energy = true; // Use energy.
|
|
op.mel_opts.low_freq = 0.0;
|
|
op.mel_opts.vtln_low = 100.0;
|
|
op.mel_opts.vtln_high = 7500.0;
|
|
op.mel_opts.htk_mode = true;
|
|
|
|
BaseFloat vtln_warp =
|
|
1.1; // our approach identical to htk for warp factor >1,
|
|
// differs slightly for higher mel bins if warp_factor <0.9
|
|
|
|
Mfcc mfcc(op);
|
|
|
|
// calculate kaldi features
|
|
Matrix<BaseFloat> kaldi_raw_features;
|
|
mfcc.Compute(waveform, vtln_warp, &kaldi_raw_features);
|
|
|
|
DeltaFeaturesOptions delta_opts;
|
|
Matrix<BaseFloat> kaldi_features;
|
|
ComputeDeltas(delta_opts, kaldi_raw_features, &kaldi_features);
|
|
|
|
// compare the results
|
|
bool passed = true;
|
|
int32 i_old = -1;
|
|
KALDI_ASSERT(kaldi_features.NumRows() == htk_features.NumRows());
|
|
KALDI_ASSERT(kaldi_features.NumCols() == htk_features.NumCols());
|
|
// Ignore ends-- we make slightly different choices than
|
|
// HTK about how to treat the deltas at the ends.
|
|
for (int32 i = 10; i + 10 < kaldi_features.NumRows(); i++) {
|
|
for (int32 j = 0; j < kaldi_features.NumCols(); j++) {
|
|
BaseFloat a = kaldi_features(i, j), b = htk_features(i, j);
|
|
if ((std::abs(b - a)) > 1.0) { //<< TOLERANCE TO DIFFERENCES!!!!!
|
|
// print the non-matching data only once per-line
|
|
if (static_cast<int32>(i_old) != i) {
|
|
std::cout << "\n\n\n[HTK-row: " << i << "] "
|
|
<< htk_features.Row(i) << "\n";
|
|
std::cout << "[Kaldi-row: " << i << "] "
|
|
<< kaldi_features.Row(i) << "\n\n\n";
|
|
i_old = i;
|
|
}
|
|
// print indices of non-matching cells
|
|
std::cout << "[" << i << ", " << j << "]";
|
|
passed = false;
|
|
}
|
|
}
|
|
}
|
|
if (!passed) KALDI_ERR << "Test failed";
|
|
|
|
// write the htk features for later inspection
|
|
HtkHeader header = {
|
|
kaldi_features.NumRows(),
|
|
100000, // 10ms
|
|
static_cast<int16>(sizeof(float) * kaldi_features.NumCols()),
|
|
021406 // MFCC_D_A_0
|
|
};
|
|
{
|
|
std::ofstream os("tmp.test.wav.fea_kaldi.5",
|
|
std::ios::out | std::ios::binary);
|
|
WriteHtk(os, kaldi_features, header);
|
|
}
|
|
|
|
std::cout << "Test passed :)\n\n";
|
|
|
|
unlink("tmp.test.wav.fea_kaldi.5");
|
|
}
|
|
|
|
static void UnitTestHTKCompare6() {
|
|
std::cout << "=== UnitTestHTKCompare6() ===\n";
|
|
|
|
|
|
std::ifstream is("test_data/test.wav", std::ios_base::binary);
|
|
WaveData wave;
|
|
wave.Read(is);
|
|
KALDI_ASSERT(wave.Data().NumRows() == 1);
|
|
SubVector<BaseFloat> waveform(wave.Data(), 0);
|
|
|
|
// read the HTK features
|
|
Matrix<BaseFloat> htk_features;
|
|
{
|
|
std::ifstream is("test_data/test.wav.fea_htk.6",
|
|
std::ios::in | std::ios_base::binary);
|
|
bool ans = ReadHtk(is, &htk_features, 0);
|
|
KALDI_ASSERT(ans);
|
|
}
|
|
|
|
// use mfcc with default configuration...
|
|
MfccOptions op;
|
|
op.frame_opts.dither = 0.0;
|
|
op.frame_opts.preemph_coeff = 0.97;
|
|
op.frame_opts.window_type = "hamming";
|
|
op.frame_opts.remove_dc_offset = false;
|
|
op.frame_opts.round_to_power_of_two = true;
|
|
op.mel_opts.num_bins = 24;
|
|
op.mel_opts.low_freq = 125.0;
|
|
op.mel_opts.high_freq = 7800.0;
|
|
op.htk_compat = true;
|
|
op.use_energy = false; // C0 not energy.
|
|
|
|
Mfcc mfcc(op);
|
|
|
|
// calculate kaldi features
|
|
Matrix<BaseFloat> kaldi_raw_features;
|
|
mfcc.Compute(waveform, 1.0, &kaldi_raw_features);
|
|
|
|
DeltaFeaturesOptions delta_opts;
|
|
Matrix<BaseFloat> kaldi_features;
|
|
ComputeDeltas(delta_opts, kaldi_raw_features, &kaldi_features);
|
|
|
|
// compare the results
|
|
bool passed = true;
|
|
int32 i_old = -1;
|
|
KALDI_ASSERT(kaldi_features.NumRows() == htk_features.NumRows());
|
|
KALDI_ASSERT(kaldi_features.NumCols() == htk_features.NumCols());
|
|
// Ignore ends-- we make slightly different choices than
|
|
// HTK about how to treat the deltas at the ends.
|
|
for (int32 i = 10; i + 10 < kaldi_features.NumRows(); i++) {
|
|
for (int32 j = 0; j < kaldi_features.NumCols(); j++) {
|
|
BaseFloat a = kaldi_features(i, j), b = htk_features(i, j);
|
|
if ((std::abs(b - a)) > 1.0) { //<< TOLERANCE TO DIFFERENCES!!!!!
|
|
// print the non-matching data only once per-line
|
|
if (static_cast<int32>(i_old) != i) {
|
|
std::cout << "\n\n\n[HTK-row: " << i << "] "
|
|
<< htk_features.Row(i) << "\n";
|
|
std::cout << "[Kaldi-row: " << i << "] "
|
|
<< kaldi_features.Row(i) << "\n\n\n";
|
|
i_old = i;
|
|
}
|
|
// print indices of non-matching cells
|
|
std::cout << "[" << i << ", " << j << "]";
|
|
passed = false;
|
|
}
|
|
}
|
|
}
|
|
if (!passed) KALDI_ERR << "Test failed";
|
|
|
|
// write the htk features for later inspection
|
|
HtkHeader header = {
|
|
kaldi_features.NumRows(),
|
|
100000, // 10ms
|
|
static_cast<int16>(sizeof(float) * kaldi_features.NumCols()),
|
|
021406 // MFCC_D_A_0
|
|
};
|
|
{
|
|
std::ofstream os("tmp.test.wav.fea_kaldi.6",
|
|
std::ios::out | std::ios::binary);
|
|
WriteHtk(os, kaldi_features, header);
|
|
}
|
|
|
|
std::cout << "Test passed :)\n\n";
|
|
|
|
unlink("tmp.test.wav.fea_kaldi.6");
|
|
}
|
|
|
|
void UnitTestVtln() {
|
|
// Test the function VtlnWarpFreq.
|
|
BaseFloat low_freq = 10, high_freq = 7800, vtln_low_cutoff = 20,
|
|
vtln_high_cutoff = 7400;
|
|
|
|
for (size_t i = 0; i < 100; i++) {
|
|
BaseFloat freq = 5000, warp_factor = 0.9 + RandUniform() * 0.2;
|
|
AssertEqual(MelBanks::VtlnWarpFreq(vtln_low_cutoff,
|
|
vtln_high_cutoff,
|
|
low_freq,
|
|
high_freq,
|
|
warp_factor,
|
|
freq),
|
|
freq / warp_factor);
|
|
|
|
AssertEqual(MelBanks::VtlnWarpFreq(vtln_low_cutoff,
|
|
vtln_high_cutoff,
|
|
low_freq,
|
|
high_freq,
|
|
warp_factor,
|
|
low_freq),
|
|
low_freq);
|
|
AssertEqual(MelBanks::VtlnWarpFreq(vtln_low_cutoff,
|
|
vtln_high_cutoff,
|
|
low_freq,
|
|
high_freq,
|
|
warp_factor,
|
|
high_freq),
|
|
high_freq);
|
|
BaseFloat freq2 = low_freq + (high_freq - low_freq) * RandUniform(),
|
|
freq3 = freq2 +
|
|
(high_freq - freq2) * RandUniform(); // freq3>=freq2
|
|
BaseFloat w2 = MelBanks::VtlnWarpFreq(vtln_low_cutoff,
|
|
vtln_high_cutoff,
|
|
low_freq,
|
|
high_freq,
|
|
warp_factor,
|
|
freq2);
|
|
BaseFloat w3 = MelBanks::VtlnWarpFreq(vtln_low_cutoff,
|
|
vtln_high_cutoff,
|
|
low_freq,
|
|
high_freq,
|
|
warp_factor,
|
|
freq3);
|
|
KALDI_ASSERT(w3 >= w2); // increasing function.
|
|
BaseFloat w3dash = MelBanks::VtlnWarpFreq(
|
|
vtln_low_cutoff, vtln_high_cutoff, low_freq, high_freq, 1.0, freq3);
|
|
AssertEqual(w3dash, freq3);
|
|
}
|
|
}
|
|
|
|
static void UnitTestFeat() {
|
|
UnitTestVtln();
|
|
UnitTestReadWave();
|
|
UnitTestSimple();
|
|
UnitTestHTKCompare1();
|
|
UnitTestHTKCompare2();
|
|
// commenting out this one as it doesn't compare right now I normalized
|
|
// the way the FFT bins are treated (removed offset of 0.5)... this seems
|
|
// to relate to the way frequency zero behaves.
|
|
UnitTestHTKCompare3();
|
|
UnitTestHTKCompare4();
|
|
UnitTestHTKCompare5();
|
|
UnitTestHTKCompare6();
|
|
std::cout << "Tests succeeded.\n";
|
|
}
|
|
|
|
|
|
int main() {
|
|
try {
|
|
for (int i = 0; i < 5; i++) UnitTestFeat();
|
|
std::cout << "Tests succeeded.\n";
|
|
return 0;
|
|
} catch (const std::exception &e) {
|
|
std::cerr << e.what();
|
|
return 1;
|
|
}
|
|
}
|