使用C/C++实现Librosa,求STFT、Mel、MFCC!!!!

本文详细介绍了如何使用C++实现基于VS2017的Librosa库,以处理wav音频文件,实现短时傅里叶变换(STFT)、梅尔滤波器(Mel)和梅尔频率倒谱系数(MFCC)计算。通过自定义头文件和源文件,实现了与Python中Librosa类似的功能,包括音频的预处理、STFT、梅尔谱和MFCC的计算,并提供了性能测试的时间消耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创作来源:

在语音识别的任务中,我们通常使用Python求语音信号的频谱特征,有时需要在其它平台使用该特征,网络上现有的使用C、C++求MFCC特征的案例很少,本文使用通俗易操作的代码基于VS2017实现使用C、C++求wav的频谱特征。只需要修改窗口函数大小,采样频率等相应的参数即可......

头文件1:librosa.h

#ifndef LIBROSA_H_
#define LIBROSA_H_

#include "eigen3/Eigen/Core"
#include "eigen3/unsupported/Eigen/FFT"

#include <vector>
#include <complex>
#include <iostream>

///
/// \brief c++ implemention of librosa
///
namespace librosa {

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif // !M_PI

    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) {
            // hanning
            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);

GFCC(gammatone frequency cepstral coefficients)特征是一种音频特征提取方法,是对音频信号进行频谱处理后进行MFCCMel频率倒谱系数)的改进。提取GFCC特征的代码可分为以下几个步骤: 1. 预处理:读取音频信号,进行预处理操作,例如归一化、去除静音段等。 2. 帧化:将预处理后的音频信号分成帧,每帧通常选取20-40毫秒的时间长度,帧与帧之间有一定的重叠。 3. 加窗:对每一帧的音频信号应用窗函数(如汉明窗),以减少由帧分割引起的频谱泄漏。 4. 快速傅里叶变换(FFT):对窗口化的音频信号进行FFT变换,将信号转换为频域表示。 5. 滤波器组设计:设计一组正则的、非线性的Gammatone滤波器,从低频到高频对频谱进行滤波。 6. 取对数能量:对每个滤波器的输出进行幅度平方运算,得到能量谱。 7. 进行倒谱变换:对能量谱应用离散余弦变换(DCT),得到GFCC系数。 8. 取GFCC特征:从DCT得到的系数中,选择一部分作为最终的GFCC特征向量。 上述步骤的具体代码实现可以使用各种音频处理库,如LibrosaPython)、Kaldi(C++)等,也可以根据上述步骤自行编写代码实现。以Python为例,使用Librosa库的代码如下: ```python import librosa # 1. 预处理 audio, sr = librosa.load('audio.wav') # 2. 帧化 frames = librosa.util.frame(audio, frame_length=2048, hop_length=512) # 3. 加窗 frames = frames * librosa.filters.get_window('hann', 2048) # 4. FFT变换 spectrogram = np.abs(librosa.stft(frames, n_fft=2048, hop_length=512)) # 5. 滤波器组设计 gammatone_filters = librosa.filters.gammatone(sr, n_fft=2048) # 6. 取对数能量 energy = np.square(np.dot(gammatone_filters, spectrogram)) # 7. 倒谱变换 gfcc = librosa.feature.mfcc(S=librosa.amplitude_to_db(energy), n_mfcc=15) # 8. 取GFCC特征 gfcc_features = gfcc[1:8] # 选择第2到第8个倒谱系数作为GFCC特征 print(gfcc_features) ``` 上述代码示例中,通过Librosa实现了从音频文件中提取GFCC特征的过程。可以根据具体需进行参数调整和代码优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值