|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIBROSA_H_ |
|
|
#define LIBROSA_H_ |
|
|
|
|
|
#include "eigen3/Eigen/Core" |
|
|
#include "eigen3/unsupported/Eigen/FFT" |
|
|
|
|
|
#include <vector> |
|
|
#include <complex> |
|
|
#include <iostream> |
|
|
#include <cmath> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace librosa{ |
|
|
|
|
|
#ifndef M_PI |
|
|
#define M_PI 3.14159265358979323846 |
|
|
#endif |
|
|
|
|
|
typedef Eigen::Matrix<float, 1, Eigen::Dynamic, Eigen::RowMajor> Vectorf; |
|
|
typedef Eigen::Matrix<std::complex<float>, 1, Eigen::Dynamic, Eigen::RowMajor> Vectorcf; |
|
|
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Matrixf; |
|
|
typedef Eigen::Matrix<std::complex<float>, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Matrixcf; |
|
|
|
|
|
namespace internal{ |
|
|
|
|
|
static Vectorf pad(Vectorf &x, int left, int right, const std::string &mode, float value){ |
|
|
Vectorf x_paded = Vectorf::Constant(left+x.size()+right, value); |
|
|
x_paded.segment(left, x.size()) = x; |
|
|
|
|
|
if (mode.compare("reflect") == 0){ |
|
|
for (int i = 0; i < left; ++i){ |
|
|
x_paded[i] = x[left-i]; |
|
|
} |
|
|
for (int i = left; i < left+right; ++i){ |
|
|
x_paded[i+x.size()] = x[x.size()-2-i+left]; |
|
|
} |
|
|
} |
|
|
|
|
|
if (mode.compare("symmetric") == 0){ |
|
|
for (int i = 0; i < left; ++i){ |
|
|
x_paded[i] = x[left-i-1]; |
|
|
} |
|
|
for (int i = left; i < left+right; ++i){ |
|
|
x_paded[i+x.size()] = x[x.size()-1-i+left]; |
|
|
} |
|
|
} |
|
|
|
|
|
if (mode.compare("edge") == 0){ |
|
|
for (int i = 0; i < left; ++i){ |
|
|
x_paded[i] = x[0]; |
|
|
} |
|
|
for (int i = left; i < left+right; ++i){ |
|
|
x_paded[i+x.size()] = x[x.size()-1]; |
|
|
} |
|
|
} |
|
|
return x_paded; |
|
|
} |
|
|
|
|
|
static Matrixcf stft(Vectorf &x, int n_fft, int n_hop, const std::string &win, bool center, const std::string &mode){ |
|
|
|
|
|
Vectorf window = 0.5*(1.f-(Vectorf::LinSpaced(n_fft, 0.f, static_cast<float>(n_fft-1))*2.f*M_PI/n_fft).array().cos()); |
|
|
|
|
|
int pad_len = center ? n_fft / 2 : 0; |
|
|
Vectorf x_paded = pad(x, pad_len, pad_len, mode, 0.f); |
|
|
|
|
|
int n_f = n_fft/2+1; |
|
|
int n_frames = 1+(x_paded.size()-n_fft) / n_hop; |
|
|
Matrixcf X(n_frames, n_fft); |
|
|
Eigen::FFT<float> fft; |
|
|
|
|
|
for (int i = 0; i < n_frames; ++i){ |
|
|
Vectorf x_frame = window.array()*x_paded.segment(i*n_hop, n_fft).array(); |
|
|
X.row(i) = fft.fwd(x_frame); |
|
|
} |
|
|
return X.leftCols(n_f); |
|
|
} |
|
|
|
|
|
static Matrixf spectrogram(Matrixcf &X, float power = 1.f){ |
|
|
return X.cwiseAbs().array().pow(power); |
|
|
} |
|
|
|
|
|
static Matrixf melfilter(int sr, int n_fft, int n_mels, int fmin, int fmax){ |
|
|
int n_f = n_fft/2+1; |
|
|
Vectorf fft_freqs = (Vectorf::LinSpaced(n_f, 0.f, static_cast<float>(n_f-1))*sr)/n_fft; |
|
|
|
|
|
float f_min = 0.f; |
|
|
float f_sp = 200.f/3.f; |
|
|
float min_log_hz = 1000.f; |
|
|
float min_log_mel = (min_log_hz-f_min)/f_sp; |
|
|
float logstep = logf(6.4f)/27.f; |
|
|
|
|
|
auto hz_to_mel = [=](int hz, bool htk = false) -> float { |
|
|
if (htk){ |
|
|
return 2595.0f*log10f(1.0f+hz/700.0f); |
|
|
} |
|
|
float mel = (hz-f_min)/f_sp; |
|
|
if (hz >= min_log_hz){ |
|
|
mel = min_log_mel+logf(hz/min_log_hz)/logstep; |
|
|
} |
|
|
return mel; |
|
|
}; |
|
|
auto mel_to_hz = [=](Vectorf &mels, bool htk = false) -> Vectorf { |
|
|
if (htk){ |
|
|
return 700.0f*(Vectorf::Constant(n_mels+2, 10.f).array().pow(mels.array()/2595.0f)-1.0f); |
|
|
} |
|
|
return (mels.array()>min_log_mel).select(((mels.array()-min_log_mel)*logstep).exp()*min_log_hz, (mels*f_sp).array()+f_min); |
|
|
}; |
|
|
|
|
|
float min_mel = hz_to_mel(fmin); |
|
|
float max_mel = hz_to_mel(fmax); |
|
|
Vectorf mels = Vectorf::LinSpaced(n_mels+2, min_mel, max_mel); |
|
|
Vectorf mel_f = mel_to_hz(mels); |
|
|
Vectorf fdiff = mel_f.segment(1, mel_f.size() - 1) - mel_f.segment(0, mel_f.size() - 1); |
|
|
Matrixf ramps = mel_f.replicate(n_f, 1).transpose().array() - fft_freqs.replicate(n_mels + 2, 1).array(); |
|
|
|
|
|
Matrixf lower = -ramps.topRows(n_mels).array()/fdiff.segment(0, n_mels).transpose().replicate(1, n_f).array(); |
|
|
Matrixf upper = ramps.bottomRows(n_mels).array()/fdiff.segment(1, n_mels).transpose().replicate(1, n_f).array(); |
|
|
Matrixf weights = (lower.array()<upper.array()).select(lower, upper).cwiseMax(0); |
|
|
|
|
|
auto enorm = (2.0/(mel_f.segment(2, n_mels)-mel_f.segment(0, n_mels)).array()).transpose().replicate(1, n_f); |
|
|
weights = weights.array()*enorm; |
|
|
|
|
|
return weights; |
|
|
} |
|
|
|
|
|
static Matrixf melspectrogram(Vectorf &x, int sr, int n_fft, int n_hop, |
|
|
const std::string &win, bool center, |
|
|
const std::string &mode, float power, |
|
|
int n_mels, int fmin, int fmax){ |
|
|
Matrixcf X = stft(x, n_fft, n_hop, win, center, mode); |
|
|
Matrixf mel_basis = melfilter(sr, n_fft, n_mels, fmin, fmax); |
|
|
Matrixf sp = spectrogram(X, power); |
|
|
Matrixf mel = mel_basis*sp.transpose(); |
|
|
return mel.transpose(); |
|
|
} |
|
|
|
|
|
static Matrixf power2db(Matrixf& x) { |
|
|
auto log_sp = 10.0f*x.array().max(1e-10).log10(); |
|
|
return log_sp.cwiseMax(log_sp.maxCoeff() - 80.0f); |
|
|
} |
|
|
|
|
|
static Matrixf dct(Matrixf& x, bool norm, int type) { |
|
|
int N = x.cols(); |
|
|
Matrixf xi = Matrixf::Zero(N, N); |
|
|
xi.rowwise() += Vectorf::LinSpaced(N, 0.f, static_cast<float>(N-1)); |
|
|
|
|
|
Matrixf coeff = 2*(M_PI*xi.transpose().array()/N*(xi.array()+0.5)).cos(); |
|
|
Matrixf dct = x*coeff.transpose(); |
|
|
|
|
|
if (norm) { |
|
|
Vectorf ortho = Vectorf::Constant(N, sqrtf(0.5f/N)); |
|
|
ortho[0] = sqrtf(0.25f/N); |
|
|
dct = dct*ortho.asDiagonal(); |
|
|
} |
|
|
return dct; |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
class Feature |
|
|
{ |
|
|
public: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static std::vector<std::vector<std::complex<float>>> stft(std::vector<float> &x, |
|
|
int n_fft, int n_hop, |
|
|
const std::string &win, bool center, |
|
|
const std::string &mode){ |
|
|
Vectorf map_x = Eigen::Map<Vectorf>(x.data(), x.size()); |
|
|
Matrixcf X = internal::stft(map_x, n_fft, n_hop, win, center, mode); |
|
|
std::vector<std::vector<std::complex<float>>> X_vector(X.rows(), std::vector<std::complex<float>>(X.cols(), 0)); |
|
|
for (int i = 0; i < X.rows(); ++i){ |
|
|
auto &row = X_vector[i]; |
|
|
Eigen::Map<Vectorcf>(row.data(), row.size()) = X.row(i); |
|
|
} |
|
|
return X_vector; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static std::vector<std::vector<float>> melspectrogram(std::vector<float> &x, int sr, |
|
|
int n_fft, int n_hop, const std::string &win, bool center, const std::string &mode, |
|
|
float power, int n_mels, int fmin, int fmax){ |
|
|
Vectorf map_x = Eigen::Map<Vectorf>(x.data(), x.size()); |
|
|
Matrixf mel = internal::melspectrogram(map_x, sr, n_fft, n_hop, win, center, mode, power, n_mels, fmin, fmax).transpose(); |
|
|
std::vector<std::vector<float>> mel_vector(mel.rows(), std::vector<float>(mel.cols(), 0.f)); |
|
|
for (int i = 0; i < mel.rows(); ++i){ |
|
|
auto &row = mel_vector[i]; |
|
|
Eigen::Map<Vectorf>(row.data(), row.size()) = mel.row(i); |
|
|
} |
|
|
return mel_vector; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static std::vector<std::vector<float>> mfcc(std::vector<float> &x, int sr, |
|
|
int n_fft, int n_hop, const std::string &win, bool center, const std::string &mode, |
|
|
float power, int n_mels, int fmin, int fmax, |
|
|
int n_mfcc, bool norm, int type) { |
|
|
Vectorf map_x = Eigen::Map<Vectorf>(x.data(), x.size()); |
|
|
Matrixf mel = internal::melspectrogram(map_x, sr, n_fft, n_hop, win, center, mode, power, n_mels, fmin, fmax).transpose(); |
|
|
Matrixf mel_db = internal::power2db(mel); |
|
|
Matrixf dct = internal::dct(mel_db, norm, type).leftCols(n_mfcc); |
|
|
std::vector<std::vector<float>> mfcc_vector(dct.rows(), std::vector<float>(dct.cols(), 0.f)); |
|
|
for (int i = 0; i < dct.rows(); ++i) { |
|
|
auto &row = mfcc_vector[i]; |
|
|
Eigen::Map<Vectorf>(row.data(), row.size()) = dct.row(i); |
|
|
} |
|
|
return mfcc_vector; |
|
|
} |
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
|